Android: Bluetooth Low Energy スキャナが null データを受信する 質問する

Android: Bluetooth Low Energy スキャナが null データを受信する 質問する

これは広告主です(通知はタイプ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) 

出力には、宣伝されているデータ文字「123456」のASCIIコード、つまり49、50、51、52、53、54が含まれているnullことに気付きました。toString

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

正しい広告データを受け取りたいのですが、何か間違っているのでしょうか?

編集: マニフェストには、Bluetooth、BT管理、位置情報の権限があります。3番目は、Android 6で実行時にリクエストを開始します。

編集: スキャンレコード全体を印刷すると、この出力が得られます

スキャン記録 [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]

基本的に、mServiceUuids 配列にある広告主が決定した uuid は使用できません。mServiceData に関連付けられたキーは別のキーであるためです。そのため、データ マップをナビゲートして値を取得するために、コードを次のように変更しました (2 つの 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)

広告主の UUID がデータ マップで使用されていないため、false を返します。

値 (2 番目の if ブロック) を見つけるためにマップを移動する必要がありましたが、それが興味のある値であるかどうかを知る手段がありません。いずれにしても、システムが、受信側アプリでスキャナーのコードを実行しているときに、私が知ることができない別のキーを配置した場合、値を取得できません。

受信側でこの問題をどのように処理すればよいでしょうか? データ フィールドを使用したいのですが、それを取得するための文字列キーは事前にわかっておらず、システムによって決定されます。

ベストアンサー1

古いスレッドであることは承知していますが、私も同じ問題を抱えていて解決策を見つけたので...

.addServiceUuid()アドバタイザーで使用される UUIDとアドバタイザー内で使用される UUID.addServiceData()は異なるオブジェクトです。最初の UUID はサービスを識別し、128 ビットの UUID です。2 番目の UUID は、そのサービス内の serviceData を識別し、16 ビットの UUID であることが期待されます。

これがスキャナーが受信する理由です

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

16 ビットは0x82b6.addServiceData に渡される UUID と共通であることに注意してください。

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

16 ビットの UUID は、96 ビットを左シフトし、Bluetooth 定数 UUID を追加することで 128 ビットに変換されます。

解決策は、この形式の UUID [ 0000xxxx-0000-1000-8000-00805f9b34fb] を使用して、広告主とスキャナーの両方でサービスデータを識別することです。元の 128 ビット UUID を保持してサービスを識別することができます。

おすすめ記事