Android BluetoothGattServer 挂起 - 客户端无法读/写特性

问题描述

我正在开发一个项目,该项目由在 Android 手机上运行的 BLE GATT 服务器(使用 BluetoothGattServer Java 类)和物联网板上的 BLE 客户端组成。使用手机作为服务器的概念是以某种方式保护物联网板免受攻击客户端。当我的 Android 应用程序想要与外部设备通信时,它会开始宣传一组特殊的数据,如果外部设备识别出广告,它就会连接到 Android BLE GATT 服务器。然后外部设备读取呈现的服务和特性,并注册一些字符的通知。 到目前为止,一切都很好。

此后,外部设备尝试将身份验证数据写入其中一个字符。如果一切都是干净和完美的,那么这个过程就会顺利进行。但是,如果由于某种原因上次连接在某些操作过程中中断并且未正确关闭,则外部设备无法读取/写入该特性。如果我重新启动手机或清除蓝牙缓存(设置 > 应用程序 > 蓝牙 > 清除数据),所有操作都可以正常进行,但我无法强制用户在正常操作中执行此操作,而且我还没有找到如何以及是否可以清除此缓存从应用程序内部以编程方式进行。

  • 我在网上阅读了 GATT 客户端缓存未记录的“刷新”方法,但在 GATT 服务器中没有这样的方法
  • 我阅读并尝试了“服务已更改”特征 (0x2A05),但对我帮助不大。
  • 我曾尝试将另一部手机用作客户端,但我怀疑是哪些设备导致了问题。我在其上运行了“BLE Scanner”应用程序并尝试连接到服务器电话 - 问题仍然存在。我可以连接,所有特征都被发现,但是当我尝试读/写一些字符时,连接在 30 秒超时后停止 - 与原始情况完全相同的行为。结论是我的 Android BluetoothGattServer 有问题

在开发过程中,问题主要发生在连接因通信错误而中断时。在我修复了所有不会发生的错误后,在现实生活中的使用中,但要记住它是无线电连接,它实际上可以被任何东西断开,我将有一个可靠的机制来重新连接。

我用这个代码打开服务器:

    private void startServer() {
        BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();
        mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
        if (mBluetoothLeAdvertiser == null) {
            Log.w("BLE","Failed to create advertiser");
            return;
        }

        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
                .setConnectable(true)
                .setTimeout(0)
                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
                .build();

        byte bData[] = new byte[24];
        bData = ...... // some proprietary advertising data
        AdvertiseData data = new AdvertiseData.Builder()
                .setIncludeDeviceName(false)
                .setIncludeTxPowerLevel(false)
                .addManufacturerData(iManufID,bData)
                .build();

        mBluetoothLeAdvertiser
                .startAdvertising(settings,data,mAdvertiseCallback);
        mBluetoothGattServer = mBluetoothManager.openGattServer(ctxActivity,mGattServerCallback);
        mBluetoothGattServer.clearServices();
        mBluetoothGattServer.addService(BLEProfile.createBLEService()); 
             /* Static method,which builds Service->Chars->Descriptors. 
              I assign the Client Config descriptor (0x2902) to each characteristic. */
    }

为了停止服务器,我使用以下代码

    private void stopServer() {
        if (mBluetoothGattServer == null) return;
        mBluetoothGattServer.clearServices();
        mBluetoothGattServer.close();

        if (mBluetoothLeAdvertiser == null) return;
        mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
    }

每次连接中断时我都会停止并启动服务器。

有人知道我做错了什么吗?

另外值得一提的是,在现实生活中,一个房间里会有很多 IoT 设备,一个房间里可能会有很多电话。一部手机应该能够被它顺序请求的任何物联网设备连接,一个物联网设备应该能够依次连接到多部手机。手机 GATT 服务器的广告数据定义了请求连接哪些 IoT 设备,并且每次手机请求与不同设备的连接时,广告数据都会发生变化。

