mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-26 12:52:40 +02:00
fix(download): recover queue after worker timeout (#524)
This commit is contained in:
@@ -467,8 +467,58 @@ class DownloadService : Service() {
|
||||
*/
|
||||
override fun onTimeout(startId: Int, fgsType: Int) {
|
||||
android.util.Log.w("DownloadService", "Foreground service timeout reached (6 hours limit). Stopping service.")
|
||||
|
||||
stopForegroundService()
|
||||
|
||||
stopNativeWorkerForSystemTimeout()
|
||||
}
|
||||
|
||||
private fun stopNativeWorkerForSystemTimeout() {
|
||||
if (!hasNativeWorkerState()) {
|
||||
stopForegroundService()
|
||||
return
|
||||
}
|
||||
nativeWorkerCancelRequested = true
|
||||
// Supersede the coroutine before cancelling it. Its catch/finally
|
||||
// blocks must not publish a skipped/finished state over the recovery
|
||||
// snapshot written below.
|
||||
nativeWorkerGeneration++
|
||||
val activeItemIds = synchronized(nativeWorkerItems) {
|
||||
nativeWorkerItems
|
||||
.filter { NativeWorkerPolicy.statusAfterWorkerStop(it.status) != it.status }
|
||||
.map { it.itemId }
|
||||
}
|
||||
for (itemId in activeItemIds) {
|
||||
try {
|
||||
Gobackend.cancelDownload(itemId)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
NativeDownloadFinalizer.cancelActiveWork()
|
||||
nativeWorkerJob?.cancel(
|
||||
CancellationException("Native queue stopped by Android timeout")
|
||||
)
|
||||
synchronized(nativeWorkerItems) {
|
||||
for (item in nativeWorkerItems) {
|
||||
val recoveredStatus = NativeWorkerPolicy.statusAfterWorkerStop(item.status)
|
||||
if (recoveredStatus == item.status) continue
|
||||
item.status = recoveredStatus
|
||||
item.progress = 0.0
|
||||
item.bytesReceived = 0L
|
||||
item.bytesTotal = 0L
|
||||
item.error = ""
|
||||
item.resultJson = null
|
||||
}
|
||||
}
|
||||
nativeWorkerCurrentItemId = ""
|
||||
writeNativeWorkerSnapshot(
|
||||
isRunning = false,
|
||||
isPaused = false,
|
||||
currentItemId = "",
|
||||
message = "Android background time limit reached",
|
||||
includeItems = true,
|
||||
)
|
||||
// Cancellation and the final recovery snapshot were handled above.
|
||||
// Only tear down the foreground-service resources here.
|
||||
stopForegroundService(cancelNativeWorker = false)
|
||||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
@@ -981,7 +1031,9 @@ class DownloadService : Service() {
|
||||
)
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
if (nativeWorkerCancelRequested) {
|
||||
if (nativeWorkerCancelRequested &&
|
||||
generation == nativeWorkerGeneration
|
||||
) {
|
||||
updateNativeWorkerItem(request.itemId) {
|
||||
it.status = "skipped"
|
||||
it.error = "Cancelled"
|
||||
|
||||
@@ -68,4 +68,9 @@ internal object NativeWorkerPolicy {
|
||||
completed: Int,
|
||||
failed: Int,
|
||||
): Boolean = !cancelRequested && completed + failed > 0
|
||||
|
||||
fun statusAfterWorkerStop(status: String): String = when (status) {
|
||||
"preparing", "downloading", "finalizing" -> "queued"
|
||||
else -> status
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,4 +149,15 @@ class NativeWorkerPolicyTest {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stoppedWorkerRequeuesOnlyInFlightItems() {
|
||||
listOf("preparing", "downloading", "finalizing").forEach { status ->
|
||||
assertEquals("queued", NativeWorkerPolicy.statusAfterWorkerStop(status))
|
||||
}
|
||||
assertEquals("queued", NativeWorkerPolicy.statusAfterWorkerStop("queued"))
|
||||
assertEquals("completed", NativeWorkerPolicy.statusAfterWorkerStop("completed"))
|
||||
assertEquals("failed", NativeWorkerPolicy.statusAfterWorkerStop("failed"))
|
||||
assertEquals("skipped", NativeWorkerPolicy.statusAfterWorkerStop("skipped"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user