6

I establish a BLE connection to send data between devices (each 10 seconds aproximately they makes a new connection, send data, and then desconnect) and normally works fine. However, after some minutes, the devices never connect again and the device that should connect with the other is in loop calling "ACTION_GATT_DISCONNECTED" (this String is refering to disconnected receiver action).

In my receiver I've 3 relevant actions:

@Override
    public void onReceive(Context context, Intent intent) {
        customBluetoothManager = customBluetoothManager.getInstance(context, null, null);
        final String action = intent.getAction();
        Log.d("test", "onReceive");
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            Log.d("test", action);
        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
            Log.d("test", action);
        } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
            Log.d("test", action);
            //....
            //Code to send data after services discovered
            //....
        }
    }

So, during the first minutes all is working fine. First is calling to the ACTION_GATT_CONNECTED, then ACTION_GATT_SERVICES_DISCOVERED action is invoked, then the device sends the data, and after that it disconnects and releases the bluetooth communication channel. The problem is that, sometimes, and not always at the same point (by this reason I can't be able to find any pattern to reproduce this issue), is in loop always calling to ACTION_GATT_DISCONNECTED, so the communication never will be established.

The ACTION_GATT_DISCONNECTED is assigned in the onConnectionSateChange callback from BluetoothGattCallback class:

@Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "Connected to GATT server.");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
                mBluetoothGatt.close();
            }
        }

I have no idea of what can be the problem... Any suggestion?

---------- UPDATE --------------

Updated with the callback that makes the connection:

private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi,
                                     byte[] scanRecord) {

                    String name = device.getName();
                    long epoch = System.currentTimeMillis() / 1000;
                    SharedPreferences prefs = context.getSharedPreferences(
                            "epoch", Context.MODE_PRIVATE);
                    long epochStored = prefs.getLong("epoch", 0);

                    if (name != null && name.compareTo(bluetoothDeviceName) == 0 && (epochStored == 0 || epochStored < epoch - Utils.getDelay())) {

                        mac = device.getAddress();
                        mDeviceAddress = device.getAddress();
                        final Intent gattServiceIntent = new Intent(context, BluetoothLeService.class);

                        if (!connected) {
                            Utils.setMessageLog(ac, tv, "Binding service");
                            context.bindService(gattServiceIntent, mServiceConnection, context.BIND_AUTO_CREATE);
                        } else {
                            mBluetoothLeService.connect(mac);
                        }
                    }
                }
            };

Connect function:

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

        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);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

After the connection is succesfully, I receive a "SERVICES_DISCOVERED" action in my receiver and I write a characteristic through this code:

public void sendData() {
        List<BluetoothGattService> listServices = mBluetoothLeService.getSupportedGattServices();

        BluetoothGattService bluetoothGattService = null;
        for (BluetoothGattService gattService : listServices) {
            if (gattService.getUuid().compareTo(myUUID) == 0) {
                bluetoothGattService = gattService;
            }
        }

        if(bluetoothGattService != null) {
            List<BluetoothGattCharacteristic> gattCharacteristics = bluetoothGattService.getCharacteristics();
            ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();

            for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                if (gattCharacteristic.getUuid().compareTo(myCharUUID)) == 0) {
                    charas.add(gattCharacteristic);
                    boolean status = mBluetoothLeService.writeCharacteristic(gattCharacteristic, Utils.getUserData(context, "id"));
                }
            }
        }
    }

The first times that the bluetooth communication is established works fine.

------------- 2nd update --------------

I'm calling close() to release the connection:

@Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            disconnect();
            close();
        }

close() function:

public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

----------- UPDATE WITH ERROR LOG ------------

