45

First of all I read SOLVED: GATT callback fails to register and took the steps suggested in that post to solve this issue with no success. The recommended fix in there if you haven't read it is to make all BLE calls from the main thread directly or with a handler.

I am working on a BLE app want to run a service (called from activity once every 10 seconds) that performs these tasks:

1)Gets list of our products available to connect to (done, works)

2)For each available device:

          2a)connect to device
          2b)discover services
          2c)read 5 characteristics in this fashion:
             2c1)read characteristic
             2c2)onCharacteristicRead parse data
             2c3)when finished with data read next characteristic
             2c4)repeat until all are read (this is done using a state var and switch statement)
         2d)disconnect from device
         2e)connect to next device
         2f)repeat until all devices are read from
         2g)stopSelf()

So the issue... Everything works great for a little bit. I can perform the entire service start {startService(...); in mainActivity} to finish {stopSelf(); in Service} 6 times.

On the 7th time I get BluetoothGatt Failed to register callback. I'm not sure why I can run it 6 times successfully and then fail on the 7th time.

Keep in mind I am making all BLE calls from the main thread, and that has been confirmed in the log cat from multiple locations.

Here is an outline of my code:

SERVICE.JAVA

private Handler handler = new Handler();
private BluetoothGatt cGatt = null;
private int unitIndex = 0; // keep track of currently connected unit
private int state = 0; //used to keep track of which characteristic to read next

public int onStartCommand(Intent intent, int flags, int startId) 
{
    Log.i(TAG, "Service Started...");
    //get ArrayList of units

    if(units.size > 0)
        handler.post(connectNextRunnable); //calls connectNextDevice()
    else
        stopSelf();   
}

private Runnable discoverServices = new Runnable()
{
    public void run()
    {
        cGatt.discoverServices();
    }
}

private Runnable readNextValue = new Runnable()
{
    public void run()
    {
        BluetoothGattCharacteristic c = null;
        switch(state)
        {
            //set c to appropriate characteristic
        default: // all characteristics read
            unitIndex++;
            handler.post(connectNextRunnable)
            return
        }

        cGatt.readCharacteristic(c);
    }
}

private void connectNextDevice()
{
    if(unitIndex == 0)
        store System.nanoTime in variable

    if(unitIndex >= units.size) //finished will all units
        stopSelf();

    if(unitIndex < units.size)
        cGatt.disconnect //if null
        cGatt.connectGatt(this, false, gattCallback)
}

private BluetoothGattCallback gattCallback = new BluetoothGattCallback() 
{
    public void onConnectionStateChange() 
    {
        handler.post(discoverServices);
    }

    public void onServicesDeiscovered() 
    {
        handler.post(readNextValue);
    }

    public void onCharacteristicRead() 
    {
        ParseData();
    }

    private void ParseData()
    {
        //do stuff with data
        handler.post(readNextValue);
    }
}

So, like I have said, all BLE stuff is called from the main thread through a handler. The service successfully runs 6 times from start to finish. On the 7th time I get that dumb failed to register callback.

I can provide more logcat information if you think it is relevant. I did not in the original post because I am output a lot of information to it to verify data received etc..

The information below is the logcat information for the 7th run of my service from start to finish.

08-15 12:00:10.746: I/PMIQ BTS(32027): Service Started...
08-15 12:00:10.746: I/PMIQ BTS(32027): Units: 1
08-15 12:00:10.746: D/AbsListView(32027): unregisterIRListener() is called 
08-15 12:00:10.766: I/PMIQ BTS(32027): Connecting to next device...
08-15 12:00:10.766: I/PMIQ BTS(32027): Unit index = 0
08-15 12:00:10.766: I/PMIQ BTS(32027): Connecting to pmIQ-IQ130_D93A
08-15 12:00:10.766: I/System.out(32027): main
08-15 12:00:10.766: D/BluetoothGatt(32027): connect() - device: 00:1E:C0:19:D9:3A, auto: false
08-15 12:00:10.766: D/BluetoothGatt(32027): registerApp()
08-15 12:00:10.766: D/BluetoothGatt(32027): registerApp() - UUID=e9d10870-4b09-451c-a9fa-c6b5f3594a77
08-15 12:00:10.766: I/BluetoothGatt(32027): Client registered, waiting for callback
08-15 12:00:10.766: D/BluetoothGatt(32027): onClientRegistered() - status=133 clientIf=0
08-15 12:00:10.766: I/PMIQ BTS(32027): CONECTION STATE CHANGED...Binder_2
**08-15 12:00:10.766: E/BluetoothGatt(32027): Failed to register callback**
08-15 12:00:10.766: I/PMIQ BTS(32027): Could not connect to null ... 257
08-15 12:00:10.766: I/PMIQ BTS(32027): Connecting to next device...
08-15 12:00:10.766: I/PMIQ BTS(32027): Unit index = 1
08-15 12:00:10.766: I/PMIQ BTS(32027): ******************************
08-15 12:00:10.766: I/PMIQ BTS(32027): Start Time: 4360642409647
08-15 12:00:10.766: I/PMIQ BTS(32027): End Time: 4360648970925
08-15 12:00:10.766: I/PMIQ BTS(32027): Difference: 6561278
08-15 12:00:10.766: I/PMIQ BTS(32027): Time to complete: 6
08-15 12:00:10.766: I/PMIQ BTS(32027): ******************************
08-15 12:00:10.876: I/PMIQ BTS(32027): ...Service Destroyed

