Skip to content

Commit

Permalink
Use our own scheduling when doing d2d backups (experimental)
Browse files Browse the repository at this point in the history
  • Loading branch information
grote committed Feb 1, 2024
1 parent 9c4f9d8 commit d361767
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 6 deletions.
1 change: 1 addition & 0 deletions Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions app/src/main/java/com/stevesoltys/seedvault/BackupWorker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 {
return if (requestBackup(applicationContext)) Result.success()
else Result.retry()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
4 changes: 3 additions & 1 deletion build.libs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit d361767

Please sign in to comment.