don / cordova-plugin-ble-central

Bluetooth Low Energy (BLE) Central plugin for Apache Cordova (aka PhoneGap)
Apache License 2.0
942 stars 603 forks source link

Unhandeled Exception on disconnect #473

Open fmgomes opened 6 years ago

fmgomes commented 6 years ago

I am using this plugin in one Android project, and have been asked to add a disconnect button, so I'm now invoking the plugin disconnect method when this additional button is pressed. I'm observing an unhandled exception that I cannot justify, do you think it is something I'm doing wrong?

code: bleDisconnect: function() { ble.disconnect(db.BLE, app.onBLEDisconnectOK, app.onBLEDisconnectERR); },

onBLEDisconnectOK: function() {
    console.log('BLE Disconnect OK');
},

onBLEDisconnectERR: function() {
    console.log('BLE Disconnect ERR');
},

log: 10-09 22:21:50.509 14283-14651/io.cordova.courteous D/BLEPlugin: action = disconnect 10-09 22:21:50.509 14283-14651/io.cordova.courteous D/BluetoothGatt: cancelOpen() - device: 00:A0:50:08:8B:56 10-09 22:21:50.516 14283-14651/io.cordova.courteous D/BluetoothGatt: close() 10-09 22:21:50.516 14283-14614/io.cordova.courteous D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=6 device=00:A0:50:08:8B:56 10-09 22:21:50.516 14283-14651/io.cordova.courteous D/BluetoothGatt: unregisterApp() - mClientIf=6 10-09 22:21:50.521 14283-14614/io.cordova.courteous W/BluetoothGatt: Unhandled exception in callback java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:227) at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70) at android.os.Binder.execTransact(Binder.java:573) 10-09 22:21:50.528 14283-14283/io.cordova.courteous D/SystemWebChromeClient: file:///android_asset/www/js/index.js: Line 716 : BLE Disconnect OK 10-09 22:21:50.528 14283-14283/io.cordova.courteous I/chromium: [INFO:CONSOLE(716)] "BLE Disconnect OK", source: file:///android_asset/www/js/index.js (716)

Thanks in advance!

Fernando

fmgomes commented 6 years ago

Some additional information, the mobile phone where I'm testing is a Samsung A5 (2017) running Android 7.0. I've upgraded the plugin to the latest release (cordova plugin remove, cordova plugin add) but the problem remains

ddugue commented 6 years ago

This is linked to the fact that the disconnect function automatically calls the success callback and never waits for the OS to send a disconnection OK signal to the plugin. Thus, it closes the GATT handler before receiving the disconnection event.

The reason why is that sometimes, android never receives the disconnect event (in the onClientConnectionState), if you would be waiting for it in your application, it would simply hangs.

I would try adding a flag in the disconnection to force wait for onClientConnectionState with a default value of False to maintain backward compatibility and submit a pull request.

fmgomes commented 6 years ago

Hi @ddugue, thank you very much for the explanation! As I have no experience developing Cordova plugins, could you point me any resource that can help at least to identify where to make the change, compile and test before submitting a pull request? I think that I could edit the plugin source on the cordova project and build it again (I made that in the past with another plugin), but I'm not sure what source it was (they appeared repeated in the project) and if a normal build is enough.

Fernando

ddugue commented 6 years ago

Right now, testing is done manually. The only unittests are javascript for the plugin interface. Not really useful. What I do, is create a fork and make my app depends on that fork and test with that.

Start by looking at https://github.com/don/cordova-plugin-ble-central/blob/master/src/android/BLECentralPlugin.java#L349 and https://github.com/don/cordova-plugin-ble-central/blob/master/src/android/Peripheral.java#L82

If you get around making a working fork, link me to it and I'll gladly check it out!

don commented 6 years ago

I think this is a duplicate of #412 (and others)

This seems to be a problem on some devices. The documentation around gatt.disconnect and gatt.close is not good. Is also appears to be have different implementations depending on the device manufacturer.

I was working on some alternatives implementations, but it caused other problems.

jedthehumanoid commented 6 years ago

We are facing this issue on a pretty high percentage of our disconnects. Since our use case is centered around connection to different peripherals with disconnection in between, this exception essentially silently destroys our ability to connect again.

I think @ddugue explains the situation pretty well

We have done a variant of the solution mentioned by @acutetech here

There seems to be no consensus on how to solve this properly, since above fix might break other devices. Our solution is to wait for onConnectionStateChanged with a timeout, if it has not fired within timeout, we do gatt.close() anyway (as is done now)

In my opinion this fixes the exception for those affected, and does not lead to a regression for those not affected (since fallback is existing behaviour).

jedthehumanoid commented 6 years ago

Changes made on our side:

https://github.com/don/cordova-plugin-ble-central/compare/master...jedthehumanoid:master

Would a pull request be appreciated?

Domvel commented 5 years ago

Same here. Sometimes on a disconnect the peripheral still connected. See logcat:

BluetoothGatt: Unhandled exception in callback
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:187)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:458)
don commented 5 years ago

I'll get a version of @acutetech and @jedthehumanoid's fix into the next release to see if that makes things better. Using a timer or handler, I could call the success callback of close when the connection actually closed, or the timer expired.

@jedthehumanoid 5 seconds is a long time to wait, does it usually take that long?

jedthehumanoid commented 5 years ago

Yeah, 5 seconds is probably a little long, I think you either get onConnectionStateChange right away or never.

Anyways, it's just a safeguard for the edge case that it never fires the event, which we have not experienced.

Domvel commented 5 years ago

Just another console output. No need for a reaction.

BluetoothGatt: Unhandled exception in callback
java.lang.NullPointerException: Attempt to invoke virtual method 
'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' 
on a null object reference
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:191)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:565)
peitschie commented 2 years ago

Some fixes were landed as part of https://github.com/don/cordova-plugin-ble-central/commit/ee7cb0dbeff237af37faeee97c50bfd2eb430497

Is this still an ongoing issue being experienced by anyone?

BetterAutomations commented 2 years ago

Some fixes were landed as part of ee7cb0d

Is this still an ongoing issue being experienced by anyone?

I'm still seeing this on version 1.4.1.

2021-12-31 12:13:38.392 com.securecoop.app W/BluetoothGatt: Unhandled exception in callback
    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference
        at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:227)
        at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
        at android.os.Binder.execTransact(Binder.java:573)
peitschie commented 2 years ago

It actually appears after some research that this is simply a case of bad logging from the Android BluetoothGatt subsystem. See https://issuetracker.google.com/issues/37057260

It looks like the log message saying "Unhandled exception" doesn't mean it's actually an unhandled exception.

I don't think there's anything incorrect with the plugin here... this seems to be an OS issue on Android.

Note, the thing that is null is the BluetoothGattCallback the Bluetooth stack is attempting to call, NOT a method inside that callback (which is where the plugin code starts).

peitschie commented 2 years ago

There's some suggested workarounds on the issue tracker, but none of these look terribly simply to get 100% correct.

I'm happy to accept patches trying to resolve this however if they are straightforward enough... but for now, apart from the undesirable noise in the logs, I'm not clear whether this log results in any harm.