Support adb shell bmgr backupnow
We don't get notified about the start nor the end of such a backup run, so we need hacks to do initialization and finalization.
This commit is contained in:
parent
e602bbe2ab
commit
8d949e2d64
5 changed files with 48 additions and 4 deletions
|
@ -38,9 +38,13 @@ internal class AppBackupManager(
|
||||||
/**
|
/**
|
||||||
* A temporary [SnapshotCreator] that has a lifetime only valid during the backup run.
|
* A temporary [SnapshotCreator] that has a lifetime only valid during the backup run.
|
||||||
*/
|
*/
|
||||||
|
@Volatile
|
||||||
var snapshotCreator: SnapshotCreator? = null
|
var snapshotCreator: SnapshotCreator? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var startedViaAdb = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this method before doing any kind of backup work.
|
* Call this method before doing any kind of backup work.
|
||||||
* It will
|
* It will
|
||||||
|
@ -113,6 +117,29 @@ internal class AppBackupManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When doing backups with `adb shell bmgr backupnow`,
|
||||||
|
* we don't get a chance to do our initialization in [beforeBackup],
|
||||||
|
* so we use this opportunity to do it now.
|
||||||
|
*/
|
||||||
|
suspend fun ensureBackupPrepared() = if (snapshotCreator == null) {
|
||||||
|
log.warn { "Backup not prepared. If not started via `adb shell bmgr` that's a bug" }
|
||||||
|
startedViaAdb = true
|
||||||
|
beforeBackup()
|
||||||
|
} else Unit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We don't get notified when backups ran from `adb shell bmgr backupnow` end,
|
||||||
|
* so [afterBackupFinished] will not run, so we need to find a place
|
||||||
|
*/
|
||||||
|
suspend fun finalizeBackupIfNeeded() {
|
||||||
|
if (startedViaAdb) {
|
||||||
|
log.warn { "Backup not finalized. If not started via `adb shell bmgr` that's a bug" }
|
||||||
|
startedViaAdb = false
|
||||||
|
afterBackupFinished(true) // is there a way to know if success or not?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the repo identified by [repoId] can be transferred to this device.
|
* Returns true if the repo identified by [repoId] can be transferred to this device.
|
||||||
* This is the case when it isn't the same as the current repoId and the version is latest.
|
* This is the case when it isn't the same as the current repoId and the version is latest.
|
||||||
|
|
|
@ -127,8 +127,8 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
|
||||||
return backupCoordinator.isAppEligibleForBackup(targetPackage, isFullBackup)
|
return backupCoordinator.isAppEligibleForBackup(targetPackage, isFullBackup)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long {
|
override fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long = runBlocking {
|
||||||
return backupCoordinator.getBackupQuota(packageName, isFullBackup)
|
backupCoordinator.getBackupQuota(packageName, isFullBackup)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearBackupData(packageInfo: PackageInfo): Int = runBlocking {
|
override fun clearBackupData(packageInfo: PackageInfo): Int = runBlocking {
|
||||||
|
|
|
@ -12,9 +12,11 @@ import android.os.IBinder
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.stevesoltys.seedvault.crypto.KeyManager
|
import com.stevesoltys.seedvault.crypto.KeyManager
|
||||||
import com.stevesoltys.seedvault.permitDiskReads
|
import com.stevesoltys.seedvault.permitDiskReads
|
||||||
|
import com.stevesoltys.seedvault.repo.AppBackupManager
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ class ConfigurableBackupTransportService : Service(), KoinComponent {
|
||||||
|
|
||||||
private val keyManager: KeyManager by inject()
|
private val keyManager: KeyManager by inject()
|
||||||
private val backupManager: IBackupManager by inject()
|
private val backupManager: IBackupManager by inject()
|
||||||
|
private val appBackupManager: AppBackupManager by inject()
|
||||||
private val notificationManager: BackupNotificationManager by inject()
|
private val notificationManager: BackupNotificationManager by inject()
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
@ -62,6 +65,11 @@ class ConfigurableBackupTransportService : Service(), KoinComponent {
|
||||||
notificationManager.onServiceDestroyed()
|
notificationManager.onServiceDestroyed()
|
||||||
transport = null
|
transport = null
|
||||||
mIsRunning.value = false
|
mIsRunning.value = false
|
||||||
|
runBlocking {
|
||||||
|
// This is a hack for `adb shell bmgr backupnow`. Better would be a foreground service,
|
||||||
|
// but since this isn't a typical use-case we don't bother for now.
|
||||||
|
appBackupManager.finalizeBackupIfNeeded()
|
||||||
|
}
|
||||||
Log.d(TAG, "Service destroyed.")
|
Log.d(TAG, "Service destroyed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,9 +126,14 @@ internal class BackupCoordinator(
|
||||||
* otherwise for key-value backup.
|
* otherwise for key-value backup.
|
||||||
* @return Current limit on backup size in bytes.
|
* @return Current limit on backup size in bytes.
|
||||||
*/
|
*/
|
||||||
fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long {
|
suspend fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long {
|
||||||
// report back quota
|
|
||||||
Log.i(TAG, "Get backup quota for $packageName. Is full backup: $isFullBackup.")
|
Log.i(TAG, "Get backup quota for $packageName. Is full backup: $isFullBackup.")
|
||||||
|
|
||||||
|
if (!isFullBackup) {
|
||||||
|
// hack for `adb shell bmgr backupnow`
|
||||||
|
// which starts with a K/V backup calling this method, so we hook in here
|
||||||
|
appBackupManager.ensureBackupPrepared()
|
||||||
|
}
|
||||||
val quota = settingsManager.quota
|
val quota = settingsManager.quota
|
||||||
Log.i(TAG, "Reported quota of $quota bytes.")
|
Log.i(TAG, "Reported quota of $quota bytes.")
|
||||||
return quota
|
return quota
|
||||||
|
|
|
@ -85,6 +85,10 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
val quota = Random.nextLong()
|
val quota = Random.nextLong()
|
||||||
|
|
||||||
every { settingsManager.quota } returns quota
|
every { settingsManager.quota } returns quota
|
||||||
|
if (!isFullBackup) { // hack for `adb shell bmgr` which starts with a K/V backup
|
||||||
|
coEvery { appBackupManager.ensureBackupPrepared() } just Runs
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(quota, backup.getBackupQuota(packageInfo.packageName, isFullBackup))
|
assertEquals(quota, backup.getBackupQuota(packageInfo.packageName, isFullBackup))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue