Move workers to foreground service context a bit more safely (#10202)
The system will crash the app if the worker that calls setForeground() finished before the service runner be able to call Service.startForeground(). This edge case is not handled by WorkManager and there is no way to check if the required calls are done. So here we suspend the worker by an arbitrary duration assuming the transition to foreground service is done by then.
This commit is contained in:
parent
dfa5c229b3
commit
24e1b4034e
@ -17,6 +17,7 @@ import com.hippo.unifile.UniFile
|
|||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||||
import eu.kanade.tachiyomi.util.system.isRunning
|
import eu.kanade.tachiyomi.util.system.isRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
@ -39,19 +40,14 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
|
|||||||
|
|
||||||
if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry()
|
if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry()
|
||||||
|
|
||||||
val backupPreferences = Injekt.get<BackupPreferences>()
|
|
||||||
|
|
||||||
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
|
||||||
?: getAutomaticBackupLocation()
|
?: getAutomaticBackupLocation()
|
||||||
?: return Result.failure()
|
?: return Result.failure()
|
||||||
|
|
||||||
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
|
setForegroundSafely()
|
||||||
|
|
||||||
try {
|
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
|
||||||
setForeground(getForegroundInfo())
|
val backupPreferences = Injekt.get<BackupPreferences>()
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val location = BackupCreator(context).createBackup(uri, flags, isAutoBackup)
|
val location = BackupCreator(context).createBackup(uri, flags, isAutoBackup)
|
||||||
|
@ -12,6 +12,7 @@ import androidx.work.workDataOf
|
|||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||||
import eu.kanade.tachiyomi.util.system.isRunning
|
import eu.kanade.tachiyomi.util.system.isRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
@ -29,11 +30,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
?: return Result.failure()
|
?: return Result.failure()
|
||||||
val sync = inputData.getBoolean(SYNC_KEY, false)
|
val sync = inputData.getBoolean(SYNC_KEY, false)
|
||||||
|
|
||||||
try {
|
setForegroundSafely()
|
||||||
setForeground(getForegroundInfo())
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val restorer = BackupRestorer(context, notifier)
|
val restorer = BackupRestorer(context, notifier)
|
||||||
|
@ -16,11 +16,10 @@ import eu.kanade.tachiyomi.data.notification.Notifications
|
|||||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||||
import eu.kanade.tachiyomi.util.system.isOnline
|
import eu.kanade.tachiyomi.util.system.isOnline
|
||||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||||
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import logcat.LogPriority
|
|
||||||
import tachiyomi.core.util.system.logcat
|
|
||||||
import tachiyomi.domain.download.service.DownloadPreferences
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -51,21 +50,18 @@ class DownloadJob(context: Context, workerParams: WorkerParameters) : CoroutineW
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
try {
|
var active = checkConnectivity() && downloadManager.downloaderStart()
|
||||||
setForeground(getForegroundInfo())
|
|
||||||
} catch (e: IllegalStateException) {
|
if (!active) {
|
||||||
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
|
return Result.failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
var networkCheck = checkConnectivity()
|
setForegroundSafely()
|
||||||
var active = networkCheck
|
|
||||||
downloadManager.downloaderStart()
|
|
||||||
|
|
||||||
// Keep the worker running when needed
|
// Keep the worker running when needed
|
||||||
while (active) {
|
while (active) {
|
||||||
delay(100)
|
delay(100)
|
||||||
networkCheck = checkConnectivity()
|
active = !isStopped && downloadManager.isRunning && checkConnectivity()
|
||||||
active = !isStopped && networkCheck && downloadManager.isRunning
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
|
@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat
|
|||||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||||
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
|
||||||
import eu.kanade.tachiyomi.util.system.isRunning
|
import eu.kanade.tachiyomi.util.system.isRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
@ -108,11 +109,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
setForegroundSafely()
|
||||||
setForeground(getForegroundInfo())
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
|
|
||||||
}
|
|
||||||
|
|
||||||
libraryPreferences.lastUpdatedTimestamp().set(Date().time)
|
libraryPreferences.lastUpdatedTimestamp().set(Date().time)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications
|
|||||||
import eu.kanade.tachiyomi.source.UnmeteredSource
|
import eu.kanade.tachiyomi.source.UnmeteredSource
|
||||||
import eu.kanade.tachiyomi.util.prepUpdateCover
|
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||||
import eu.kanade.tachiyomi.util.system.isRunning
|
import eu.kanade.tachiyomi.util.system.isRunning
|
||||||
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
@ -53,11 +54,7 @@ class MetadataUpdateJob(private val context: Context, workerParams: WorkerParame
|
|||||||
private var mangaToUpdate: List<LibraryManga> = mutableListOf()
|
private var mangaToUpdate: List<LibraryManga> = mutableListOf()
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
try {
|
setForegroundSafely()
|
||||||
setForeground(getForegroundInfo())
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
|
|
||||||
}
|
|
||||||
|
|
||||||
addMangaToQueue()
|
addMangaToQueue()
|
||||||
|
|
||||||
|
@ -17,13 +17,12 @@ import eu.kanade.tachiyomi.network.await
|
|||||||
import eu.kanade.tachiyomi.network.newCachelessCallWithProgress
|
import eu.kanade.tachiyomi.network.newCachelessCallWithProgress
|
||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.storage.saveTo
|
import eu.kanade.tachiyomi.util.storage.saveTo
|
||||||
|
import eu.kanade.tachiyomi.util.system.setForegroundSafely
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import logcat.LogPriority
|
|
||||||
import okhttp3.internal.http2.ErrorCode
|
import okhttp3.internal.http2.ErrorCode
|
||||||
import okhttp3.internal.http2.StreamResetException
|
import okhttp3.internal.http2.StreamResetException
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.core.util.system.logcat
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -43,11 +42,7 @@ class AppUpdateDownloadJob(private val context: Context, workerParams: WorkerPar
|
|||||||
return Result.failure()
|
return Result.failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
setForegroundSafely()
|
||||||
setForeground(getForegroundInfo())
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
|
|
||||||
}
|
|
||||||
|
|
||||||
withIOContext {
|
withIOContext {
|
||||||
downloadApk(title, url)
|
downloadApk(title, url)
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package eu.kanade.tachiyomi.util.system
|
package eu.kanade.tachiyomi.util.system
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.WorkInfo
|
import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.util.system.logcat
|
||||||
|
|
||||||
val Context.workManager: WorkManager
|
val Context.workManager: WorkManager
|
||||||
get() = WorkManager.getInstance(this)
|
get() = WorkManager.getInstance(this)
|
||||||
@ -11,3 +15,21 @@ fun WorkManager.isRunning(tag: String): Boolean {
|
|||||||
val list = this.getWorkInfosByTag(tag).get()
|
val list = this.getWorkInfosByTag(tag).get()
|
||||||
return list.any { it.state == WorkInfo.State.RUNNING }
|
return list.any { it.state == WorkInfo.State.RUNNING }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes this worker run in the context of a foreground service.
|
||||||
|
*
|
||||||
|
* Note that this function is a no-op if the process is subject to foreground
|
||||||
|
* service restrictions.
|
||||||
|
*
|
||||||
|
* Moving to foreground service context requires the worker to run a bit longer,
|
||||||
|
* allowing Service.startForeground() to be called and avoiding system crash.
|
||||||
|
*/
|
||||||
|
suspend fun CoroutineWorker.setForegroundSafely() {
|
||||||
|
try {
|
||||||
|
setForeground(getForegroundInfo())
|
||||||
|
delay(500)
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Not allowed to set foreground job" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user