安卓:蓝牙低功耗扫描仪接收空数据

这是广告客户(通知作为类型传递)dataAdvertiseData

  private void advertise() {
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    BluetoothLeAdvertiser advertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
    AdvertiseSettings settings = new AdvertiseSettings.Builder()
            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
            .setConnectable(false)
            .build();
    ParcelUuid pUuid = new ParcelUuid(UUID.fromString("cf2c82b6-6a06-403d-b7e6-13934e602664"));
    AdvertiseData data = new AdvertiseData.Builder()
            //.setIncludeDeviceName(true)
            .addServiceUuid(pUuid)
            .addServiceData(pUuid, "123456".getBytes(Charset.forName("UTF-8")))
            .build();
    AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            Log.i(tag, "Advertising onStartSuccess");
            super.onStartSuccess(settingsInEffect);
        }

        @Override
        public void onStartFailure(int errorCode) {
            Log.e(tag, "Advertising onStartFailure: " + errorCode);
            super.onStartFailure(errorCode);
        }
    };
    advertiser.startAdvertising(settings, data, advertiseCallback);
}

它成功开始了。

这是扫描仪

 private void discover() {
    ScanSettings settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
            .build();
    mBluetoothLeScanner.startScan(null, settings, mScanCallback);
}

private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        Log.i(tag, "Discovery onScanResult");
        if (result == null) {
            Log.i(tag, "no result");
            return;
        }
        ScanRecord scanRecord = result.getScanRecord();
        List<ParcelUuid> list = scanRecord != null ? scanRecord.getServiceUuids() : null;
        if (list != null) {
            Log.i(tag, scanRecord.toString());
            for (ParcelUuid uuid : list) {
                byte[] data = scanRecord.getServiceData(uuid);
            }
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
        Log.i(tag, "Discovery onBatchScanResults");
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.e(tag, "Discovery onScanFailed: " + errorCode);
    }
};

在回调中,我记录生成此输出的扫描记录onScarnResulttoString()

 ScanRecord [mAdvertiseFlags=2, 
         mServiceUuids=[cf2c82b6-6a06-403d-b7e6-13934e602664],
         mManufacturerSpecificData={}, 
         mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}, 
         mTxPowerLevel=-2147483648, mDeviceName=null]

uuid匹配,不幸的是

  byte[] data = scanRecord.getServiceData(uuid) 

是。我注意到输出具有广告数据字符“123456”的ASCII代码,即49,50,51,52,53,54nulltoString

 mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}

我想收到正确的广告数据,我是否做错了什么?

编辑:清单具有蓝牙,bt管理员和位置的权限。第三个在Android 6的运行时启动请求

编辑:通过打印整个扫描记录,你得到这个输出

ScanRecord [mAdvertiseFlags=-1, mServiceUuids=[cf2c82b6-6a06-403d-b7e6-13934e602664], mManufacturerSpecificData={}, mServiceData={000082b6-0000-1000-8000-00805f9b34fb=[49, 50, 51, 52, 53, 54]}, mTxPowerLevel=-2147483648, mDeviceName=null]

基本上,您不能使用由广告商决定的 uuid,它位于 mServiceUuid 数组中,因为与 mServiceData 关联的键是另一个密钥。所以我以这种方式更改了代码,以导航数据映射并获取值(请参阅两个if块)

   public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
        for (ScanResult result : results) {
            ScanRecord scanRecord = result.getScanRecord();
            List<ParcelUuid> uuids = scanRecord.getServiceUuids();
            Map<ParcelUuid, byte[]> map = scanRecord.getServiceData();
            if (uuids != null) {
                for (ParcelUuid uuid : uuids) {
                    byte[] data = scanRecord.getServiceData(uuid);
                    Log.i(tag, uuid + " -> " + data + " contain " + map.containsKey(uuid));
                }
            }

            if (map != null) {
                Set<Map.Entry<ParcelUuid, byte[]>> set = map.entrySet();
                Iterator<Map.Entry<ParcelUuid, byte[]>> iterator = set.iterator();
                while (iterator.hasNext()) {
                    Log.i(tag, new String(iterator.next().getValue()));
                }
            }
        }
    }

事实上,这条线

 map.containsKey(uuid)

返回 false,因为数据映射未使用广告客户的 uuid。

我必须在地图上导航才能找到值(第2个if-block),但我没有任何办法知道这是否是我感兴趣的值。无论哪种方式,如果系统在接收器应用程序上运行扫描仪代码时放置了另一个我不知道的密钥,则我无法获取该值。

如何在接收器上处理此问题?我想使用数据字段,但是获取它们的字符串键不是先验的,而是由系统决定的。


答案 1

我知道这是一个旧线程,但是由于我遇到了同样的问题并找到了解决方案......

与广告客户一起使用和在广告客户中使用的 UUID 是不同的对象。第一个标识服务,是 128 位 UUID。第二个标识该服务中的服务数据,并且应为 16 位 UUID。.addServiceUuid().addServiceData()

这就是扫描仪接收

0000**82b6**-0000-1000-8000-00805f9b34fb

请注意,16 位与传递给 .addServiceData 的 UUID 是通用的:0x82b6

cf2c**82b6**-6a06-403d-b7e6-13934e602664

16 位 UUID 通过左移 96 位并添加蓝牙常量 UUID 转换为 128 位。

解决方案只是使用此表单 [ ] 的 UUID 来标识广告客户和扫描程序中的服务数据。您可以保留原始的 128 位 UUID 来标识服务。0000xxxx-0000-1000-8000-00805f9b34fb


答案 2

尝试添加扫描过滤器并检索结果

 private void discover() 
{
    List<ScanFilter> filters = new ArrayList<ScanFilter>();

    ScanFilter filter = new ScanFilter.Builder()
            .setServiceUuid( new ParcelUuid(UUID.fromString( 
   getString(R.string.ble_uuid ) ) ) )
            .build();
    filters.add( filter );

源代码链接:https://github.com/tutsplus/Android-BluetoohLEAdvertising/blob/master/app/src/main/java/com/tutsplus/bleadvertising/MainActivity.java


推荐