Android: Bluetooth Low Energy сканер получает нулевые данные

Это рекламодатель (уведомление 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 рекламируемых символов данных "123456", то есть 49,50,51,52,53,54

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

Я хотел бы получить правильные рекламируемые данные, не делаю ли я что-то не так?

EDIT: манифест имеет разрешения для bluetooth, администратора и местоположения 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-блока)

   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), но у меня нет никаких средств, чтобы узнать, интересует ли это значение. В любом случае я не могу получить значение if система добавила еще один ключ, который я не могу знать при запуске кода сканера в приложении-получателе.

Как я могу справиться с этой проблемой на ресивере? Я хотел бы использовать поле данных, но строковый ключ для их получения неизвестен априори и определяется системой.

Ответ 1

Я нашел ошибку. Измените эту строку:

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

в

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

Что это. Я использовал тот же пример кода, что и вы.

Найдите лучший пример здесь:
https://github.com/googlesamples/android-BluetoothAdvertisements

Ответ 2

Попробуйте добавить 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