Bluetooth GATT onConnectionState Change does not work on Lollipop

4.2k views Asked by At

I currently have a method which writes to the BLE devices to beep it. My Bluetooth Callback goes as follows :

public class ReadWriteCharacteristic extends BluetoothGattCallback {
    public ReadWriteCharacteristic(Context context, String macAddress, UUID service, UUID characteristic, Object tag, Activity activity) {
        mMacAddress = macAddress;
        mService = service;
        mCharacteristic = characteristic;
        mTag = tag;
        mContext = context;
        this.activity =activity;
        final BluetoothManager bluetoothManager =
                (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

    }

    final private static String TAG = "ReadCharacteristic";
    private Object mTag;
    private String mMacAddress;
    private UUID mService;
    private BluetoothManager mBluetoothManager = null;
    private BluetoothAdapter mBtAdapter = null;
    private BluetoothGatt mBluetoothGatt = null;
    private String mBluetoothDeviceAddress ="";
    private UUID mCharacteristic;
    BluetoothDevice device;
    private Activity activity;
    private BluetoothAdapter mBluetoothAdapter;
    private Context mContext;
    ReadWriteCharacteristic rc;

    private int retry = 5;


    public String getMacAddress() {
        return mMacAddress;
    }

    public UUID getService() {
        return mService;
    }

    public UUID getCharacteristic() {
        return mCharacteristic;
    }

    public byte[] getValue() { return mValue; }

    public void onError() {
        Log.w(TAG, "onError");
    }

    public void readCharacteristic(){
        if (retry == 0)
        {
            onError();
            return;
        }
        retry--;


                device = mBluetoothAdapter.getRemoteDevice(getMacAddress());


                mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
                int connectionState = mBluetoothManager.getConnectionState(device,
                        BluetoothProfile.GATT);

                if (device != null) {

                    if (connectionState == BluetoothProfile.STATE_DISCONNECTED)
                    {

                        // Previously connected device. Try to reconnect.
                        if (mBluetoothDeviceAddress != null
                                && getMacAddress().equals(mBluetoothDeviceAddress)
                                && mBluetoothGatt != null) {
                            Log.w(TAG, "Re-use GATT connection");
                            if (mBluetoothGatt.connect()) {
                                Log.w(TAG, "Already connected, discovering services");
                                mBluetoothGatt.discoverServices();
                                //return ;
                            } else {
                                Log.w(TAG, "GATT re-connect failed.");
                                return;
                            }
                        }

                        if (device == null) {
                            Log.w(TAG, "Device not found.  Unable to connect.");
                            return;
                        }
                        Log.w(TAG, "Create a new GATT connection.");
                        rc= ReadWriteCharacteristic.this;
                        Log.w(TAG, "Starting Read [" + getService() + "|" + getCharacteristic() + "]");
                        mBluetoothGatt = device.connectGatt(mContext, false, rc);
                        refreshDeviceCache(mBluetoothGatt);
                        mBluetoothDeviceAddress = getMacAddress();
                    } else {
                        Log.w(TAG, "Attempt to connect in state: " + connectionState);
                        if(mBluetoothGatt!=null)
                            mBluetoothGatt.close();
                        readCharacteristic();
                    }

                }

    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);

        Log.w(TAG,"onConnectionStateChange [" + status + "|" + newState + "]");


        if ((newState == 2)&&(status ==0)) {
            gatt.discoverServices();
        }
        else if(status == 133 )
        {
            //gatt.disconnect();
            gatt.close();
            mBluetoothGatt = null;
            try
            {
                Thread.sleep(2000);
            }
            catch(Exception e)
            {

            }
            readCharacteristic();
        }
        else{

            if(mBluetoothGatt!=null)
                mBluetoothGatt.close();

           // gatt.close();

            Log.w(TAG, "[" + status + "]");
            //gatt.disconnect();

            try
            {
                Thread.sleep(2000);
            }
            catch(Exception e)
            {

            }
            mBluetoothGatt = null;
            readCharacteristic();
        }
    }



    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        Log.w(TAG,"onServicesDiscovered [" + status + "]");
        BluetoothGattService bgs = gatt.getService(getService());
        if (bgs != null) {
            BluetoothGattCharacteristic bgc = bgs.getCharacteristic(getCharacteristic());
            gatt.readCharacteristic(bgc);
        }
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic,
                                      int status) {
        Log.w(TAG,"onCharacteristicWrite [" + status + "]");
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.w(TAG,"onCharacteristicWrite [" + getDataHex(characteristic.getValue()) + "]");
           // gatt.disconnect();
            if(mBluetoothGatt!=null)
                mBluetoothGatt.close();
          //  gatt.close();
          //  mBluetoothGatt=null;
        }
        else if(status ==133)
        {
            gatt.close();
            try
            {
                Thread.sleep(2000);
            }
            catch(Exception e)
            {

            }

            readCharacteristic();
        }

        else{
            //gatt.disconnect();
            gatt.close();

        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        Log.w(TAG,"onCharacteristicRead [" + status + "]");
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mValue = characteristic.getValue();
            // Perform write operations
            gatt.writeCharacteristic(bgc);


        }

        else if(status ==133)
        {
            gatt.close();
            try
            {
                Thread.sleep(2000);
            }
            catch(Exception e)
            {

            }

            readCharacteristic();
        }

        else {
          //  gatt.disconnect();
            gatt.close();
        }
    }

}

