Android:蓝牙低功耗扫描程序接收空数据

时间:2016-07-07 09:16:57

标签: java android bluetooth bluetooth-lowenergy btle

这是广告客户(通知data传递为AdvertiseData类型)

  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);
    }
};

在回调onScarnResult中,我记录产生此输出的扫描记录toString()

 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) 

null。 我注意到toString输出具有广告数据字符的ASCII代码&#34; 123456&#34;,即49,50,51,52,53,54

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

我想收到正确的广告数据,我做错了吗?

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

编辑:通过打印整个scanRecord,您将获得此输出

  

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,该广告客户位于mServiceUuids数组中,因为与mServiceData关联的密钥是另一个。所以我以这种方式更改了代码,导航数据映射并获取值(请参阅两个if-blocks)

   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。

我必须浏览地图才能找到价值(第二个if-block),但我没有办法知道这是否是我感兴趣的价值。无论哪种方式如果系统在接收器应用程序上运行扫描程序代码时放置了另一个我无法知道的密钥,那么我无法获得价值。

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

4 个答案:

答案 0 :(得分:0)

我发现了错误。改变这一行:

.addServiceData(pUuid, "123456".getBytes(Charset.forName("UTF-8")))

为:

.addServiceData(pUuid, "123456".getBytes()

就是这样。我使用了与您相同的示例代码。

在这里找到一个更好的例子:
https://github.com/googlesamples/android-BluetoothAdvertisements

答案 1 :(得分:0)

尝试添加ScanFilter并检索结果

 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

答案 2 :(得分:0)

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

广告客户中与.addServiceUuid().addServiceData()一起使用的

UUID是不同的对象。 第一个标识服务,是一个128位的UUID。第二个标识该服务中的serviceData,并且应为16位UUID。

这就是扫描仪收到的原因

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

请注意,传递给.addServiceData的UUID共有16位0x82b6

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

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

解决方案是仅使用这种格式的[0000xxxx-0000-1000-8000-00805f9b34fb的UUID ]来识别广告客户和扫描仪中的serviceData。您可以保留原始的128位UUID来标识服务。

答案 3 :(得分:0)

@guiv在这里https://stackoverflow.com/a/54726135/1869562给出了正确答案

让我以更简洁的方式添加。

addServiceData需要16位UUIDS,而addServiceUUIDs可以接受16位或128。 addServiceData不会引发异常,而是自动将128位UUID调整为16位,因此addServiceData的输入参数(在Advertiser中)和getServiceData()的返回值键(在Scanner中)之间是不同的。

解决方案-确保您在广告客户中使用16位UUID