If you have made it here, thanks! I could not find ANY information on what status=133 means?! It only happens when the callback fails. Every other time it is status=0.

08-15 12:00:10.766: D/BluetoothGatt(32027): onClientRegistered() - status=133 clientIf=0

If anyone could even answer this.. it may help me greatly. Or if anyone can tell me why it only runs 6 times. Any insight or hunch could be helpful!

Thanks everyone!

Community
  • 1
  • 1
Chris K
  • 1,581
  • 1
  • 10
  • 17
  • There is a possible answer to this here https://code.google.com/p/android/issues/detail?id=68538 However I won't be able to test until Monday, it seems I'm not disconnecting properly from the device. – Chris K Aug 15 '14 at 20:32
  • it does work for me i still get error 133 after calling close in android – prasanthMurugan Oct 25 '17 at 09:40

6 Answers6

65

Alright I have figured it out. The issue was mainly an oversight of when I was reading through the BluetoothGatt documentation. I was calling .disconnect(), but not .close(). Since the Galaxy s4 can only handle 6 connections at a time, my service was only running 6 times. Adding the .close() to my code allowed it to properly shut down the connection and freed up those used connections.

Source that made me re-read the docs more carefully!

So remember to use .close() on your BluetoothGatt object if you have a recurring connection to the same device(s)!!

D.J
  • 1,439
  • 1
  • 12
  • 23
Chris K
  • 1,581
  • 1
  • 10
  • 17
  • 16
    133 is GATT_ERROR (source: https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-4.4.4_r2.0.1/stack/include/gatt_api.h) – PaulT Jun 03 '15 at 20:17
  • if I do .close() then it close BT of device... :| Am I missing anything !! – CoDe Oct 29 '15 at 06:51
  • @shubh make sure you do disconnect and close on bluetoothgatt – Deko Nov 06 '15 at 06:01
  • 2
    Yes, Your are right. But that also not helping, after some sequence Gatt_Error occur (Error Code: 133). Any suggestion! – CoDe Nov 06 '15 at 06:38
  • 1
    what's the difference between `disconnect()` and `close()` ? should i use disconnect() + close() or just one of them? Which one? – 4ntoine Feb 12 '16 at 13:17
  • @4ntoine this may help you http://stackoverflow.com/questions/23110295/difference-between-close-and-disconnect-in-android-bluetooth-api – Marian Paździoch Jun 30 '16 at 08:12
  • 4
    The answer is correct, but it doesn't resolve all the possible error cases.. at least for the experience I had with the beloved 133. – andrea.rinaldi Jul 19 '16 at 08:33
  • 3
    @andrea.rinaldi With error **133**, the only thing you can do is retry multiple times. That is the only known solution. – IgorGanapolsky May 01 '17 at 13:56
  • 1
    @ck1221 Where is the documentation that talks about the S4 only handling 6 devices? Should be 7, right? – Bryan Bryce May 24 '17 at 21:17
  • it does work for me i still get error 133 after calling close in android – prasanthMurugan Oct 24 '17 at 14:12
30

After months of research and pulling my hair out, I have found a solution that is not normally talked about.

Your normal connect request looks something like this:

cGatt.connectGatt(this, false, gattCallback);

