fix: Resolve top Crashlytics issues for 29320633 beta release (#5278)

This commit is contained in:
James Rich
2026-04-29 06:13:52 -05:00
committed by GitHub
parent a24f786ec4
commit e0c1934d96
4 changed files with 28 additions and 12 deletions

View File

@@ -17,7 +17,7 @@
package org.meshtastic.app.map.component
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalView
import androidx.lifecycle.compose.LocalLifecycleOwner
@@ -49,11 +49,13 @@ fun NodeClusterMarkers(
val savedStateRegistryOwner = LocalSavedStateRegistryOwner.current
// Workaround for https://github.com/googlemaps/android-maps-compose/issues/858
// and https://github.com/googlemaps/android-maps-compose/issues/875
// The maps clustering library creates an internal ComposeView to snapshot markers.
// If that view is not attached to the hierarchy (which it often isn't during rendering),
// it fails to find the Lifecycle and SavedState owners. We propagate them to the root view
// so the internal snapshot view can find them when walking up the tree.
LaunchedEffect(view, lifecycleOwner, savedStateRegistryOwner) {
// We do this in a SideEffect to ensure it happens before or during composition of children.
SideEffect {
val root = view.rootView
if (root.findViewTreeLifecycleOwner() == null) {
root.setViewTreeLifecycleOwner(lifecycleOwner)

View File

@@ -17,6 +17,7 @@
package org.meshtastic.core.ble
import co.touchlab.kermit.Logger
import com.juul.kable.NotConnectedException
import com.juul.kable.Peripheral
import com.juul.kable.PeripheralBuilder
import com.juul.kable.State
@@ -259,6 +260,9 @@ class KableBleConnection(private val scope: CoroutineScope, private val loggingC
private suspend fun safeClosePeripheral(tag: String) {
try {
peripheral?.disconnect()
} catch (_: NotConnectedException) {
// Silence "Disconnect requested" which Kable throws if already disconnected.
// This is a common non-fatal reported in Crashlytics that is safe to ignore here.
} catch (e: Exception) {
Logger.w(e) { "[$tag] Failed to disconnect peripheral" }
}

View File

@@ -356,6 +356,9 @@ class BleRadioTransport(
// materially speeds up the initial config drain and any bulk fromRadio reads.
if (bleConnection.requestHighConnectionPriority()) {
Logger.d { "[$address] Requested high BLE connection priority" }
// Wait for the connection parameter update to succeed before starting the heavy traffic
// in onConnect(). Otherwise, the Android BLE stack may disconnect with GATT 147.
delay(1.seconds)
}
this@BleRadioTransport.callback.onConnect()

View File

@@ -159,9 +159,24 @@ class MeshService : Service() {
0
}
startForegroundSafely(notification, foregroundServiceType)
return if (!wantForeground) {
Logger.i { "Stopping mesh service because no device is selected" }
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
START_NOT_STICKY
} else {
START_STICKY
}
}
private fun startForegroundSafely(notification: android.app.Notification, foregroundServiceType: Int) {
@Suppress("TooGenericExceptionCaught")
try {
ServiceCompat.startForeground(this, SERVICE_NOTIFY_ID, notification, foregroundServiceType)
} catch (ex: android.app.ForegroundServiceStartNotAllowedException) {
Logger.e(ex) { "ForegroundServiceStartNotAllowedException: OS restricted background start." }
} catch (ex: SecurityException) {
// On Android 14+ starting a location FGS from the background can fail with SecurityException
// if the app is not in an allowed state. Retry without the location type if that was requested.
@@ -177,6 +192,8 @@ class MeshService : Service() {
}
try {
ServiceCompat.startForeground(this, SERVICE_NOTIFY_ID, notification, connectedDeviceOnly)
} catch (retryEx: android.app.ForegroundServiceStartNotAllowedException) {
Logger.e(retryEx) { "ForegroundServiceStartNotAllowedException on retry." }
} catch (retryEx: Exception) {
Logger.e(retryEx) { "Failed to start foreground service even after retry" }
}
@@ -185,16 +202,6 @@ class MeshService : Service() {
}
} catch (ex: Exception) {
Logger.e(ex) { "Error starting foreground service" }
return START_NOT_STICKY
}
return if (!wantForeground) {
Logger.i { "Stopping mesh service because no device is selected" }
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
START_NOT_STICKY
} else {
START_STICKY
}
}