05-31 11:14:38.581 5897-6100/app D/OCSC: Status: 0
05-31 11:14:38.581 5897-6100/app  D/OCSC: New State: 2
05-31 11:15:08.361 5897-6073/app  D/OCSC: Status: 0
05-31 11:15:08.361 5897-6073/app  D/OCSC: New State: 2
05-31 11:15:35.031 5897-6010/app  D/OCSC: Status: 0
05-31 11:15:35.031 5897-6010/app  D/OCSC: New State: 2
05-31 11:16:08.331 5897-5909/app  D/OCSC: Status: 0
05-31 11:16:08.341 5897-5909/app  D/OCSC: New State: 2
05-31 11:16:36.031 5897-5908/app  D/OCSC: Status: 0
05-31 11:16:36.031 5897-5908/app  D/OCSC: New State: 2
05-31 11:17:08.341 5897-6100/app  D/OCSC: Status: 0
05-31 11:17:08.341 5897-6100/app  D/OCSC: New State: 2
05-31 11:17:38.621 5897-6100/app  D/OCSC: Status: 0
05-31 11:17:38.621 5897-6100/app  D/OCSC: New State: 2
05-31 11:18:08.431 5897-6058/app  D/OCSC: Status: 0
05-31 11:18:08.431 5897-6058/app  D/OCSC: New State: 2
05-31 11:18:38.011 5897-6123/app  D/OCSC: Status: 0
05-31 11:18:38.011 5897-6123/app  D/OCSC: New State: 2
05-31 11:19:13.451 5897-6123/app  D/OCSC: Status: 133
05-31 11:19:13.451 5897-6123/app  D/OCSC: New State: 0

When the disconnect problem occurs, I've seen that always is shown the new state as 22 or 133 (only one time I've seen new state as 19).

D/OCSC: New State: 133
D/OCSC: Status: 0
D/OCSC: New State: 133
D/OCSC: Status: 0
D/OCSC: New State: 133
D/OCSC: Status: 0

D/OCSC: New State: 22
D/OCSC: Status: 0
D/OCSC: New State: 22
D/OCSC: Status: 0
D/OCSC: New State: 22
D/OCSC: Status: 0

Error that I've found on btsnoop_hci.log:

Rcvd Error Response - Attribute Not Found, Handle: 0x0005, Handle: 0x0005 (Generic Access Profile: Appearance)

Sent Read By Type Request, GATT Include Declaration, Handles: 0x0006..0x0008

Rcvd Error Response - Attribute Not Found, Handle: 0x0006, Handle: 0x0006 (Generic Attribute Profile)

....

Rcvd Error Response - Attribute Not Found, Handle: 0x0008, Handle: 0x0008 (Generic Attribute Profile: Service Changed)

....

Rcvd Error Response - Attribute Not Found, Handle: 0x0102, Handle: 0x0102 (Unknown: Unknown)

....

Rcvd Error Response - Invalid Attribute Value Length, Handle: 0x0102, Handle: 0x0102 (Unknown: Unknown)

Logcat (test 1):

BluetoothGatt: onClientConnectionState() - status=0 clientIf=10
OCSC    : Status: 0
OCSC    : New State: 2
OCSC    : Connected to GATT server.
BluetoothGatt: discoverServices()
BluetoothLeService: Attempting to start service discovery:true
test    : com.example.bluetooth.le.ACTION_GATT_CONNECTED
BluetoothGatt: onClientConnectionState() - status=22 clientIf=8 
BluetoothGatt: onClientConnectionState() - status=22 clientIf=10
BtGatt.GattService: onDisconnected() - clientIf=12
luetoothGatt: onClientConnectionState() - status=22 clientIf=11
BluetoothGatt: onClientConnectionState() - status=22 clientIf=12
OCSC    : Status: 22
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
OCSC    : Status: 22
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
OCSC    : Status: 22
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
OCSC    : Status: 22
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
BluetoothGatt: close()
BluetoothGatt: close()
BluetoothGatt: close()
BluetoothGatt: close()

Logcat (test 2):

BluetoothGatt: onClientConnectionState() - status=133 clientIf=7
OCSC    : Status: 133
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
BluetoothGatt: close()
BluetoothGatt: onClientConnectionState() - status=133 clientIf=7
OCSC    : Status: 133
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
BluetoothGatt: close()
BluetoothGatt: onClientConnectionState() - status=22 clientIf=12
OCSC    : Status: 22
OCSC    : New State: 0
OCSC    : Disconnected from GATT server.
BluetoothGatt: close()