There is another version of the connectGatt command, with a 4th parameter. This parameter specifies what type of bluetooth device you are connecting to. I added a "2" to specify that I am connecting via Bluetooth LE. (it's called "transport", forgive me if my explanation is incorrect, but it solved all my problems)

Try this:

cGatt.connectGatt(this, false, gattCallback, 2);

And BAM, now my #133 nightmare is over (I pray)!

btmcmahan
  • 301
  • 3
  • 2
  • 11
    Take care, the `connectGatt` method with `transport` parameter requires API level 23. And you can use the constant `BluetoothDevice.TRANSPORT_LE` instead of hardcoded value 2. – Bubu Feb 06 '18 at 08:57
  • 1
    I have no idea why I needed to add this now in 2022, after 2 years of using this line with no issue. I'm guessing maybe the OS update for my hardware changed something with BLE to the point that this is required now to connect to my hardware but I would really like to know why. – Nonlin Dec 09 '22 at 02:02
15

Android OS < 6.0:

mBluetoothDevice.connectGatt(context, false, callback);

Android OS >= 6.0:

mBluetoothDevice.connectGatt(context, false, callback, BluetoothDevice.TRANSPORT_LE);

Ultimately, hardware equipment is needed to completely solve this problem.

Zero
  • 2,764
  • 1
  • 17
  • 20
  • Thanks for providing such a valuable answer. It woked for me. But It will be very helpfull if you provide more documentation about this logic. – vinay shetty Feb 18 '21 at 08:23
  • I have no idea why I needed to add this now in 2022, after 2 years of using this line with no issue. I'm guessing maybe the OS update for my hardware changed something with BLE to the point that this is required now to connect to my hardware but I would really like to know why. – Nonlin Dec 09 '22 at 02:02
2
  • On some devices, it got fixed with mBluetoothDevice.connectGatt(context, false, callback, BluetoothDevice.TRANSPORT_LE);

  • on some devices like Samsung S7, A8, it was a problem with the ScanSettings.Builder().setReportDelay(400) // or 500ms. it should not be 0 or more like 1000ms. ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_BALANCED) .setReportDelay(400) .build();

1

Try to use next workaround:

private static boolean gatt_status_133 = false;

final Handler handler = new Handler();

public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

    if (newState == BluetoothProfile.STATE_CONNECTED) {
        mConnectionState = STATE_CONNECTED;
        Log.i(TAG, "Connected to GATT server.");
        // Attempts to discover services after successful connection.
        Log.i(TAG, "Attempting to start service discovery:" +
                mBluetoothGatt.discoverServices());

    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
        if(status == 133)
        {
            gatt_status_133=true;
        }
        else{
            mConnectionState = STATE_DISCONNECTED;
            Log.i(TAG, "Disconnected from GATT server."); 
        }
    }
}


@RequiresApi(api = Build.VERSION_CODES.M)
public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
        return false;
    }

    // Previously connected device.  Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
            && mBluetoothGatt != null) {
        Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
        if (mBluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        Log.w(TAG, "Device not found.  Unable to connect.");
        return false;
    }

    mBluetoothGatt= device.connectGatt(this, false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
    Log.d(TAG, "Trying to create a new connection.");
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;

    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            if(gatt_status_133)
            {
                Log.d(TAG, "Catch issue");
                connect(address);
                gatt_status_133=false;
            }
        }
    }, 4000);

    return true;
}
J Blaz
  • 783
  • 1
  • 6
  • 26
1

Somehow BluetoothLeScanner.startScan() helps

private final Handler mMainHandler = new Handler(Looper.getMainLooper());
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
    if (newState == BluetoothProfile.STATE_CONNECTED) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mBluetoothAdapter.isEnabled() && mBleScanner != null) {
            mBleScanner.stopScan(mLeScanCallback);
        }
    } else if (status == 133 && newState == BluetoothProfile.STATE_DISCONNECTED) {
        if (D) Log.e(TAG, "connectGatt status == 133");
        mMainHandler.post(mDisconnectRunnable);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mBluetoothAdapter.isEnabled()) {
            mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();
            mBleScanner.startScan(null, new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(),
                    mLeScanCallback);
        }
        mMainHandler.postDelayed(mConnectRunnable, 10000);
    }
}

private ScanCallback mLeScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        if (result.getDevice().getAddress().equals(mBluetoothDeviceAddress)) {
            mMainHandler.post(mConnectRunnable);
            if (mBleScanner != null) {
                mBleScanner.stopScan(mLeScanCallback);
                mBleScanner = null;
            }
        }
    }
};
Alexandr
  • 11
  • 1