fix(background): improve foreground service lifecycle and keep-alive handling
This commit is contained in:
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -116,7 +116,6 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate commit messages since the previous tag
|
# Generate commit messages since the previous tag
|
||||||
echo "## What's Changed" > release_notes.md
|
|
||||||
echo "" >> release_notes.md
|
echo "" >> release_notes.md
|
||||||
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..${{ github.ref_name }} >> release_notes.md
|
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..${{ github.ref_name }} >> release_notes.md
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class BackgroundStreamingService : Service() {
|
|||||||
private val activeStreams = mutableSetOf<String>()
|
private val activeStreams = mutableSetOf<String>()
|
||||||
private var isForeground = false
|
private var isForeground = false
|
||||||
private var currentForegroundType: Int = 0
|
private var currentForegroundType: Int = 0
|
||||||
|
private var foregroundStartTime: Long = 0
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "conduit_streaming_channel"
|
const val CHANNEL_ID = "conduit_streaming_channel"
|
||||||
@@ -51,6 +52,12 @@ class BackgroundStreamingService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For KEEP_ALIVE, only refresh the wake lock without restarting foreground
|
||||||
|
if (intent?.action == "KEEP_ALIVE") {
|
||||||
|
keepAlive()
|
||||||
|
return START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
val notification = createMinimalNotification()
|
val notification = createMinimalNotification()
|
||||||
val desiredType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
val desiredType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
resolveForegroundServiceType(intent)
|
resolveForegroundServiceType(intent)
|
||||||
@@ -74,9 +81,6 @@ class BackgroundStreamingService : Service() {
|
|||||||
acquireWakeLock()
|
acquireWakeLock()
|
||||||
println("BackgroundStreamingService: Started foreground service")
|
println("BackgroundStreamingService: Started foreground service")
|
||||||
}
|
}
|
||||||
"KEEP_ALIVE" -> {
|
|
||||||
keepAlive()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
@@ -92,9 +96,12 @@ class BackgroundStreamingService : Service() {
|
|||||||
startForeground(NOTIFICATION_ID, notification)
|
startForeground(NOTIFICATION_ID, notification)
|
||||||
}
|
}
|
||||||
isForeground = true
|
isForeground = true
|
||||||
|
foregroundStartTime = System.currentTimeMillis()
|
||||||
|
println("BackgroundStreamingService: Foreground service started at $foregroundStartTime")
|
||||||
true
|
true
|
||||||
} catch (e: SecurityException) {
|
} catch (e: Exception) {
|
||||||
println("BackgroundStreamingService: Failed to enter foreground: ${e.message}")
|
// Catch all exceptions including ForegroundServiceStartNotAllowedException
|
||||||
|
println("BackgroundStreamingService: Failed to enter foreground: ${e.javaClass.simpleName}: ${e.message}")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +161,9 @@ class BackgroundStreamingService : Service() {
|
|||||||
PowerManager.PARTIAL_WAKE_LOCK,
|
PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
"Conduit::StreamingWakeLock"
|
"Conduit::StreamingWakeLock"
|
||||||
).apply {
|
).apply {
|
||||||
acquire(3 * 60 * 60 * 1000L) // 3 hours max (refreshed every 5 minutes)
|
// Use shorter wake lock duration to comply with Android restrictions
|
||||||
|
// Refresh periodically via keepAlive instead of long timeout
|
||||||
|
acquire(10 * 60 * 1000L) // 10 minutes (refreshed every 5 minutes)
|
||||||
}
|
}
|
||||||
println("BackgroundStreamingService: Wake lock acquired")
|
println("BackgroundStreamingService: Wake lock acquired")
|
||||||
}
|
}
|
||||||
@@ -170,6 +179,18 @@ class BackgroundStreamingService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun keepAlive() {
|
private fun keepAlive() {
|
||||||
|
// Check if we're approaching Android 14's 6-hour dataSync limit
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && isForeground) {
|
||||||
|
val uptime = System.currentTimeMillis() - foregroundStartTime
|
||||||
|
val fiveHours = 5 * 60 * 60 * 1000L // 5 hours in milliseconds
|
||||||
|
|
||||||
|
if (uptime > fiveHours) {
|
||||||
|
println("BackgroundStreamingService: Approaching time limit (${uptime / 3600000}h), stopping service")
|
||||||
|
stopStreaming()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh wake lock to extend background processing time
|
// Refresh wake lock to extend background processing time
|
||||||
releaseWakeLock()
|
releaseWakeLock()
|
||||||
acquireWakeLock()
|
acquireWakeLock()
|
||||||
@@ -177,15 +198,20 @@ class BackgroundStreamingService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun stopStreaming() {
|
private fun stopStreaming() {
|
||||||
|
println("BackgroundStreamingService: Stopping service...")
|
||||||
activeStreams.clear()
|
activeStreams.clear()
|
||||||
releaseWakeLock()
|
releaseWakeLock()
|
||||||
|
|
||||||
if (isForeground) {
|
if (isForeground) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
try {
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
} else {
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
@Suppress("DEPRECATION")
|
} else {
|
||||||
stopForeground(true)
|
@Suppress("DEPRECATION")
|
||||||
|
stopForeground(true)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("BackgroundStreamingService: Error stopping foreground: ${e.message}")
|
||||||
}
|
}
|
||||||
isForeground = false
|
isForeground = false
|
||||||
}
|
}
|
||||||
@@ -194,9 +220,18 @@ class BackgroundStreamingService : Service() {
|
|||||||
println("BackgroundStreamingService: Service stopped")
|
println("BackgroundStreamingService: Service stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||||
|
println("BackgroundStreamingService: Task removed, stopping service")
|
||||||
|
stopStreaming()
|
||||||
|
super.onTaskRemoved(rootIntent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
println("BackgroundStreamingService: onDestroy called")
|
||||||
releaseWakeLock()
|
releaseWakeLock()
|
||||||
|
activeStreams.clear()
|
||||||
isForeground = false
|
isForeground = false
|
||||||
|
foregroundStartTime = 0
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
println("BackgroundStreamingService: Service destroyed")
|
println("BackgroundStreamingService: Service destroyed")
|
||||||
}
|
}
|
||||||
@@ -382,11 +417,9 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
|||||||
streamsRequiringMic.isNotEmpty(),
|
streamsRequiringMic.isNotEmpty(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
// Use startService (not startForegroundService) for keep-alive pings
|
||||||
context.startForegroundService(serviceIntent)
|
// to avoid ForegroundServiceStartNotAllowedException on Android 14+
|
||||||
} else {
|
context.startService(serviceIntent)
|
||||||
context.startService(serviceIntent)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("BackgroundStreamingHandler: Failed to keep alive service: ${e.message}")
|
println("BackgroundStreamingHandler: Failed to keep alive service: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user