Any ideas?

adlagar
  • 877
  • 10
  • 31
  • 1
    How do you connect to the device in the first place? – Emil May 29 '17 at 12:39
  • 1
    Updated with the code. With the epoch variables i'm making a delay between each connection. – adlagar May 29 '17 at 15:46
  • 1
    That's not how you connect but how you bind to a service... I'm looking after the connectGatt call and the interaction with the BluetoothGatt object. – Emil May 29 '17 at 16:30
  • 1
    Updated Emil. Thanks for your help! If you want to see more code, ask me... I'm going crazy :( – adlagar May 29 '17 at 17:19

1 Answers1

0

I suspect you simply don't clean up the BluetoothGatt objects when you are done with them. After disconnecting and if you are not interested in connect to the device anymore using that BluetoothGatt object, you must call .close() on the object in order to free the resources allocated by the object. The maximum BluetoothGatt objects for all apps together is 32 on the latest Android versions.

It seems like you in your code overwrite the variable mBluetoothGatt. That way you loose the reference and can't call close anymore... Be sure to always close it before the reference is removed.

Emil
  • 16,784
  • 2
  • 41
  • 52
  • I've updated the code one more time. I'm calling disconnect() and close() inside the onCharacteristicWrite() callback. – adlagar May 30 '17 at 06:52
  • But then this code doesn't work, since the object is closed: 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; } } – Emil May 30 '17 at 07:26
  • But if is closed, then I call to connectGatt method: mBluetoothGatt = device.connectGatt(this, false, mGattCallback); Log.d(TAG, "Trying to create a new connection."); mBluetoothDeviceAddress = address; mConnectionState = STATE_CONNECTING; – adlagar May 30 '17 at 07:39
  • What I can see, you don't set mBluetoothGatt to null. Therefore the expression (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) still yields true, and therefore mBluetoothGatt.connect() is still called (on a closed object)? – Emil May 30 '17 at 09:19
  • I've edited the post with my close() function. Inside of that function, I set mBluetoothGatt to null. Thanks so much for your help. – adlagar May 30 '17 at 11:14
  • I see... Could you print to logcat the "status" and "newState" parameters in onCharacteristicChange? Then post the logcat output from your app when it fails. – Emil May 30 '17 at 11:47
  • Hmm.. Does the android system print anything to the logcat? (if you remove the filter to only show the app's output). Also, it would also be nice to see a btsnoop_hci.log file. – Emil May 31 '17 at 11:58
  • Updated with more date. I've the log, but if I open the file, it is not readable. Appears characters like this: â.˜Bò]'Wý. How can I read this log correctly? – adlagar Jun 01 '17 at 10:53
  • You use Wireshark. – Emil Jun 01 '17 at 10:57
  • I just noticed that if you don't receive the onCharacteristicWrite callback, close() is never called. This will happen if the connection drops unexpectedly before you write to the characteristic. You should post the full logcat log also, which should include some info from the system why the BT connection failed. – Emil Jun 01 '17 at 11:01
  • You're right! I've tested the app and seems that when the problem occurs, close() is never called (at least for now). I update the post with some errors that I've found on the BT log and I think that they could be important. – adlagar Jun 01 '17 at 11:30
  • Try to move your close() to your onConnectionStateChange instead (under disconnected). – Emil Jun 01 '17 at 11:52
  • It is there too. I've 2 "close()" calls since yesterday: one on the "onConnectionStateChange" and the second on the "onWriteCharacteristic" method. – adlagar Jun 01 '17 at 12:34
  • . You should post the full logcat log also, which should include some info from the system why the BT connection failed. – Emil Jun 01 '17 at 13:01
  • I've updated with the logcat, but seems that not appears any interesting line with new information... :( – adlagar Jun 01 '17 at 13:41