4

For an application I have need for an android device to be both a Ble Gatt peripheral and server to accept both incoming and send outgoing messages; however, it seems I cannot find much information pertaining to setting up and maintaining the server other than looking at projects of others in github. Can anyone show me the correct solution or guide me to more information regarding setting up BluetoothGattServer.

kdgwill
  • 2,129
  • 4
  • 29
  • 46

1 Answers1

18

I would like to quickly mention, that most of the Android devices in use don't support the BluetoothGattServer. However newer models have that capability more and more.

First of all you probably want to advertise your service, so that other devices know about the server. For that we need to create the AdvertiseSettings, the AdvertiseData and the AdvertiseData for ScanResponses.

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

After that you need to create a callback for the advertising status:

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

Now you can start advertising your service:

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

For the BluetoothGattServer we need first to create a 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);
            }
        };

You don't need to implement all of those methods, only those you are interested in. For example you could implement the onCharacteristicReadRequest(...) method to return data to a device reading the characteristic on your BluetoothGattServer.

After that you can open a GattServer, create your service and add the service to the server:

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

Now you have setup your BluetoothGattServer with a read characteristic, from which other devices can read. I hope this short summary helps you, otherwise I will try to help you clarify the unclear things.

p2pkit
  • 1,159
  • 8
  • 11
  • Can a server also be a gatt client ? I seem to have an issue since adding the server with the gatt client connecting however, I am assuming the problem occurs when the phone attempts to connect to another device that itself is already connected to. Once more there is no way to identify a ble device as the interfaces change and the equals method is based only on the address which changes and also in api23 it returns the incorrect address. – kdgwill May 19 '16 at 01:26
  • Yes a device can act as a peripheral and central at the same time. As for the issue you have, I am not quite sure, but as far as I know it should work. Keep in mind, that the Android ble stack is very unstable. – p2pkit May 19 '16 at 08:18
  • For the identification, there is a way you could identify a device. In the advertisement setup you can add additional data to a service uuid, just add a call to addServiceData(...) in the building of the ScanResponseData. However the size of the ScanResponseData cannot exceed 31 Bytes. Another pitfall is, that on the receiving side the serviceData key is a 32 bit uuid instead of a 128bit. – p2pkit May 19 '16 at 08:24
  • That is actually what I do to uniquely identify devices which works for the most part; however, though I am not sure I think the issue may occur when a device I am connected to in turn attempts to connect to me, but I have no way to prevent it based on my current understanding of changes in API23. API23 http://robinhenniges.com/en/android6-get-mac-address-programmatically . I know the MAC address hiding happens for wifi and bluetooth but will it also break equality for identifying gatt devices as it seems to soley use it's mac address as identification in equality – kdgwill May 19 '16 at 10:44
  • As far as I know the hiding applies only to your "own" device and not for the scanned ones. What happens, is that most of the Android devices and all iOS devices change their own mac address in an interval (some shorter, some longer). This means the equality works as long as the scanned device did not change its mac address. – p2pkit May 19 '16 at 13:35