eipipuz
eipipuz

Reputation: 563

How to get BLE notifications in android?

I'm trying to get notifications for custom device. I know the device is working because in iOS with the Lightblue app I do get the notifications. I have tried with both a nexus 5 and a samsung s4 (both kitkats). Nothing seems to work. My "onCharacteristicChanged" is not getting called.

MainActivity.java

package com.foo.ble;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;


public class MainActivity extends Activity implements BluetoothAdapter.LeScanCallback {
    private BluetoothAdapter adapter;

    final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
    private Callback callback;

    /**
     * Converts 0xfe to "FE"
     * @return hex representation of the adScanned
     */
    public static String getPayload(final byte[] adScanned) {
        if (adScanned == null) return "N/A";

        final char[] hexChars = new char[adScanned.length * 2];
        for (int j = 0; j < adScanned.length; j++) {
            final int v = adScanned[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        adapter = manager.getAdapter();
        callback = new Callback();

        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                adapter.stopLeScan(MainActivity.this);
            }
        }, 3*10000);

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter.startLeScan(MainActivity.this);
            }
        });
    }

    @Override
    public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
        if (device.getAddress().equals("CE:AD:09:F2:BB:DC")) {
            Log.d("foo", "onLeScan payload:" + getPayload(scanRecord));
            adapter.stopLeScan(this);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    device.connectGatt(MainActivity.this, true, callback);
                }
            });
        }
    }

    @Override
    protected void onDestroy() {
        callback.close();
    }
}

Callback.java

package com.foo.ble;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothProfile;
import android.util.Log;

import java.util.UUID;

public class Callback extends BluetoothGattCallback {
    final static UUID SERVICE = UUID.fromString("something1");
    final static UUID ADV_CHAR = UUID.fromString("something2");
    final static UUID ADV_DESCRIPTOR = UUID.fromString("something3");

    BluetoothGatt gatt;

    public void close() {
        if (gatt == null) return;
        Log.d("foo", "close with gatt");

        gatt.disconnect();
        gatt.close();
        gatt = null;
    }

    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, final int status,
                                        final int newState) {
        Log.d("foo", "onConnectionStateChange");
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.d("foo", "status connected");
            this.gatt = gatt;
            gatt.discoverServices();
        }
    }

    @Override
    public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
        Log.d("foo", "onServicesDiscovered");
        if (status == BluetoothGatt.GATT_SUCCESS) {
            final BluetoothGattCharacteristic characteristic =
                    gatt.getService(SERVICE).getCharacteristic(ADV_CHAR);
            final BluetoothGattDescriptor descriptor =
                    characteristic.getDescriptor(ADV_DESCRIPTOR);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            gatt.setCharacteristicNotification(characteristic, true);
            gatt.writeDescriptor(descriptor);
        }
    }

    @Override
    public void onCharacteristicRead(final BluetoothGatt gatt,
                                     final BluetoothGattCharacteristic characteristic,
                                     final int status) {
        Log.d("foo", "onCharacteristicRead status:" + status + " payload: " +
                MainActivity.getPayload(characteristic.getValue()));
    }

    @Override
    public void onCharacteristicWrite(final BluetoothGatt gatt,
                                      final BluetoothGattCharacteristic characteristic,
                                      final int status) {
        Log.d("foo", "onCharacteristicWrite");
    }

    @Override
    public void onCharacteristicChanged(final BluetoothGatt gatt,
                                        final BluetoothGattCharacteristic characteristic) {
        Log.d("foo", "onCharacteristicChanged payload:" +
                MainActivity.getPayload(characteristic.getValue()));
    }

    @Override
    public void onDescriptorWrite(final BluetoothGatt gatt,
                                  final BluetoothGattDescriptor descriptor,
                                  final int status) {
        Log.d("foo", "onDescriptorWrite status: " + status);
        final BluetoothGattCharacteristic characteristic =
                gatt.getService(SERVICE).getCharacteristic(ADV_CHAR);
        if (characteristic.getValue() != null) {
            Log.d("foo", "value: " + MainActivity.getPayload(characteristic.getValue()));
        } else {
            gatt.readCharacteristic(characteristic);
        }
    }

}

My AndroidManifest.xml has:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

The log looks like this:

