Use our own scheduling when doing d2d backups (experimental)
This commit is contained in:
parent
d1f0fef718
commit
de51ad2cc9
7 changed files with 99 additions and 6 deletions
|
@ -30,6 +30,7 @@ android_app {
|
|||
"androidx.activity_activity-ktx",
|
||||
"androidx.preference_preference",
|
||||
"androidx.documentfile_documentfile",
|
||||
"androidx.work_work-runtime-ktx",
|
||||
"androidx.lifecycle_lifecycle-viewmodel-ktx",
|
||||
"androidx.lifecycle_lifecycle-livedata-ktx",
|
||||
"androidx-constraintlayout_constraintlayout",
|
||||
|
|
|
@ -135,6 +135,7 @@ dependencies {
|
|||
implementation(libs.androidx.lifecycle.livedata.ktx)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.documentfile)
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
implementation(libs.google.material)
|
||||
|
||||
implementation(libs.google.tink.android)
|
||||
|
|
69
app/src/main/java/com/stevesoltys/seedvault/BackupWorker.kt
Normal file
69
app/src/main/java/com/stevesoltys/seedvault/BackupWorker.kt
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The Calyx Institute
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.stevesoltys.seedvault
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.BackoffPolicy
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.UPDATE
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.stevesoltys.seedvault.transport.requestBackup
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class BackupWorker(
|
||||
appContext: Context,
|
||||
workerParams: WorkerParameters,
|
||||
) : Worker(appContext, workerParams) {
|
||||
|
||||
companion object {
|
||||
private const val UNIQUE_WORK_NAME = "APP_BACKUP"
|
||||
|
||||
fun schedule(appContext: Context) {
|
||||
val backupConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.UNMETERED)
|
||||
.setRequiresCharging(true)
|
||||
.build()
|
||||
val backupWorkRequest = PeriodicWorkRequestBuilder<BackupWorker>(
|
||||
repeatInterval = 24,
|
||||
repeatIntervalTimeUnit = TimeUnit.HOURS,
|
||||
flexTimeInterval = 2,
|
||||
flexTimeIntervalUnit = TimeUnit.HOURS,
|
||||
).setConstraints(backupConstraints)
|
||||
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.HOURS)
|
||||
.build()
|
||||
val workManager = WorkManager.getInstance(appContext)
|
||||
workManager.enqueueUniquePeriodicWork(UNIQUE_WORK_NAME, UPDATE, backupWorkRequest)
|
||||
}
|
||||
|
||||
fun unschedule(appContext: Context) {
|
||||
val workManager = WorkManager.getInstance(appContext)
|
||||
workManager.cancelUniqueWork(UNIQUE_WORK_NAME)
|
||||
}
|
||||
|
||||
fun logWorkInfo(appContext: Context) {
|
||||
val workManager = WorkManager.getInstance(appContext)
|
||||
workManager.getWorkInfosForUniqueWork(UNIQUE_WORK_NAME).get().forEach {
|
||||
Log.e(
|
||||
"BackupWorker", " ${it.state.name} - ${Date(it.nextScheduleTimeMillis)} - " +
|
||||
"runAttempts: ${it.runAttemptCount}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
// TODO once we make this the default, we should do storage backup here as well
|
||||
// or have two workers and ensure they never run at the same time
|
||||
return if (requestBackup(applicationContext)) Result.success()
|
||||
else Result.retry()
|
||||
}
|
||||
}
|
|
@ -44,7 +44,8 @@ class ExpertSettingsFragment : PreferenceFragmentCompat() {
|
|||
val d2dPreference = findPreference<SwitchPreferenceCompat>(PREF_KEY_D2D_BACKUPS)
|
||||
|
||||
d2dPreference?.setOnPreferenceChangeListener { _, newValue ->
|
||||
d2dPreference.isChecked = newValue as Boolean
|
||||
viewModel.onD2dChanged(newValue as Boolean)
|
||||
d2dPreference.isChecked = newValue
|
||||
|
||||
// automatically enable unlimited quota when enabling D2D backups
|
||||
if (d2dPreference.isChecked) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import android.net.NetworkCapabilities
|
|||
import android.net.NetworkRequest
|
||||
import android.net.Uri
|
||||
import android.os.Process.myUid
|
||||
import android.os.UserHandle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
|
@ -24,6 +25,7 @@ import androidx.lifecycle.Transformations.switchMap
|
|||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.recyclerview.widget.DiffUtil.calculateDiff
|
||||
import com.stevesoltys.seedvault.BackupWorker
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.crypto.KeyManager
|
||||
import com.stevesoltys.seedvault.metadata.MetadataManager
|
||||
|
@ -261,4 +263,13 @@ internal class SettingsViewModel(
|
|||
Toast.makeText(app, str, LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun onD2dChanged(enabled: Boolean) {
|
||||
backupManager.setFrameworkSchedulingEnabledForUser(UserHandle.myUserId(), !enabled)
|
||||
if (enabled) {
|
||||
BackupWorker.schedule(app)
|
||||
} else {
|
||||
BackupWorker.unschedule(app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,10 +60,15 @@ class ConfigurableBackupTransportService : Service(), KoinComponent {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the system to initiate a backup.
|
||||
*
|
||||
* @return true iff backups was requested successfully (backup itself can still fail).
|
||||
*/
|
||||
@WorkerThread
|
||||
fun requestBackup(context: Context) {
|
||||
fun requestBackup(context: Context): Boolean {
|
||||
val backupManager: IBackupManager = get().get()
|
||||
if (backupManager.isBackupEnabled) {
|
||||
return if (backupManager.isBackupEnabled) {
|
||||
val packageService: PackageService = get().get()
|
||||
val packages = packageService.eligiblePackages
|
||||
val appTotals = packageService.expectedAppTotals
|
||||
|
@ -78,11 +83,14 @@ fun requestBackup(context: Context) {
|
|||
nm.onBackupError()
|
||||
}
|
||||
if (result == BackupManager.SUCCESS) {
|
||||
Log.i(TAG, "Backup succeeded ")
|
||||
Log.i(TAG, "Backup request succeeded ")
|
||||
true
|
||||
} else {
|
||||
Log.e(TAG, "Backup failed: $result")
|
||||
Log.e(TAG, "Backup request failed: $result")
|
||||
false
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "Backup is not enabled")
|
||||
true // this counts as success
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ coroutines = { strictly = "1.6.4" }
|
|||
|
||||
# AndroidX versions
|
||||
# https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-14.0.0_r1/current/androidx/Android.bp
|
||||
room = { strictly = "2.4.0-alpha05" }
|
||||
room = { strictly = "2.5.0" }
|
||||
androidx-core = { strictly = "1.9.0-alpha05" }
|
||||
androidx-fragment = { strictly = "1.5.0-alpha03" }
|
||||
androidx-activity = { strictly = "1.5.0-alpha03" }
|
||||
|
@ -47,6 +47,7 @@ androidx-lifecycle-viewmodel-ktx = { strictly = "2.5.0-alpha03" }
|
|||
androidx-lifecycle-livedata-ktx = { strictly = "2.5.0-alpha03" }
|
||||
androidx-constraintlayout = { strictly = "2.2.0-alpha05" }
|
||||
androidx-documentfile = { strictly = "1.1.0-alpha01" }
|
||||
androidx-work-runtime = { strictly = "2.9.0-alpha01" }
|
||||
|
||||
[libraries]
|
||||
# Kotlin standard dependencies
|
||||
|
@ -76,6 +77,7 @@ androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-view
|
|||
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle-livedata-ktx" }
|
||||
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" }
|
||||
androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "androidx-documentfile" }
|
||||
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work-runtime" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
|
||||
[bundles]
|
||||
|
|
Loading…
Reference in a new issue