Totally Disconnect a Bluetooth Low Energy Device

22,446

Solution 1

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);
    }
}

Solution 2

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

Solution 3

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....

Solution 4

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()
        }
    }
}
Share:
22,446
nilsi
Author by

nilsi

Hi!

Updated on September 03, 2021

Comments

  • nilsi
    nilsi almost 3 years

    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?

    • SoroushA
      SoroushA over 8 years
      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.
    • nilsi
      nilsi over 8 years
      Thank you but yes I call stopScan upon successful connection.
  • nilsi
    nilsi over 8 years
    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?
  • Fakher
    Fakher over 8 years
    no no in fact you must call the disconnect method otherwise it just close the Gatt without disconnecting !! just try it
  • Tanasis
    Tanasis about 8 years
    Does isConnected() return some kind of a private variable that you track? Can you elaborate more...? Thx!
  • nilsi
    nilsi about 8 years
    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
  • Brian Reinhold
    Brian Reinhold over 5 years
    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.