newnode icon indicating copy to clipboard operation
newnode copied to clipboard

restart BTLE after backgrounding

Open IvanSeleznevSpb opened this issue 2 years ago • 2 comments

Internal/NewNode has a startNearby() method that calls Bluetooth.bluetoothOn() -> Bluetooth.startServer() -> Bluetooth.tryStartServer() -> BluetoothManager.openGattServer(). https://github.com/clostra/newnode/blob/44f48fe824592987d2025a019b2c1e3dcd4a5693/android/src/main/java/com/clostra/newnode/internal/Bluetooth.java#L265 The startNearby() method is called in onActivityResumed() which causes a new BluetoothGattServer to be created each time a new activity is opened. https://github.com/clostra/newnode/blob/44f48fe824592987d2025a019b2c1e3dcd4a5693/android/src/main/java/com/clostra/newnode/internal/NewNode.java#L623 It works fine a few times (usually 1 or 3 times), and then openGattServer() starts returning null, which results in the exception described above. Before returning null, the openGattServer() method runs for a long time and does it on the main thread, which causes the interface to freeze.

Bluetooth                           com.newnode.messenger      E  startServer
                                                                  java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.bluetooth.BluetoothGattServer.addService(android.bluetooth.BluetoothGattService)' on a null object reference
                                                                  	at com.clostra.newnode.internal.Bluetooth.tryStartServer(Bluetooth.java:280)
                                                                  	at com.clostra.newnode.internal.Bluetooth.startServer(Bluetooth.java:288)
                                                                  	at com.clostra.newnode.internal.Bluetooth.bluetoothOn(Bluetooth.java:142)
                                                                  	at com.clostra.newnode.internal.NewNode.startNearby(NewNode.java:590)
                                                                  	at com.clostra.newnode.internal.NewNode.onActivityResumed(NewNode.java:622)
                                                                  	at android.app.Application.dispatchActivityResumed(Application.java:450)
                                                                  	at android.app.Activity.dispatchActivityResumed(Activity.java:1482)
                                                                  	at android.app.Activity.onResume(Activity.java:2043)
                                                                  	at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:455)
                                                                  	at com.clostra.nnm.BaseActivity.onResume(BaseActivity.java:46)
                                                                  	at com.clostra.nnm.PassphraseRequiredActivity.onResume(PassphraseRequiredActivity.java:78)
                                                                  	at com.clostra.nnm.conversation.ConversationActivity.onResume(ConversationActivity.java:508)
                                                                  	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1531)
                                                                  	at android.app.Activity.performResume(Activity.java:8734)
                                                                  	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:5351)
                                                                  	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:5444)
                                                                  	at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
                                                                  	at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
                                                                  	at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
                                                                  	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
                                                                  	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2574)
                                                                  	at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                  	at android.os.Looper.loopOnce(Looper.java:226)
                                                                  	at android.os.Looper.loop(Looper.java:313)
                                                                  	at android.app.ActivityThread.main(ActivityThread.java:8757)
                                                                  	at java.lang.reflect.Method.invoke(Native Method)
                                                                  	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
                                                                  	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

IvanSeleznevSpb avatar Aug 14 '23 09:08 IvanSeleznevSpb

@ghazel, It looks like GattServer should be run from the main thread. Therefore, the only thing that can be done is to check before starting that server is not running. Something like this:

if (gattServer != null) {
    return;
}

// run openGattServer

IvanSeleznevSpb avatar Aug 16 '23 14:08 IvanSeleznevSpb

More research is needed here. L2cap channels are also exhausted by recreating advertise/scan objects every time. In particular listenUsingInsecureL2capChannel().

The question is what, if anything, needs to be restarted or recreated after the app has been backgrounded for long enough that BTLE advertise/scan has stopped.

ghazel avatar Aug 16 '23 16:08 ghazel