This code works perfectly on device running Kitkat and below. But on Devices running Lollipop, this code works fine for the first instance. But from the next instance, irrespective of whether I disconnect or close the connection and try again, it just does not work. It keeps giving me a status code of 257 in onConnectionStateChange method. As far as I know, The Bluetooth GATT methods are the same for both kitkat and Lollipop devices.

What surprises me is that this code works fine on Lollipop devices when i use the old BLE API i.e startLeScan ( For eg - mBluetoothAdapter.startLeScan(mLeScanCallback);). This problem only arises when I use the new BLE API i.e BluetoothLeScanner ( scanner.startScan(filters, settings, new scancallback());). The scanning rate is really slow for Lollipop devices using the old BLE API, hence I cannot use it. I just don't understand how to solve this problem.Has anyone faced the same problem and found a solution? Any help would be deeply appreciated.

1

There are 1 answers

2
nickle_nine On

Quite a few things I would change here. Create a class variable for the data you want to read from the characteristic, such as private string heartRate;

1) You don't need the readCharacteristic() method. Instead, in the onConnectionStateChange once the device has connected properly, call mBluetoothGatt.discoverServices(). Then in the onServicesDiscovered() method I would call gatt.getServices(). Then use a foreach loop and loop through the returned services and compare the UUID of the service until you find the one you care about. Then if heartRate == null, call service.getCharacteristic(HeartRateUUID) and then read the characteristic. In onCharacteristicRead() check if the UUID is equal to the heart rate characteristic. If it is, assign the value of the characteristic to the heartRate variable. If you are interested, I can type out the methods or provide pseudocode.

2) I wouldn't call gatt.connect() followed by gatt.discoverServices(). gatt.connect() will reconnect to the current device as soon as it sees an advertisement packet from the device. I would call gatt.connect() and then call gatt.discoverServices() in the onConnectedStateChange() method.

3) In the onConnectedStateChange method don't use the gatt variable. Use mBluetoothGatt instead. mBluetoothGatt.disconnect() disconnects from the currently connected device. mBluetoothGatt.close() terminates the gatt instance. You cannot call mBluetoothGatt.connect() after calling mBluetoothGatt.Close(). This might not be needed, but if the device is connected I call mBluetoothGatt.disconnect() followed by mBluetoothGatt.close().

4) You can also chain characteristic readings together. In onCharacteristicRead(), after you get the value of the heartRate, you can immediately call characteristic.getService().getCharacteristic(UUIDTemperature) and then read that characteristic. It will call the OnCharacteristicRead method again.

Let me know if you want me to clarify anything; I'm typing on the crappy Surface Pro 3 keyboard. :)