11-05 12:17:13.314    3683-3683/com.foo.ble D/BluetoothAdapter﹕ startLeScan(): null
11-05 12:17:13.364    3683-3695/com.foo.ble D/BluetoothAdapter﹕ onClientRegistered() - status=0 clientIf=5
11-05 12:17:13.394    3683-3683/com.foo.ble I/Adreno-EGL﹕ <qeglDrvAPI_eglInitialize:320>: EGL 1.4 QUALCOMM Build: I0404c4692afb8623f95c43aeb6d5e13ed4b30ddbDate: 11/06/13
11-05 12:17:13.414    3683-3683/com.foo.ble D/OpenGLRenderer﹕ Enabling debug mode 0
11-05 12:17:13.454    3683-3683/com.foo.ble D/dalvikvm﹕ GC_FOR_ALLOC freed 238K, 2% free 16971K/17240K, paused 14ms, total 14ms
11-05 12:17:15.174    3683-3694/com.foo.ble D/foo﹕ onLeScan payload:02010517FF0000000000000005000000000000000000395401001207086C6976656C7911072E9AB04DB9790F857A4CF0634C7AB94A000000000000000000
11-05 12:17:15.174    3683-3694/com.foo.ble D/BluetoothAdapter﹕ stopLeScan()
11-05 12:17:15.184    3683-3683/com.foo.ble D/BluetoothGatt﹕ connect() - device: CE:AD:09:F2:BB:DC, auto: true
11-05 12:17:15.184    3683-3683/com.foo.ble D/BluetoothGatt﹕ registerApp()
11-05 12:17:15.184    3683-3683/com.foo.ble D/BluetoothGatt﹕ registerApp() - UUID=605cbd6f-6080-4fd8-97ff-1f97b406258e
11-05 12:17:15.194    3683-3695/com.foo.ble D/BluetoothGatt﹕ onClientRegistered() - status=0 clientIf=5
11-05 12:17:43.344    3683-3683/com.foo.ble D/BluetoothAdapter﹕ stopLeScan()
11-05 12:23:59.194    3683-3694/com.foo.ble D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=5 device=CE:AD:09:F2:BB:DC
11-05 12:23:59.194    3683-3694/com.foo.ble D/foo﹕ onConnectionStateChange
11-05 12:23:59.204    3683-3694/com.foo.ble D/foo﹕ status connected
11-05 12:23:59.204    3683-3694/com.foo.ble D/BluetoothGatt﹕ discoverServices() - device: CE:AD:09:F2:BB:DC
11-05 12:24:00.754    3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetService() - Device=CE:AD:09:F2:BB:DC UUID=randomService1
11-05 12:24:00.764    3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetService() - Device=CE:AD:09:F2:BB:DC UUID=randomService2
11-05 12:24:00.784    3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetService() - Device=CE:AD:09:F2:BB:DC UUID=something1
11-05 12:24:00.794    3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID1
11-05 12:24:00.804    3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID2
11-05 12:24:00.804    3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID3
11-05 12:24:00.814    3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID4
11-05 12:24:00.814    3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID5
11-05 12:24:00.814    3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID6
11-05 12:24:00.814    3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID7
11-05 12:24:00.814    3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=something2
11-05 12:24:00.824    3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetDescriptor() - Device=CE:AD:09:F2:BB:DC UUID=something3
11-05 12:24:00.824    3683-3695/com.foo.ble D/BluetoothGatt﹕ onSearchComplete() = Device=CE:AD:09:F2:BB:DC Status=0
11-05 12:24:00.824    3683-3695/com.foo.ble D/foo﹕ onServicesDiscovered
11-05 12:24:00.824    3683-3695/com.foo.ble D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: something2 enable: true
11-05 12:24:00.834    3683-3695/com.foo.ble D/BluetoothGatt﹕ writeDescriptor() - uuid: something3
11-05 12:24:00.894    3683-3694/com.foo.ble D/BluetoothGatt﹕ onDescriptorWrite() - Device=CE:AD:09:F2:BB:DC UUID=something3
11-05 12:24:00.894    3683-3694/com.foo.ble D/foo﹕ onDescriptorWrite status: 0
11-05 12:24:00.894    3683-3694/com.foo.ble D/BluetoothGatt﹕ readCharacteristic() - uuid: something2
11-05 12:24:00.994    3683-3695/com.foo.ble D/BluetoothGatt﹕ onCharacteristicRead() - Device=CE:AD:09:F2:BB:DC UUID=something2 Status=0
11-05 12:24:00.994    3683-3695/com.foo.ble D/foo﹕ onCharacteristicRead status:0 payload: 0000000000050000000000000000005154010012

Any help will be appreciated!

Upvotes: 1

Views: 1645

Answers (1)

Doug Koellmer
Doug Koellmer

Reputation: 427

I have observed notifications either not working with certain phone/peripheral/environment combinations, or stop working after having worked in the same connection session, with no apparent reason. All calls return success but notifications just don't come through. Regardless of if you solve the immediate problem at hand, you should consider doing a read poll for your characteristic value as a back-up solution.

The various calls in your onServicesDiscovered() method should also be gated by conditional checks on the return values of the various gatt methods you're calling.

Anyway, some things you can try:

  • Try resetting the Bluetooth stack through your phone settings, or even restarting the entire phone.
  • Test other devices. There are apps available for iOS that allow your iPhone to act as a peripheral. Try connecting to such a "sim device" and see if notifications work in this case.
  • Try a different Android phone. I've observed that the Nexus 4 has the most issues with notifications if that's what you're using.

Finally, I'll act like technical support asking if the computer is plugged in: make certain that the characteristic value on the peripheral is indeed changing.

Upvotes: 2

Related Questions