15

I connect to a BLE device with the connectGatt() method in Android. This works great.

When I disconnect I use the following:

private void disconnectDevice() {
    gatt.disconnect();
}

When I receive the callback I do a close.

private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        switch (newState) {
            case BluetoothProfile.STATE_CONNECTED:
                Log.d("BLED-GATT", "STATE_CONNECTED");
                setUIConnectionStatus("Discover services on device...", Color.YELLOW);
                checkEnableAddButton(simpleTrackEditText.getText().toString());
                gatt.discoverServices();
                break;
            case BluetoothProfile.STATE_DISCONNECTED:
                Log.d("BLED-GATT", "STATE_DISCONNECTED");
                setUIConnectionStatus("Not Connected!", Color.RED);
                gatt.close();
                break;
            default:
                Log.d("BLED-GATT", "STATE_OTHER");
        }
    }
}

This is executed and I can no longer control the device after calling disconnectDevice(). The device itself seems to think that it is still connected since I cant put it in broadcasting visibility mode (which happens if it already has a connection). However, if I kill the application and open it again then I can set the device in broadcasting mode. This tells me the app was not properly disconnected.

Any idea what I missed here?

nilsi
  • 10,351
  • 10
  • 67
  • 79
  • 1
    do you stop scanning for devices after you connect to the ble device? I had a similar issue which the phone stayed connected to the ble chip even after calling disconnect. The issue was that I never called mScanner.stopScan(mScanCallback) after successful connection. – SoroushA Mar 14 '16 at 17:32
  • Thank you but yes I call stopScan upon successful connection. – nilsi Mar 15 '16 at 02:21

4 Answers4

17

The problem was that I during scanning was connecting to the same device multiple times causing my application to have many connections open at the same time. Adding !isConnected() solved the problem:

/**
 * Connects to the device. Does nothing if already connected.
 * @param macAddress the address of the device.
 */
private void connectDevice(String macAddress) {
    if (isConnected()) {
        return;
    }
    
    device = bluetoothAdapter.getRemoteDevice(macAddress);

    if (device == null) {
        showToast("Device not available");
    } else {
        showToast("Connecting...");
        gatt = device.connectGatt(this, true, gattCallback);
    }
}
nilsi
  • 10,351
  • 10
  • 67
  • 79
  • Does `isConnected()` return some kind of a private variable that you track? Can you elaborate more...? Thx! – Tanasis Jun 25 '16 at 12:14
  • Don't remember but I think that was just a utility method I made. Checking if `gatt` was null, if so then I was sure there was no connection already. I should also have set `gatt` to null in `onConnectionStateChange` I think – nilsi Jun 26 '16 at 05:05
  • That is not proper behavior by the peer according to the spec. When a peer receives a connection event, it shall stop advertising. Since many BTLE devices behave badly (as does the Android API) as a work-a-round I ignore any received advertisement from an already connected device. I do turn of scanning during connection but once connected I turn it back on. Otherwise you cannot support multiple simultaneous connections. – Brian Reinhold Mar 28 '19 at 10:12
  • is it even possible to have multiple connections from the same mobile phone to the same ble device? based on my understanding is that you could have multiple GATT connections but not multiple GAP connections right? – Manas Oct 28 '22 at 11:43
7

That's totally logic because you are not disconnecting before closing Gatt.

public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }

try to call this method before closing your GATT

Fakher
  • 2,098
  • 3
  • 29
  • 45
  • 7
    I am disconnecting before closing, the callback gets called on disconnect. Which means I already disconnected it when calling `close()`? Your answer is only adding a null check to my code? – nilsi Mar 14 '16 at 15:27
  • 1
    no no in fact you must call the disconnect method otherwise it just close the Gatt without disconnecting !! just try it – Fakher Mar 14 '16 at 15:46
1

I experienced same problem and I found two possible root cause as follows: 1. Was same as @nilsi answer, connect to the same device more than once in one scanning. → solved by lock & List 2. connect to the device in one scanning and didn't get onConnectionStateChange, than I scanned again, it resulted in second connection to the same device. → solved by keeping BluetoothGatt object when I call device.connectGatt(), and call object.disconnect(), object.close() before scanning.

My test phone is 4 years old so sometimes it needs more than 20 seconds to return onConnectionStateChange....

Ray Chung
  • 21
  • 3
1

As previously stated, the problem is caused by calling connectGatt multiple times. Each one of those calls create a new BluetoothGatt instance and they are all kept alive, while you have only the last one. Given the fact that sometimes it is needed to call connectGatt multiple times, I just keep all the instances that it returns and call disconnect/close on all of them when I'm done. This fixed the disconnection issue instantly

private val gattInstances = LinkedList<BluetoothGatt>()


fun connect() {
    bluetoothGatt = device?.connectGatt(
        context,
        false, gattCallback, TRANSPORT_LE
    )
    bluetoothGatt?.let { gattInstances.add(it) }
}

fun finish() {
    bluetoothGatt?.close()
    while (gattInstances.isNotEmpty()) {
        gattInstances.pop().apply {
            disconnect()
            close()
        }
    }
}
Nick
  • 585
  • 4
  • 11