Skip to content

Commit

Permalink
WIP: Request @pm@ backup after initialization
Browse files Browse the repository at this point in the history
to avoid a 2nd restore set being used
  • Loading branch information
grote committed Feb 26, 2024
1 parent cef3936 commit bd2b437
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class KoinInstrumentationTestApp : App() {

viewModel {
currentBackupStorageViewModel =
spyk(BackupStorageViewModel(context, get(), get(), get()))
spyk(BackupStorageViewModel(context, get(), get(), get(), get()))
currentBackupStorageViewModel!!
}

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/stevesoltys/seedvault/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ open class App : Application() {

viewModel { SettingsViewModel(this@App, get(), get(), get(), get(), get(), get()) }
viewModel { RecoveryCodeViewModel(this@App, get(), get(), get(), get(), get(), get()) }
viewModel { BackupStorageViewModel(this@App, get(), get(), get()) }
viewModel { BackupStorageViewModel(this@App, get(), get(), get(), get()) }
viewModel { RestoreStorageViewModel(this@App, get(), get()) }
viewModel { RestoreViewModel(this@App, get(), get(), get(), get(), get(), get()) }
viewModel { FileSelectionViewModel(this@App, get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ internal class DocumentsProviderStoragePlugin(
storage.reset(token)

// get or create root backup dir
storage.rootBackupDir ?: throw IOException()
// TODO do we need to do this?
// storage.rootBackupDir ?: throw IOException()
}

@Throws(IOException::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2024 The Calyx Institute
* SPDX-License-Identifier: Apache-2.0
*/

package com.stevesoltys.seedvault.transport.backup

import android.app.backup.BackupProgress
import android.app.backup.IBackupManager
import android.app.backup.IBackupObserver
import android.os.UserHandle
import android.util.Log
import androidx.annotation.WorkerThread
import com.stevesoltys.seedvault.BackupMonitor
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
import com.stevesoltys.seedvault.transport.TRANSPORT_ID

class BackupInitializer(
private val backupManager: IBackupManager,
) {

companion object {
private val TAG = BackupInitializer::class.simpleName
}

fun initialize(onError: () -> Unit, onSuccess: () -> Unit) {
val observer = BackupObserver("Initialization", onError) {
// After successful initialization, we request a @pm@ backup right away,
// because if this finds empty state, it asks us to do another initialization.
// And then we end up with yet another restore set token.
// Since we want the final token as soon as possible, we need to get it here.
backupManager.requestBackup(
arrayOf(MAGIC_PACKAGE_MANAGER),
BackupObserver("Initial backup of @pm@", onError, onSuccess),
BackupMonitor(),
0,
)
}
backupManager.initializeTransportsForUser(
UserHandle.myUserId(),
arrayOf(TRANSPORT_ID),
observer,
)
}

@WorkerThread
private inner class BackupObserver(
private val operation: String,
private val onError: () -> Unit,
private val onSuccess: () -> Unit,
) : IBackupObserver.Stub() {
override fun onUpdate(currentBackupPackage: String, backupProgress: BackupProgress) {
// noop
}

override fun onResult(target: String, status: Int) {
// noop
}

override fun backupFinished(status: Int) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "$operation finished. Status: $status")
}
if (status == 0) {
onSuccess()
} else {
onError()
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module

val backupModule = module {
single { BackupInitializer(get()) }
single { InputFactory() }
single {
PackageService(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.stevesoltys.seedvault.ui.recoverycode

import android.app.backup.IBackupManager
import android.os.UserHandle
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import cash.z.ecc.android.bip39.Mnemonics
Expand All @@ -12,8 +11,7 @@ import cash.z.ecc.android.bip39.toSeed
import com.stevesoltys.seedvault.App
import com.stevesoltys.seedvault.crypto.Crypto
import com.stevesoltys.seedvault.crypto.KeyManager
import com.stevesoltys.seedvault.transport.TRANSPORT_ID
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
import com.stevesoltys.seedvault.transport.backup.BackupInitializer
import com.stevesoltys.seedvault.ui.LiveEvent
import com.stevesoltys.seedvault.ui.MutableLiveEvent
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
Expand All @@ -32,7 +30,7 @@ internal class RecoveryCodeViewModel(
private val crypto: Crypto,
private val keyManager: KeyManager,
private val backupManager: IBackupManager,
private val backupCoordinator: BackupCoordinator,
private val backupInitializer: BackupInitializer,
private val notificationManager: BackupNotificationManager,
private val storageBackup: StorageBackup,
) : AndroidViewModel(app) {
Expand Down Expand Up @@ -109,11 +107,9 @@ internal class RecoveryCodeViewModel(
storageBackup.clearCache()
try {
// initialize the new location
if (backupManager.isBackupEnabled) backupManager.initializeTransportsForUser(
UserHandle.myUserId(),
arrayOf(TRANSPORT_ID),
null
)
if (backupManager.isBackupEnabled) backupInitializer.initialize({ }) {
// no-op
}
} catch (e: IOException) {
Log.e(TAG, "Error starting new RestoreSet", e)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package com.stevesoltys.seedvault.ui.storage

import android.app.Application
import android.app.backup.BackupProgress
import android.app.backup.IBackupManager
import android.app.backup.IBackupObserver
import android.app.job.JobInfo
import android.net.Uri
import android.os.UserHandle
import android.util.Log
import androidx.annotation.WorkerThread
import androidx.lifecycle.viewModelScope
import androidx.work.ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.storage.StorageBackupJobService
import com.stevesoltys.seedvault.transport.TRANSPORT_ID
import com.stevesoltys.seedvault.transport.backup.BackupInitializer
import com.stevesoltys.seedvault.worker.AppBackupWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand All @@ -28,6 +24,7 @@ private val TAG = BackupStorageViewModel::class.java.simpleName
internal class BackupStorageViewModel(
private val app: Application,
private val backupManager: IBackupManager,
private val backupInitializer: BackupInitializer,
private val storageBackup: StorageBackup,
settingsManager: SettingsManager,
) : StorageViewModel(app, settingsManager) {
Expand All @@ -52,13 +49,22 @@ internal class BackupStorageViewModel(
storageBackup.clearCache()
try {
// initialize the new location (if backups are enabled)
if (backupManager.isBackupEnabled) backupManager.initializeTransportsForUser(
UserHandle.myUserId(),
arrayOf(TRANSPORT_ID),
// if storage is on USB and this is not SetupWizard, do a backup right away
InitializationObserver(isUsb && !isSetupWizard)
) else {
InitializationObserver(false).backupFinished(0)
if (backupManager.isBackupEnabled) {
val onError = {
Log.e(TAG, "Error starting new RestoreSet")
onInitializationError()
}
backupInitializer.initialize(onError) {
val requestBackup = isUsb && !isSetupWizard
if (requestBackup) {
AppBackupWorker.scheduleNow(app, reschedule = false)
}
// notify the UI that the location has been set
mLocationChecked.postEvent(LocationResult())
}
} else {
// notify the UI that the location has been set
mLocationChecked.postEvent(LocationResult())
}
} catch (e: IOException) {
Log.e(TAG, "Error starting new RestoreSet", e)
Expand Down Expand Up @@ -90,35 +96,6 @@ internal class BackupStorageViewModel(
BackupJobService.cancelJob(app)
}

@WorkerThread
private inner class InitializationObserver(val requestBackup: Boolean) :
IBackupObserver.Stub() {
override fun onUpdate(currentBackupPackage: String, backupProgress: BackupProgress) {
// noop
}

override fun onResult(target: String, status: Int) {
// noop
}

override fun backupFinished(status: Int) {
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "Initialization finished. Status: $status")
}
if (status == 0) {
// notify the UI that the location has been set
mLocationChecked.postEvent(LocationResult())
if (requestBackup) {
val isUsb = settingsManager.getStorage()?.isUsb ?: false
AppBackupWorker.scheduleNow(app, reschedule = !isUsb)
}
} else {
// notify the UI that the location was invalid
onInitializationError()
}
}
}

private fun onInitializationError() {
val errorMsg = app.getString(R.string.storage_check_fragment_backup_error)
mLocationChecked.postEvent(LocationResult(errorMsg))
Expand Down

0 comments on commit bd2b437

Please sign in to comment.