From 8d6af244887c2890807925e1b4ef25c6acbb1d94 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 17 Jul 2020 14:12:46 -0700 Subject: [PATCH 1/6] amazon fire requires a longer delay for the nasty BLE force-refresh hack --- app/build.gradle | 4 ++-- .../java/com/geeksville/mesh/service/BluetoothInterface.kt | 6 +++++- geeksville-androidlib | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b74bfe28d..247450e02 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "com.geeksville.mesh" minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works) targetSdkVersion 29 - versionCode 10795 // format is Mmmss (where M is 1+the numeric major number - versionName "0.7.95" + versionCode 10796 // format is Mmmss (where M is 1+the numeric major number + versionName "0.7.96" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt b/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt index b78f42af0..8cdb4d2d8 100644 --- a/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt +++ b/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt @@ -193,6 +193,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String // NRF52 targets do not need the nasty force refresh hack that ESP32 needs (because they keep their // BLE handles stable. So turn the hack off for these devices. FIXME - find a better way to know that the board is NRF52 based + // and Amazon fire devices seem to not need this hack either + // Build.MANUFACTURER != "Amazon" && private var needForceRefresh = !address.startsWith("FD:10:04") init { @@ -415,7 +417,9 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String if (needForceRefresh) { // Our ESP32 code doesn't properly generate "service changed" indications. Therefore we need to force a refresh on initial start //needForceRefresh = false // In fact, because of tearing down BLE in sleep on the ESP32, our handle # assignments are not stable across sleep - so we much refetch every time forceServiceRefresh() // this article says android should not be caching, but it does on some phones: https://punchthrough.com/attribute-caching-in-ble-advantages-and-pitfalls/ - delay(200) // From looking at the android C code it seems that we need to give some time for the refresh message to reach that worked _before_ we try to set mtu/get services + + delay(500) // From looking at the android C code it seems that we need to give some time for the refresh message to reach that worked _before_ we try to set mtu/get services + // 200ms was not enough on an Amazon Fire } // we begin by setting our MTU size as high as it can go (if we can) diff --git a/geeksville-androidlib b/geeksville-androidlib index cfe31d66e..bdb2685ae 160000 --- a/geeksville-androidlib +++ b/geeksville-androidlib @@ -1 +1 @@ -Subproject commit cfe31d66e4de324fa91a2978a76ffcfba5e01085 +Subproject commit bdb2685aefd04791d62f018e9299173ec3002a6c From 7a47240e47255cbdf3d04bdf54e75446a92695a0 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 18 Jul 2020 13:17:30 -0700 Subject: [PATCH 2/6] fix autobug: someone is testing crashing the app with invalid URLS ;-) --- .../java/com/geeksville/mesh/MainActivity.kt | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index dbc81456b..d17f7ddb7 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -48,6 +48,7 @@ import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.tasks.Task import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.tabs.TabLayoutMediator +import com.google.protobuf.InvalidProtocolBufferException import com.vorlonsoft.android.rate.AppRate import com.vorlonsoft.android.rate.StoreType import kotlinx.android.synthetic.main.activity_main.* @@ -629,29 +630,37 @@ class MainActivity : AppCompatActivity(), Logging, private fun perhapsChangeChannel() { // If the is opening a channel URL, handle it now requestedChannelUrl?.let { url -> - val channel = Channel(url) - requestedChannelUrl = null + try { + val channel = Channel(url) + requestedChannelUrl = null - MaterialAlertDialogBuilder(this) - .setTitle(R.string.new_channel_rcvd) - .setMessage(getString(R.string.do_you_want_switch).format(channel.name)) - .setNeutralButton(R.string.cancel) { _, _ -> - // Do nothing - } - .setPositiveButton(R.string.accept) { _, _ -> - debug("Setting channel from URL") - try { - model.setChannel(channel.settings) - } catch (ex: RemoteException) { - errormsg("Couldn't change channel ${ex.message}") - Toast.makeText( - this, - "Couldn't change channel, because radio is not yet connected. Please try again.", - Toast.LENGTH_SHORT - ).show() + MaterialAlertDialogBuilder(this) + .setTitle(R.string.new_channel_rcvd) + .setMessage(getString(R.string.do_you_want_switch).format(channel.name)) + .setNeutralButton(R.string.cancel) { _, _ -> + // Do nothing } - } - .show() + .setPositiveButton(R.string.accept) { _, _ -> + debug("Setting channel from URL") + try { + model.setChannel(channel.settings) + } catch (ex: RemoteException) { + errormsg("Couldn't change channel ${ex.message}") + Toast.makeText( + this, + "Couldn't change channel, because radio is not yet connected. Please try again.", + Toast.LENGTH_SHORT + ).show() + } + } + .show() + } catch (ex: InvalidProtocolBufferException) { + Toast.makeText( + this, + R.string.channel_invalid, + Toast.LENGTH_LONG + ).show() + } } } From 25ceaa63646842d3c8a9b1ff1c93956e5b546643 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 18 Jul 2020 13:18:38 -0700 Subject: [PATCH 3/6] Fix autobugs related to Samsung BLE --- .../geeksville/mesh/service/SafeBluetooth.kt | 49 ++++++++++--------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt index 4306ea3a8..f3330844d 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt @@ -609,7 +609,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD private fun queueDiscoverServices(cont: Continuation) { queueWork("discover", cont) { - gatt!!.discoverServices() + gatt?.discoverServices() ?: throw BLEException("GATT is null") } } @@ -742,6 +742,10 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD null // clear gat before calling close, bcause close might throw dead object exception g2.close() } + } catch (ex: NullPointerException) { + // Attempt to invoke virtual method 'com.android.bluetooth.gatt.AdvertiseClient com.android.bluetooth.gatt.AdvertiseManager.getAdvertiseClient(int)' on a null object reference + //com.geeksville.mesh.service.SafeBluetooth.closeGatt + warn("Ignoring NPE in close - probably buggy Samsung BLE") } catch (ex: DeadObjectException) { warn("Ignoring dead object exception, probably bluetooth was just disabled") } finally { @@ -789,28 +793,28 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD gatt!!.setCharacteristicNotification(c, enable) /* - c is null sometimes + c is null sometimes 2020-04-13 15:59:38.222 2111-2182/com.geeksville.mesh D/BluetoothGatt: setCharacteristicNotification() - uuid: ed9da18c-a800-4f66-a670-aa7547e34453 enable: true 2020-04-13 15:59:38.225 2111-2182/com.geeksville.mesh E/com.geeksville.util.Exceptions: exceptionReporter Uncaught Exception - kotlin.KotlinNullPointerException - at com.geeksville.mesh.service.SafeBluetooth.setNotify(SafeBluetooth.kt:505) - at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:328) - at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:90) - at com.geeksville.concurrent.CallbackContinuation.resume(SyncContinuation.kt:20) - at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:329) - at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:33) - at com.geeksville.util.ExceptionsKt.exceptionReporter(Exceptions.kt:34) - at com.geeksville.mesh.service.SafeBluetooth.completeWork(SafeBluetooth.kt:312) - at com.geeksville.mesh.service.SafeBluetooth.access$completeWork(SafeBluetooth.kt:33) - at com.geeksville.mesh.service.SafeBluetooth$gattCallback$1.onMtuChanged(SafeBluetooth.kt:221) - at android.bluetooth.BluetoothGatt$1$13.run(BluetoothGatt.java:658) - at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:780) - at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:41) - at android.bluetooth.BluetoothGatt$1.onConfigureMTU(BluetoothGatt.java:653) - at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:330) - at android.os.Binder.execTransactInternal(Binder.java:1021) - at android.os.Binder.execTransact(Binder.java:994) - */ +kotlin.KotlinNullPointerException + at com.geeksville.mesh.service.SafeBluetooth.setNotify(SafeBluetooth.kt:505) + at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:328) + at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:90) + at com.geeksville.concurrent.CallbackContinuation.resume(SyncContinuation.kt:20) + at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:329) + at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:33) + at com.geeksville.util.ExceptionsKt.exceptionReporter(Exceptions.kt:34) + at com.geeksville.mesh.service.SafeBluetooth.completeWork(SafeBluetooth.kt:312) + at com.geeksville.mesh.service.SafeBluetooth.access$completeWork(SafeBluetooth.kt:33) + at com.geeksville.mesh.service.SafeBluetooth$gattCallback$1.onMtuChanged(SafeBluetooth.kt:221) + at android.bluetooth.BluetoothGatt$1$13.run(BluetoothGatt.java:658) + at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:780) + at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:41) + at android.bluetooth.BluetoothGatt$1.onConfigureMTU(BluetoothGatt.java:653) + at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:330) + at android.os.Binder.execTransactInternal(Binder.java:1021) + at android.os.Binder.execTransact(Binder.java:994) + */ // per https://stackoverflow.com/questions/27068673/subscribe-to-a-ble-gatt-notification-android val descriptor: BluetoothGattDescriptor = c.getDescriptor(configurationDescriptorUUID) ?: throw BLEException("Notify descriptor not found for ${c.uuid}") // This can happen on buggy BLE implementations @@ -820,5 +824,4 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD debug("Notify enable=$enable completed") } } -} - +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1763a2332..5aeaa8f33 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -73,4 +73,5 @@ About A list of nodes in the mesh Text messages + This Channel URL is invalid and can not be used From 7b46046af6823cbde53779ac911838f2ea8a4677 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 18 Jul 2020 14:23:58 -0700 Subject: [PATCH 4/6] cancel all jobs when we shutdown interface (an autobug) --- .../geeksville/mesh/service/BluetoothInterface.kt | 5 +++-- .../mesh/service/RadioInterfaceService.kt | 13 ++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt b/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt index 8cdb4d2d8..3be7ecf51 100644 --- a/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt +++ b/app/src/main/java/com/geeksville/mesh/service/BluetoothInterface.kt @@ -353,6 +353,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String } } catch (ex: CancellationException) { warn("retryDueToException was cancelled") + } finally { + reconnectJob = null } /// We only try to set MTU once, because some buggy implementations fail @@ -417,7 +419,7 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String if (needForceRefresh) { // Our ESP32 code doesn't properly generate "service changed" indications. Therefore we need to force a refresh on initial start //needForceRefresh = false // In fact, because of tearing down BLE in sleep on the ESP32, our handle # assignments are not stable across sleep - so we much refetch every time forceServiceRefresh() // this article says android should not be caching, but it does on some phones: https://punchthrough.com/attribute-caching-in-ble-advantages-and-pitfalls/ - + delay(500) // From looking at the android C code it seems that we need to give some time for the refresh message to reach that worked _before_ we try to set mtu/get services // 200ms was not enough on an Amazon Fire } @@ -447,7 +449,6 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String override fun close() { reconnectJob?.cancel() // Cancel any queued reconnect attempts - reconnectJob = null if (safe != null) { info("Closing BluetoothInterface") diff --git a/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt index acf156a17..7fd73e7c9 100644 --- a/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt @@ -18,6 +18,7 @@ import com.geeksville.util.toRemoteExceptions import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel class RadioNotConnectedException(message: String = "Not connected to radio") : @@ -121,8 +122,10 @@ class RadioInterfaceService : Service(), Logging { private lateinit var sentPacketsLog: BinaryLogFile // inited in onCreate private lateinit var receivedPacketsLog: BinaryLogFile - private val serviceJob = Job() - val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) + /** + * We recreate this scope each time we stop an interface + */ + var serviceScope = CoroutineScope(Dispatchers.IO + Job()) private val nopIf = NopInterface() private var radioIf: IRadioInterface = nopIf @@ -198,7 +201,7 @@ class RadioInterfaceService : Service(), Logging { override fun onDestroy() { unregisterReceiver(bluetoothStateReceiver) stopInterface() - serviceJob.cancel() + serviceScope.cancel("Destroying RadioInterface") runningService = null super.onDestroy() } @@ -248,6 +251,10 @@ class RadioInterfaceService : Service(), Logging { radioIf = nopIf r.close() + // cancel any old jobs and get ready for the new ones + serviceScope.cancel("stopping interface") + serviceScope = CoroutineScope(Dispatchers.IO + Job()) + if (logSends) sentPacketsLog.close() if (logReceives) From 6b0e19ea9af535dc4c1f6ae50b8aafe6c44d87f3 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 18 Jul 2020 14:24:06 -0700 Subject: [PATCH 5/6] remove comment --- .../geeksville/mesh/service/SafeBluetooth.kt | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt index f3330844d..39e659e66 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt @@ -791,30 +791,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD notifyHandlers[c.uuid] = onChanged // c.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT gatt!!.setCharacteristicNotification(c, enable) - - /* - c is null sometimes -2020-04-13 15:59:38.222 2111-2182/com.geeksville.mesh D/BluetoothGatt: setCharacteristicNotification() - uuid: ed9da18c-a800-4f66-a670-aa7547e34453 enable: true -2020-04-13 15:59:38.225 2111-2182/com.geeksville.mesh E/com.geeksville.util.Exceptions: exceptionReporter Uncaught Exception -kotlin.KotlinNullPointerException - at com.geeksville.mesh.service.SafeBluetooth.setNotify(SafeBluetooth.kt:505) - at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:328) - at com.geeksville.mesh.service.RadioInterfaceService$onConnect$1$1.invoke(RadioInterfaceService.kt:90) - at com.geeksville.concurrent.CallbackContinuation.resume(SyncContinuation.kt:20) - at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:329) - at com.geeksville.mesh.service.SafeBluetooth$completeWork$1.invoke(SafeBluetooth.kt:33) - at com.geeksville.util.ExceptionsKt.exceptionReporter(Exceptions.kt:34) - at com.geeksville.mesh.service.SafeBluetooth.completeWork(SafeBluetooth.kt:312) - at com.geeksville.mesh.service.SafeBluetooth.access$completeWork(SafeBluetooth.kt:33) - at com.geeksville.mesh.service.SafeBluetooth$gattCallback$1.onMtuChanged(SafeBluetooth.kt:221) - at android.bluetooth.BluetoothGatt$1$13.run(BluetoothGatt.java:658) - at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:780) - at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:41) - at android.bluetooth.BluetoothGatt$1.onConfigureMTU(BluetoothGatt.java:653) - at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:330) - at android.os.Binder.execTransactInternal(Binder.java:1021) - at android.os.Binder.execTransact(Binder.java:994) - */ + // per https://stackoverflow.com/questions/27068673/subscribe-to-a-ble-gatt-notification-android val descriptor: BluetoothGattDescriptor = c.getDescriptor(configurationDescriptorUUID) ?: throw BLEException("Notify descriptor not found for ${c.uuid}") // This can happen on buggy BLE implementations From f6b20ee3ea48636608d561fe86ae069c05609d92 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 18 Jul 2020 14:24:14 -0700 Subject: [PATCH 6/6] 0.8.1 --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 247450e02..fc55b5aae 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "com.geeksville.mesh" minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works) targetSdkVersion 29 - versionCode 10796 // format is Mmmss (where M is 1+the numeric major number - versionName "0.7.96" + versionCode 10801 // format is Mmmss (where M is 1+the numeric major number + versionName "0.8.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -128,7 +128,7 @@ dependencies { implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0' // mapbox specifies a really old version of okhttp3 which causes lots of API warnings. trying a newer version - implementation 'com.squareup.okhttp3:okhttp:4.7.2' + implementation 'com.squareup.okhttp3:okhttp:4.8.0' // location services implementation 'com.google.android.gms:play-services-location:17.0.0'