Reputation: 1
We are using a ble device that has a noticeable characteristic, and we use the EnableNotification to get data through the callback. We implemented a queue of commands, and we reach a state in which we do not issue any more commands, we instead only listen to the characteristic notification.
After a random amount of time, we encounter an error in which the bluetooth service/adapter dies, in the logcat we find a DeadObjectException. It's an error that is very hard to replicate and track, but it's critical to our app. In the callback we try to handle all possible exceptions (in each one of the class methods) but the DeadObjectException cannot be handled, it seems it is thrown in another process.
The device we are using sends 2 bytes of data every time it is triggered, and can send about 20 packages per second.
We had not any luck finding help about this. Can anybody give us some advice? Is there any way to capture exceptions thrown by the bluetooth binder or the service? Which is the correct way of handling noticeable characteristics?
This is the logcat output:
2022-02-22 13:16:50.125 5423-5910/? I/bt_stack: [INFO:gatt_main.cc(919)] gatt_data_process op_code = 27, msg_len = 4
2022-02-22 13:16:50.125 5423-5910/? E/bt_btif: bta_gattc_process_indicate, ignore HID ind/notificiation
2022-02-22 13:16:50.125 5423-5541/? E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 152)
2022-02-22 13:16:50.126 5423-5541/? E/BtGatt.JNI: An exception was thrown by callback 'btgattc_notify_cb'.
2022-02-22 13:16:50.143 5423-5910/? I/bt_stack: [INFO:gatt_main.cc(919)] gatt_data_process op_code = 27, msg_len = 4
2022-02-22 13:16:50.143 5423-5910/? E/bt_btif: bta_gattc_process_indicate, ignore HID ind/notificiation
2022-02-22 13:16:50.171 17518-8879/---.---.--- I/SMNPlugin: Android [Main.cpp:763:_setStatePieceDetected()] [Piece State Detection] PIECE_NO_CORRECT.
2022-02-22 13:16:50.180 5423-5541/? E/BtGatt.JNI: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:550)
at android.bluetooth.IBluetoothGattCallback$Stub$Proxy.onNotify(IBluetoothGattCallback.java:561)
at com.android.bluetooth.gatt.GattService.onNotify(GattService.java:1530)
2022-02-22 13:16:50.180 5423-5541/? E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 152)
2022-02-22 13:16:50.180 5423-5541/? E/BtGatt.JNI: An exception was thrown by callback 'btgattc_notify_cb'.
2022-02-22 13:16:50.181 5423-5541/? E/BtGatt.JNI: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:550)
at android.bluetooth.IBluetoothGattCallback$Stub$Proxy.onNotify(IBluetoothGattCallback.java:561)
at com.android.bluetooth.gatt.GattService.onNotify(GattService.java:1530)
This is an example of our callback:
public BluetoothDeviceInterface(final BluetoothDevice device, final int arrayIndex) {
_device = device;
_internalArrayIndex = arrayIndex;
_gatt = null;
_gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.i(LOGTAG, "On Connection State Change. Status: " + status + ". New state: " + newState);
try {
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(LOGTAG, "Connected to device: " + GetAddress());
_gatt = gatt;
_gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(LOGTAG, "Disconnected from device: " + GetAddress());
_gatt.close();
_gatt = null;
}
} else {
Log.i(LOGTAG, "Error connecting GATT.");
_gatt.close();
_gatt = null;
}
_callback.OnStateChanged(status, newState);
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onConnectionStateChange: " + e.toString());
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i(LOGTAG, "On Services Discovered. Status: " + status);
try {
Log.i(LOGTAG, "Services of device " + GetAddress() + " discovered. Total number of: " + gatt.getServices().size());
_callback.CleanServices();
for(BluetoothGattService service : gatt.getServices()) {
String serviceUUID = service.getUuid().toString();
_callback.AddNewService(service.getType(), serviceUUID);
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
String characteristicUUID = characteristic.getUuid().toString();
int characteristicProperties = characteristic.getProperties();
boolean isReadable = (characteristicProperties & BluetoothGattCharacteristic.PROPERTY_READ) != 0;
boolean isWritable = (characteristicProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0;
boolean isWritableWithoutResponse = (characteristicProperties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0;
boolean isIndicateable = (characteristicProperties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0;
boolean isNotifiable = (characteristicProperties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0;
_callback.AddNewCharacteristic(serviceUUID, characteristicUUID, isReadable, isWritable,
isWritableWithoutResponse, isIndicateable, isNotifiable);
}
}
_callback.OnServiceDiscoveryEnd();
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onServicesDiscovered: " + e.toString());
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.i(LOGTAG, "On Characteristic Read. Status: " + status);
try {
if (status == BluetoothGatt.GATT_SUCCESS) {
_callback.OnCharacteristicRead(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(), characteristic.getValue());
} else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
_callback.OnCharacteristicReadError(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(), "Read not permitted.");
} else {
_callback.OnCharacteristicReadError(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(), "Status error: " + status);
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onCharacteristicRead: " + e.toString());
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.i(LOGTAG, "On Characteristic Write. Status: " + status);
try {
if (status == BluetoothGatt.GATT_SUCCESS) {
_callback.OnCharacteristicWrite(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString());
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
_callback.OnCharacteristicWriteError(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(), "Write not permitted.");
} else {
_callback.OnCharacteristicWriteError(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(), "Status error: " + status);
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onCharacteristicWrite: " + e.toString());
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
Log.i(LOGTAG, "On Descriptor Write. Status: " + status);
try {
if (status == BluetoothGatt.GATT_SUCCESS) {
_callback.OnDescriptorWrite(descriptor.getCharacteristic().getService().getUuid().toString(), descriptor.getCharacteristic().getUuid().toString());
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
_callback.OnDescriptorWriteError(descriptor.getCharacteristic().getService().getUuid().toString(), descriptor.getCharacteristic().getUuid().toString(), "Write not permitted.");
} else {
_callback.OnDescriptorWriteError(descriptor.getCharacteristic().getService().getUuid().toString(), descriptor.getCharacteristic().getUuid().toString(), "Status error: " + status);
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onDescriptorWrite: " + e.toString());
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.i(LOGTAG, "On Characteristic Changed.");
try {
Log.i(LOGTAG, characteristic.getService().getUuid().toString() + characteristic.getUuid().toString() + characteristic.getValue().toString());
_callback.OnCharacteristicChange(characteristic.getService().getUuid().toString(), characteristic.getUuid().toString(), characteristic.getValue());
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onCharacteristicChanged: " + e.toString());
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
Log.i(LOGTAG, "On Read Remote Rssi. Status: " + status + ". Rssi: " + rssi);
try {
if (status == BluetoothGatt.GATT_SUCCESS) {
_callback.OnRssiRead(rssi);
} else {
_callback.OnRssiRead(-1);
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception in onReadRemoteRssi: " + e.toString());
}
}
};
With an empty callback it still fails:
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.i(LOGTAG, "On Characteristic Changed: " + characteristic.getStringValue(0));
}
This is the code we use for enabling the notifications:
public boolean EnableNotifications(String serviceUUID, String characteristicUUID) {
if (!IsConnected())
return false;
BluetoothGattService service = _gatt.getService(UUID.fromString(serviceUUID));
if (service == null)
return false;
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(characteristicUUID));
if (characteristic == null)
return false;
int properties = characteristic.getProperties();
byte[] payload;
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
payload = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
payload = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
} else {
return false;
}
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID);
if (descriptor == null)
return false;
if (!_gatt.setCharacteristicNotification(characteristic, true))
return false;
descriptor.setValue(payload);
return _gatt.writeDescriptor(descriptor);
}
Upvotes: 0
Views: 759