Re-initialize backup location when changing recovery code
* delete all storage backups for current user
* clears the storage backup cache
* start a new app data restore set and initializes it
The reason is that old backups won't be readable anymore with the new key. We also can't delete other backups safely as we did before, because we can't be sure that they don't belong to a different device or user.
diff --git a/app/src/main/java/com/stevesoltys/seedvault/App.kt b/app/src/main/java/com/stevesoltys/seedvault/App.kt
index 6aaeeb2..a443b3b 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/App.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/App.kt
@@ -46,7 +46,7 @@
factory { AppListRetriever(this@App, get(), get(), get()) }
viewModel { SettingsViewModel(this@App, get(), get(), get(), get(), get(), get()) }
- viewModel { RecoveryCodeViewModel(this@App, get(), get(), get()) }
+ viewModel { RecoveryCodeViewModel(this@App, get(), get(), 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()) }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt
index 9789b43..c71d450 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/plugins/saf/DocumentsProviderBackupPlugin.kt
@@ -45,11 +45,6 @@
}
@Throws(IOException::class)
- override suspend fun deleteAllBackups() {
- storage.rootBackupDir?.deleteContents(context)
- }
-
- @Throws(IOException::class)
override suspend fun getMetadataOutputStream(): OutputStream {
val setDir = storage.getSetDir() ?: throw IOException()
val metadataFile = setDir.createOrGetFile(context, FILE_BACKUP_METADATA)
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt
index 5a08a63..f867250 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupPlugin.kt
@@ -26,12 +26,6 @@
suspend fun initializeDevice()
/**
- * Delete all existing [RestoreSet]s from the storage medium.
- */
- @Throws(IOException::class)
- suspend fun deleteAllBackups()
-
- /**
* Returns an [OutputStream] for writing backup metadata.
*/
@Throws(IOException::class)
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt
index 1bde12c..4b707b8 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt
@@ -192,7 +192,7 @@
private val regenRequest = registerForActivityResult(StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
- viewModel.deleteAllBackup()
+ viewModel.reinitializeBackupLocation()
parentFragmentManager.popBackStack()
Snackbar.make(requireView(), R.string.recovery_code_recreated, Snackbar.LENGTH_LONG)
.show()
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt
index c306947..3b8c617 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt
@@ -1,11 +1,14 @@
package com.stevesoltys.seedvault.ui.recoverycode
+import android.app.backup.IBackupManager
+import android.os.UserHandle
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import com.stevesoltys.seedvault.App
import com.stevesoltys.seedvault.crypto.Crypto
import com.stevesoltys.seedvault.crypto.KeyManager
-import com.stevesoltys.seedvault.transport.backup.BackupPlugin
+import com.stevesoltys.seedvault.transport.TRANSPORT_ID
+import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
import com.stevesoltys.seedvault.ui.LiveEvent
import com.stevesoltys.seedvault.ui.MutableLiveEvent
import io.github.novacrypto.bip39.JavaxPBKDF2WithHmacSHA512
@@ -21,6 +24,7 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
+import org.calyxos.backup.storage.api.StorageBackup
import java.io.IOException
import java.security.SecureRandom
import java.util.ArrayList
@@ -28,11 +32,15 @@
internal const val WORD_NUM = 12
internal const val WORD_LIST_SIZE = 2048
-class RecoveryCodeViewModel(
+private val TAG = RecoveryCodeViewModel::class.java.simpleName
+
+internal class RecoveryCodeViewModel(
app: App,
private val crypto: Crypto,
private val keyManager: KeyManager,
- private val backupPlugin: BackupPlugin
+ private val backupManager: IBackupManager,
+ private val backupCoordinator: BackupCoordinator,
+ private val storageBackup: StorageBackup
) : AndroidViewModel(app) {
internal val wordList: List<CharSequence> by lazy {
@@ -79,12 +87,32 @@
}
}
- fun deleteAllBackup() {
+ /**
+ * Deletes all storage backups for current user and clears the storage backup cache.
+ * Also starts a new app data restore set and initializes it.
+ *
+ * The reason is that old backups won't be readable anymore with the new key.
+ * We can't delete other backups safely, because we can't be sure
+ * that they don't belong to a different device or user.
+ */
+ fun reinitializeBackupLocation() {
+ Log.d(TAG, "Re-initializing backup location...")
GlobalScope.launch(Dispatchers.IO) {
+ // remove old storage snapshots and clear cache
+ storageBackup.deleteAllSnapshots()
+ storageBackup.clearCache()
try {
- backupPlugin.deleteAllBackups()
+ // will also generate a new backup token for the new restore set
+ backupCoordinator.startNewRestoreSet()
+
+ // initialize the new location
+ backupManager.initializeTransportsForUser(
+ UserHandle.myUserId(),
+ arrayOf(TRANSPORT_ID),
+ null
+ )
} catch (e: IOException) {
- Log.e("RecoveryCodeViewModel", "Error deleting backups", e)
+ Log.e(TAG, "Error starting new RestoreSet", e)
}
}
}