Fix foreground service start eligibility (#714)

* Fix foreground service start eligibility

* Defer foreground service start when backgrounded

* fix crash

---------

Co-authored-by: CC <cc@ggg.local>
This commit is contained in:
callebtc
2026-06-17 11:59:43 -05:00
committed by GitHub
co-authored by CC
parent 9e0a919cb8
commit b89644f5f7
2 changed files with 33 additions and 14 deletions
@@ -59,6 +59,7 @@ class MainActivity : OrientationAwareActivity() {
private lateinit var meshService: BluetoothMeshService
private lateinit var unifiedMeshService: MeshService
private val mainViewModel: MainViewModel by viewModels()
private var pendingMeshForegroundServiceStart = false
private val chatViewModel: ChatViewModel by viewModels {
object : ViewModelProvider.Factory {
override fun <T : androidx.lifecycle.ViewModel> create(modelClass: Class<T>): T {
@@ -114,8 +115,8 @@ class MainActivity : OrientationAwareActivity() {
// Initialize permission management
permissionManager = PermissionManager(this)
// Ensure foreground service is running and get mesh instance from holder
try { com.bitchat.android.service.MeshForegroundService.start(applicationContext) } catch (_: Exception) { }
// Start the foreground service when allowed, then get mesh instances from the holder.
startMeshForegroundServiceBestEffort()
meshService = com.bitchat.android.service.MeshServiceHolder.getOrCreate(applicationContext)
unifiedMeshService = com.bitchat.android.service.MeshServiceHolder.getUnifiedOrCreate(applicationContext)
// Expose BLE mesh to WiFi Aware controller for cross-transport relays - DEPRECATED
@@ -614,6 +615,22 @@ class MainActivity : OrientationAwareActivity() {
mainViewModel.updateErrorMessage(message)
mainViewModel.updateOnboardingState(OnboardingState.ERROR)
}
private fun startMeshForegroundServiceBestEffort() {
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
pendingMeshForegroundServiceStart = true
Log.i("MainActivity", "Deferring foreground mesh service start until activity is started")
return
}
try {
com.bitchat.android.service.MeshForegroundService.start(applicationContext)
pendingMeshForegroundServiceStart = false
} catch (e: Exception) {
pendingMeshForegroundServiceStart = true
Log.w("MainActivity", "Unable to start foreground mesh service; will retry when activity is started", e)
}
}
/**
* Check Battery Optimization status and proceed with onboarding flow
@@ -715,6 +732,7 @@ class MainActivity : OrientationAwareActivity() {
// Set up unified mesh delegate and start enabled transports
unifiedMeshService.delegate = chatViewModel
unifiedMeshService.startServices()
startMeshForegroundServiceBestEffort()
Log.d("MainActivity", "Mesh service started successfully")
@@ -752,6 +770,13 @@ class MainActivity : OrientationAwareActivity() {
handleVerificationIntent(intent)
}
}
override fun onStart() {
super.onStart()
if (pendingMeshForegroundServiceStart) {
startMeshForegroundServiceBestEffort()
}
}
override fun onResume() {
super.onResume()
@@ -38,24 +38,20 @@ class MeshForegroundService : Service() {
fun start(context: Context) {
val intent = Intent(context, MeshForegroundService::class.java).apply { action = ACTION_START }
// On API >= 26, avoid background-service start restrictions by using startForegroundService
// only when we can actually post a notification (Android 13+ requires runtime notif permission)
val bgEnabled = MeshServicePreferences.isBackgroundEnabled(true)
val hasNotifPerm = hasNotificationPermissionStatic(context)
// Only launch as an FGS when onStartCommand can promote immediately.
val shouldStartForeground = shouldStartAsForeground(context)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (bgEnabled && hasNotifPerm) {
if (shouldStartForeground) {
context.startForegroundService(intent)
} else {
// Do not attempt to start a background service from headless context without notif permission
// or when background is disabled, to avoid BackgroundServiceStartNotAllowedException.
android.util.Log.i(
"MeshForegroundService",
"Not starting service on API>=26 (bgEnabled=$bgEnabled, hasNotifPerm=$hasNotifPerm)"
"Not starting service on API>=26 (shouldStartForeground=$shouldStartForeground)"
)
}
} else {
if (bgEnabled) {
if (MeshServicePreferences.isBackgroundEnabled(true)) {
context.startService(intent)
} else {
android.util.Log.i("MeshForegroundService", "Background disabled; not starting service (pre-O)")
@@ -69,12 +65,10 @@ class MeshForegroundService : Service() {
*/
fun onNotificationPermissionGranted(context: Context) {
// If background is enabled and permission now granted, start/promo service
val hasNotifPerm = hasNotificationPermissionStatic(context)
if (!MeshServicePreferences.isBackgroundEnabled(true) || !hasNotifPerm) return
if (!shouldStartAsForeground(context)) return
val intent = Intent(context, MeshForegroundService::class.java).apply { action = ACTION_UPDATE_NOTIFICATION }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Safe now that we can show a notification
context.startForegroundService(intent)
} else {
context.startService(intent)