更新: 这是服务器回调的代码

    private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {

        @Override
        public void onMtuChanged(BluetoothDevice device,int mtu) {
            super.onMtuChanged(device,mtu);
            Log.i("BLE","MTU changed: "+mtu);
        }

        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device,int requestId,BluetoothGattCharacteristic characteristic,boolean preparedWrite,boolean responseNeeded,int offset,byte[] value) {
            if(BLEProfile.XXXXXX.equals(characteristic.getUuid()))
            {
                // ... some data checks ...
                
                mBluetoothGattServer.sendResponse(device,requestId,iValid,bEncrypted);
                characteristic.setValue(bEncrypted);
                mBluetoothGattServer.notifyCharacteristicChanged(device,characteristic,true);
            } else if (...) /* similar operations for all other characteristics */
            {
                ...
            }
            else
            {
                Log.i("BLE","Write not mine characteristic");
                mBluetoothGattServer.sendResponse(device,BluetoothGatt.GATT_FAILURE,null);
            }
        }

        @Override
        public void onConnectionStateChange(BluetoothDevice device,int status,int newState) {

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i("BLE","BluetoothDevice ... CONNECTED: " + device);
                if(device != null)
                {
                    BluetoothGattService mServ = mBluetoothGattServer.getService(BLEProfile.GATT_SERVICE);
                    if(mServ != null)
                    {
                        BluetoothGattCharacteristic mChar = mServ.getCharacteristic(BLEProfile.SERVICE_CHANGED);
                        if(mChar != null)
                            mBluetoothGattServer.notifyCharacteristicChanged(device,mChar,false);
                    }
                }
            } else if (newState == BluetoothProfile.STATE_disCONNECTED) {
                mRegisteredDevices.remove(device);
                stopAdvertising();
                startAdvertising();
            }
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device,BluetoothGattCharacteristic characteristic) {

            /* Not used currently. Just some testing code below. */
            if(BLEProfile.XXXXXX.equals(characteristic.getUuid()))
            {
                mBluetoothGattServer.sendResponse(device,BluetoothGatt.GATT_SUCCESS,new byte[] {0x01,0x02,0x03,0x04,0x05});
            } else if(...)      /* similar operations for all other characteristics */
            {
                ...
            }
            else
            {
                Log.i("BLE","Read not mine characteristic");
                mBluetoothGattServer.sendResponse(device,null);
            }
        }

        @Override
        public void onDescriptorReadRequest(BluetoothDevice device,BluetoothGattDescriptor descriptor) {

            if (BLEProfile.CLIENT_CONfig.equals(descriptor.getUuid())) {
                Log.d("BLE","Config descriptor read");
                byte[] returnValue;
                if (mRegisteredDevices.contains(device)) {
                    returnValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
                } else {
                    returnValue = BluetoothGattDescriptor.disABLE_NOTIFICATION_VALUE;
                }
                /* Not sure why I am responding with GATT_FAILURE here insted of GAT_SUCCESS !?!? May be some copy/paste mistake. */
                mBluetoothGattServer.sendResponse(device,returnValue);
            } else {
                Log.w("BLE","UnkNown descriptor read request");
                mBluetoothGattServer.sendResponse(device,null);
            }
        }

        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device,BluetoothGattDescriptor descriptor,byte[] value) {

            if (BLEProfile.CLIENT_CONfig.equals(descriptor.getUuid())) {
                if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE,value)) {
                    Log.i("BLE","Subscribe device to notifications: " + device);
                    mRegisteredDevices.add(device);
                } else if (Arrays.equals(BluetoothGattDescriptor.disABLE_NOTIFICATION_VALUE,"Unsubscribe device from notifications: " + device);
                    mRegisteredDevices.remove(device);
                }
                if (responseNeeded) {
                    mBluetoothGattServer.sendResponse(device,null);
                }
            } else {
                Log.w("BLE","UnkNown descriptor write request");
                if (responseNeeded) {
                    mBluetoothGattServer.sendResponse(device,null);
                }
            }
        }
    };

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...