mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-07-23 11:39:15 +00:00
Block sync adapter's onPerformSync until SyncWorker finishes (bitfireAT/davx5#278)
* Block sync framework until SyncWorker finishes * Bump version code for 4.3.3 (previous version code was never released publicly) * Fetch translations from Transifex * Release internal version automatically [skip ci] * Update periodic sync workers when "Sync only on WiFi" flag is changed (#282) * Update periodic sync workers when "sync only on WiFi" flag is changed * Remove BootCompletedReceiver which was only needed to repair sync intervals (not required with WorkManager anymore) * Bump version code to 403030006 (stays 4.3.3) * Use unique worker name, Java notify/wait and observeForever * Remove observer when sync finished * Catch and ignore, but log interruption exceptions --------- Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
parent
46488f1618
commit
fac68680ee
|
@ -13,9 +13,14 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.SyncResult
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import at.bitfire.davdroid.InvalidAccountException
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.logging.Level
|
||||
|
||||
abstract class SyncAdapterService: Service() {
|
||||
|
@ -44,7 +49,7 @@ abstract class SyncAdapterService: Service() {
|
|||
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
|
||||
// We seem to have to pass this old SyncFramework extra for an Android 7 workaround
|
||||
val upload = extras.containsKey(ContentResolver.SYNC_EXTRAS_UPLOAD)
|
||||
Logger.log.info("Sync request via sync adapter (upload=$upload)")
|
||||
Logger.log.info("Sync request via sync framework (upload=$upload)")
|
||||
|
||||
val accountSettings = try {
|
||||
AccountSettings(context, account)
|
||||
|
@ -55,12 +60,54 @@ abstract class SyncAdapterService: Service() {
|
|||
|
||||
// Should we run the sync at all?
|
||||
if (!SyncWorker.wifiConditionsMet(context, accountSettings)) {
|
||||
Logger.log.info("Sync conditions not met. Aborting sync adapter")
|
||||
Logger.log.info("Sync conditions not met. Aborting sync framework initiated sync")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.log.fine("Sync adapter now handing over to SyncWorker")
|
||||
SyncWorker.enqueue(context, account, authority, upload = upload)
|
||||
Logger.log.fine("Sync framework now starting SyncWorker")
|
||||
val workerName = SyncWorker.enqueue(context, account, authority, upload = upload)
|
||||
|
||||
// Block the onPerformSync method to simulate an ongoing sync
|
||||
Logger.log.fine("Blocking sync framework until SyncWorker finishes")
|
||||
|
||||
// Because we are not allowed to observe worker state on a background thread, we can not
|
||||
// use it to block the sync adapter. Instead we check periodically whether the sync has
|
||||
// finished, putting the thread to sleep in between checks.
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
val status = workManager.getWorkInfosForUniqueWorkLiveData(workerName)
|
||||
|
||||
var finished = false
|
||||
val lock = Object()
|
||||
|
||||
val observer = Observer<List<WorkInfo>> { workInfoList ->
|
||||
for (workInfo in workInfoList) {
|
||||
if (workInfo.state.isFinished) {
|
||||
synchronized(lock) {
|
||||
finished = true
|
||||
lock.notify()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking(Dispatchers.Main) { // observeForever not allowed in background thread
|
||||
status.observeForever(observer)
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
try {
|
||||
if (!finished)
|
||||
lock.wait(10*60*1000) // wait max 10 minutes
|
||||
} catch (e: InterruptedException) {
|
||||
Logger.log.info("Interrupted while blocking sync framework. Sync may still be running")
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking(Dispatchers.Main) {
|
||||
status.removeObserver(observer)
|
||||
}
|
||||
|
||||
Logger.log.info("Returning to sync framework")
|
||||
}
|
||||
|
||||
override fun onSecurityException(account: Account, extras: Bundle, authority: String, syncResult: SyncResult) {
|
||||
|
|
|
@ -118,6 +118,7 @@ class SyncWorker @AssistedInject constructor(
|
|||
* @param resync whether to request (full) re-synchronization or not
|
||||
* @param upload see [ContentResolver.SYNC_EXTRAS_UPLOAD] used only for contacts sync
|
||||
* and android 7 workaround
|
||||
* @return existing or newly created worker name
|
||||
*/
|
||||
fun enqueue(
|
||||
context: Context,
|
||||
|
@ -125,7 +126,7 @@ class SyncWorker @AssistedInject constructor(
|
|||
authority: String,
|
||||
@ArgResync resync: Int = NO_RESYNC,
|
||||
upload: Boolean = false
|
||||
) {
|
||||
): String {
|
||||
// Worker arguments
|
||||
val argumentsBuilder = Data.Builder()
|
||||
.putString(ARG_AUTHORITY, authority)
|
||||
|
@ -152,14 +153,16 @@ class SyncWorker @AssistedInject constructor(
|
|||
.build()
|
||||
|
||||
// enqueue and start syncing
|
||||
Logger.log.log(Level.INFO, "Enqueueing unique worker: ${workerName(account, authority)}")
|
||||
val name = workerName(account, authority)
|
||||
Logger.log.log(Level.INFO, "Enqueueing unique worker: $name")
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||
workerName(account, authority),
|
||||
name,
|
||||
ExistingWorkPolicy.KEEP, // If sync is already running, just continue.
|
||||
// Existing retried work will not be replaced (for instance when
|
||||
// PeriodicSyncWorker enqueues another scheduled sync).
|
||||
workRequest
|
||||
)
|
||||
return name
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue