clostra/newnode

restart BTLE after backgrounding

IvanSeleznevSpb opened this issue · 2 comments

Internal/NewNode has a startNearby() method that calls Bluetooth.bluetoothOn() -> Bluetooth.startServer() -> Bluetooth.tryStartServer() -> BluetoothManager.openGattServer().

gattServer = manager.openGattServer(NewNode.app(), new BluetoothGattServerCallback() {

The startNearby() method is called in onActivityResumed() which causes a new BluetoothGattServer to be created each time a new activity is opened.

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)

@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
ghazel commented

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.