Android 使用 BluetoothGattServer

Posted

技术标签:

【中文标题】Android 使用 BluetoothGattServer【英文标题】:Android Using BluetoothGattServer 【发布时间】:2016-05-12 08:51:04 【问题描述】:

对于一个应用程序,我需要一个 android 设备既是 Ble Gatt 外围设备又是服务器以接受传入和发送传出消息;但是,除了在 github 中查看其他人的项目之外,我似乎找不到太多与设置和维护服务器有关的信息。 谁能告诉我正确的解决方案或指导我了解有关设置 BluetoothGattServer 的更多信息。

【问题讨论】:

【参考方案1】:

我想快速提一下,目前使用的大多数 Android 设备都不支持 BluetoothGattServer。然而,更新的模型越来越多地具有这种能力。

首先,您可能想要宣传您的服务,以便其他设备了解该服务器。为此,我们需要为 ScanResponses 创建 AdvertiseSettings、AdvertiseData 和 AdvertiseData。

AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setConnectable(true)
                .build();

AdvertiseData advertiseData = new AdvertiseData.Builder()
                    .setIncludeDeviceName(true)
                    .setIncludeTxPowerLevel(true)
                    .build();

AdvertiseData scanResponseData = new AdvertiseData.Builder()
                    .addServiceUuid(new ParcelUuid(your_service_uuid))
                    .setIncludeTxPowerLevel(true)
                    .build();

之后需要为广告状态创建回调:

AdvertiseCallback callback = new AdvertiseCallback() 
                @Override
                public void onStartSuccess(AdvertiseSettings settingsInEffect) 
                    Log.d(TAG, "BLE advertisement added successfully");
                

                @Override
                public void onStartFailure(int errorCode) 
                    Log.e(TAG, "Failed to add BLE advertisement, reason: " + errorCode);
                
            ;

现在您可以开始宣传您的服务了:

BluetoothLeAdvertiser bluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
bluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponseData, callback);

对于BluetoothGattServer,我们首先需要创建一个BluetoothGattServerCallback:

BluetoothGattServerCallback callback = new BluetoothGattServerCallback() 
            @Override
            public void onConnectionStateChange(BluetoothDevice device, int status, int newState) 
                super.onConnectionStateChange(device, status, newState);
            

            @Override
            public void onServiceAdded(int status, BluetoothGattService service) 
                super.onServiceAdded(status, service);
            

            @Override
            public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) 
                super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
            

            @Override
            public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) 
                super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
            

            @Override
            public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) 
                super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
            

            @Override
            public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) 
                super.onDescriptorReadRequest(device, requestId, offset, descriptor);
            

            @Override
            public void onNotificationSent(BluetoothDevice device, int status) 
                super.onNotificationSent(device, status);
            

            @Override
            public void onMtuChanged(BluetoothDevice device, int mtu) 
                super.onMtuChanged(device, mtu);
            

            @Override
            public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) 
                super.onExecuteWrite(device, requestId, execute);
            
        ;

您不需要实现所有这些方法,只需实现您感兴趣的那些。例如,您可以实现 onCharacteristicReadRequest(...) 方法以将数据返回到读取 BluetoothGattServer 上的特征的设备。

之后您可以打开一个 GattServer,创建您的服务并将该服务添加到服务器:

BluetoothGattServer bluetoothGattServer = mBluetoothManager.openGattServer(Context, callback);
BluetoothGattService service = new BluetoothGattService(your_service_uuid, BluetoothGattService.SERVICE_TYPE_PRIMARY);

//add a read characteristic.
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(your_characteristic_uuid, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);

service.addCharacteristic(characteristic)

bluetoothGattServer.addService(service);

现在您已经设置了具有读取特性的 BluetoothGattServer,其他设备可以从中读取。 我希望这个简短的摘要对您有所帮助,否则我会尽力帮助您澄清不清楚的事情。

【讨论】:

服务器也可以是 gatt 客户端吗?我似乎有一个问题,因为添加服务器与 gatt 客户端连接但是,我假设当手机尝试连接到另一个本身已经连接到的设备时会出现问题。再一次,随着接口的变化,无法识别 ble 设备,equals 方法仅基于更改的地址,并且在 api23 中它返回不正确的地址。 是的,设备可以同时充当外围设备和中心设备。至于您遇到的问题,我不太确定,但据我所知,它应该可以工作。请记住,Android ble 堆栈非常不稳定。 对于识别,有一种方法可以识别设备。在广告设置中,您可以向服务 uuid 添加其他数据,只需在 ScanResponseData 的构建中添加对 addServiceData(...) 的调用。但是,ScanResponseData 的大小不能超过 31 字节。另一个陷阱是,在接收端,serviceData 键是 32 位 uuid 而不是 128 位。 这实际上是我唯一识别大部分工作的设备的方法;但是,虽然我不确定我认为当我连接到的设备反过来尝试连接到我时可能会出现问题,但根据我目前对 API23 更改的理解,我无法阻止它。 API23 robinhenniges.com/en/android6-get-mac-address-programmatically 。我知道 wifi 和蓝牙会发生 MAC 地址隐藏,但它是否也会破坏识别 gatt 设备的平等性,因为它似乎只使用它的 MAC 地址作为平等性的识别 据我所知,隐藏仅适用于您的“自己的”设备,不适用于扫描的设备。发生的情况是,大多数 Android 设备和所有 ios 设备都会定期更改自己的 mac 地址(有些更短,有些更长)。这意味着只要扫描的设备没有更改其 MAC 地址,相等就可以工作。

以上是关于Android 使用 BluetoothGattServer的主要内容,如果未能解决你的问题,请参考以下文章

Android之SharedPreferences使用

想要使用cordova/android禁用android的HardBack按钮

如何在Mac中使用Android SDK

何时使用“?android”或“@android”?

Android 安装包优化Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )

Android Handler使用