Enabling peripheral mode in Android KitKat (4.4)

If you were hoping to see support for Bluetooth LE peripheral mode in Android Kit Kat we have bad news for you:  it is not there.  The bluedroid stack supports that capability but it is not hooked up to the Android framework, and therefore inaccessible to applications.

Our team could not wait, so we rolled up our sleeves and got to work.   After some digging around we noticed that the function that does what we want is buried deep in the stack:

/*******************************************************************************
**
** Function         GATT_Listen
**
** Description      This function start or stop LE advertisement and listen for
**                  connection.
** (...) 
*/
BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr)

On the app side, we extended the BluetoothGattServer.java to support a new functions, listen().  Connecting the two requires many (but straighforward) changes in the following files:

project external/bluetooth/bluedroid:

a/bta/gatt/bta_gatts_act.c
a/bta/gatt/bta_gatts_api.c
a/bta/gatt/bta_gatts_main.c
a/btif/src/btif_gatt_server.c
a/stack/gatt/gatt_api.c

project frameworks/base:

a/core/java/android/bluetooth/BluetoothGattServer.java
a/core/java/android/bluetooth/IBluetoothGatt.aidl

project hardware/libhardware:

a/include/hardware/bt_gatt_server.h

project packages/apps/Bluetooth:

a/jni/com_android_bluetooth_gatt.cpp
a/src/com/android/bluetooth/gatt/GattService.java

After compiling and updating the framework, you can easily advertise a service by:

public void startPeripheralGattServer() {
    final BluetoothManager bluetoothManager =
        (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);

    mGattServer = bluetoothManager.openGattServer(mContext, new BluetoothGattServerCallback() {

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

            if (mGattServer != null) {
                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, new byte[] { bogusValue++ });
        }
});

    UUID serviceUUID = UUID.randomUUID();
    UUID characteristicUUID = UUID.randomUUID();
    UUID descriptorUUID = UUID.randomUUID();

    BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(characteristicUUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ);
    characteristic.setValue(77, BluetoothGattCharacteristic.FORMAT_UINT8, 0);

    BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(descriptorUUID,     BluetoothGattDescriptor.PERMISSION_READ);

    characteristic.addDescriptor(descriptor);

    BluetoothGattService service = new BluetoothGattService(serviceUUID,     BluetoothGattService.SERVICE_TYPE_PRIMARY);
    service.addCharacteristic(characteristic);

    mGattServer.addService(service);
    mGattServer.listen();
}

The video below shows how an Xperia Z phone with our updated Android image and minimal Bluetooth application is seen as a Bluetooth LE peripheral by an iPhone.

And now I was going to give details on how we implemented the glue code, but the Android folks seem to be already working on this as can be seen here. So it’s very likely this will be supported in 4.5.  Yay!

Leave a Reply

Your email address will not be published. Required fields are marked *