if phone ble stack is hosed restart it

1.2-legacy
geeksville 2020-02-25 09:28:47 -08:00
rodzic dac3610f7d
commit 2c8c7ac8d6
5 zmienionych plików z 75 dodań i 11 usunięć

Wyświetl plik

@ -80,7 +80,6 @@ rules at the BluetoothDevice level. Either make SafeBluetooth lock at the devic
* register app link for our URLs https://developer.android.com/studio/write/app-link-indexing.html
* let user change radio params and share radio join info via QR code or text message (use an encoded app specific URL - to autoprompt for app installation as needed)
* test with an oldish android release using real hardware
* if necessary restart entire BT adapter with this tip from Michael https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called
* stop using a foreground service
* change info() log strings to debug()
* use platform theme (dark or light)
@ -152,4 +151,5 @@ Don't leave device discoverable. Don't let unpaired users do things with device
* tell Compose geeks
* call crashlytics from exceptionReporter!!! currently not logging failures caught there
* do setOwner every time we connect to the radio, use our settings, radio should ignore if unchanged
* send location data for devices that don't have a GPS - https://developer.android.com/training/location/change-location-settings
* send location data for devices that don't have a GPS - https://developer.android.com/training/location/change-location-settings
* if necessary restart entire BT adapter with this tip from Michael https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called

Wyświetl plik

@ -16,6 +16,8 @@ import android.os.RemoteException
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_MIN
import com.geeksville.analytics.DataPair
import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.android.ServiceClient
import com.geeksville.mesh.*
@ -163,6 +165,8 @@ class MeshService : Service(), Logging {
@SuppressLint("MissingPermission")
private fun startLocationRequests() {
if (fusedLocationClient == null) {
GeeksvilleApplication.analytics.track("location_start") // Figure out how many users needed to use the phone GPS
val request = LocationRequest.create().apply {
interval =
5 * 60 * 1000 // FIXME, do more like once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
@ -209,6 +213,7 @@ class MeshService : Service(), Logging {
private fun stopLocationRequests() {
if (fusedLocationClient != null) {
debug("Stopping location requests")
GeeksvilleApplication.analytics.track("location_stop")
fusedLocationClient?.removeLocationUpdates(locationCallback)
fusedLocationClient = null
}
@ -231,7 +236,7 @@ class MeshService : Service(), Logging {
private fun broadcastNodeChange(info: NodeInfo) {
debug("Broadcasting node change $info")
val intent = Intent(ACTION_NODE_CHANGE)
intent.putExtra(EXTRA_NODEINFO, info)
explicitBroadcast(intent)
}
@ -388,6 +393,8 @@ class MeshService : Service(), Logging {
)
private val numNodes get() = nodeDBbyNodeNum.size
/**
* How many nodes are currently online (including our local node)
*/
@ -623,6 +630,12 @@ class MeshService : Service(), Logging {
// Do our startup init
try {
reinitFromRadio()
GeeksvilleApplication.analytics.track(
"mesh_connect",
DataPair("num_nodes", numNodes),
DataPair("num_online", numOnlineNodes)
)
} catch (ex: RemoteException) {
// It seems that when the ESP32 goes offline it can briefly come back for a 100ms ish which
// causes the phone to try and reconnect. If we fail downloading our initial radio state we don't want to
@ -633,6 +646,12 @@ class MeshService : Service(), Logging {
} else {
// lost radio connection, therefore no need to keep listening to GPS
stopLocationRequests()
GeeksvilleApplication.analytics.track(
"mesh_disconnect",
DataPair("num_nodes", numNodes),
DataPair("num_online", numOnlineNodes)
)
}
}

Wyświetl plik

@ -224,7 +224,8 @@ class RadioInterfaceService : Service(), Logging {
else {
val fromRadio = service.getCharacteristic(BTM_FROMRADIO_CHARACTER)
safe!!.asyncReadCharacteristic(fromRadio) {
val b = it.getOrThrow().value
val b = it.getOrThrow()
.value.clone() // We clone the array just in case, I'm not sure if they keep reusing the array
if (b.isNotEmpty()) {
debug("Received ${b.size} bytes from radio")
@ -380,13 +381,14 @@ class RadioInterfaceService : Service(), Logging {
// Note: we generate a new characteristic each time, because we are about to
// change the data and we want the data stored in the closure
val toRadio = service.getCharacteristic(uuid)
var a = safe!!.readCharacteristic(toRadio).value
var a = safe!!.readCharacteristic(toRadio)
.value.clone() // we copy the bluetooth array because it might still be in use
debug("Read of $uuid got ${a.size} bytes")
if (a.isEmpty()) // An empty bluetooth response is converted to a null response for our clients
a = null
a
null
else
a
}
}

Wyświetl plik

@ -5,6 +5,8 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.concurrent.CallbackContinuation
import com.geeksville.concurrent.Continuation
@ -65,7 +67,8 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
debug("We were not connected, so ignoring bluetooth shutdown")
}
BluetoothAdapter.STATE_ON -> {
warn("FIXME - requeue a connect anytime bluetooth is reenabled?")
warn("requeue a connect anytime bluetooth is reenabled")
reconnect()
}
}
}
@ -100,6 +103,35 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
}
}
/**
* skanky hack to restart BLE if it says it is hosed
* https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called
*/
var mHandler: Handler = Handler()
fun restartBle() {
GeeksvilleApplication.analytics.track("ble_restart") // record # of times we needed to use this nasty hack
error("Doing emergency BLE restart")
val mgr =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val adp = mgr.adapter
if (null != adp) {
if (adp.isEnabled) {
adp.disable()
// TODO: display some kind of UI about restarting BLE
mHandler.postDelayed(object : Runnable {
override fun run() {
if (!adp.isEnabled) {
adp.enable()
} else {
mHandler.postDelayed(this, 2500)
}
}
}, 2500)
}
}
}
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
@ -153,6 +185,11 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
debug("No connectionCallback registered")
}
}
if (status == 257) { // mystery error code when phone is hung
//throw Exception("Mystery bluetooth failure - debug me")
restartBle()
}
}
}
}
@ -350,6 +387,13 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
queueConnect(autoConnect, CallbackContinuation(cb))
}
/// Restart any previous connect attempts
private fun reconnect() {
connectionCallback?.let { cb ->
queueConnect(true, CallbackContinuation(cb))
}
}
fun connect(autoConnect: Boolean = false) = makeSync<Unit> { queueConnect(autoConnect, it) }
private fun queueReadCharacteristic(

Wyświetl plik

@ -43,8 +43,7 @@ object ScanState : Logging {
debug("stopping scan")
scanner!!.stopScan(callback)
callback = null
} else
debug("not stopping bt scanner")
}
}
}