Preserve backups when disabling "Backup my apps"
Ignore AOSP's attempt to wipe backup data when backups are disabled. Remaining quirks: * When backups are re-enabled and another backup is started, the system will call initializeDevice, and Seedvault will generate a new backup salt and start the backup from scratch. This was already the case, and this patch does not change that. Issue: seedvault-app/seedvault#476 Change-Id: I6ab41a885fcf7c4143814ebe849b8263a4f6e595
This commit is contained in:
parent
271b9a560f
commit
a0e3474783
4 changed files with 69 additions and 22 deletions
|
|
@ -10,6 +10,7 @@ import android.app.backup.BackupTransport.TRANSPORT_NOT_INITIALIZED
|
|||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
||||
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
||||
import android.app.backup.IBackupManager
|
||||
import android.app.backup.RestoreSet
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
|
|
@ -72,6 +73,7 @@ internal class BackupCoordinator(
|
|||
private val metadataManager: MetadataManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val nm: BackupNotificationManager,
|
||||
private val backupManager: IBackupManager,
|
||||
) {
|
||||
|
||||
private val state = CoordinatorState(
|
||||
|
|
@ -115,7 +117,10 @@ internal class BackupCoordinator(
|
|||
* @return One of [TRANSPORT_OK] (OK so far) or
|
||||
* [TRANSPORT_ERROR] (to retry following network error or other failure).
|
||||
*/
|
||||
suspend fun initializeDevice(): Int = try {
|
||||
suspend fun initializeDevice(): Int {
|
||||
if (shouldIgnoreBackupManager("initializeDevice")) return TRANSPORT_OK
|
||||
|
||||
return try {
|
||||
val token = settingsManager.getToken()
|
||||
if (token == null) {
|
||||
Log.i(TAG, "No RestoreSet started, initialization is no-op.")
|
||||
|
|
@ -134,9 +139,12 @@ internal class BackupCoordinator(
|
|||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error initializing device", e)
|
||||
// Show error notification if we needed init or were ready for backups
|
||||
if (metadataManager.requiresInit || settingsManager.canDoBackupNow()) nm.onBackupError()
|
||||
if (metadataManager.requiresInit || settingsManager.canDoBackupNow()) {
|
||||
nm.onBackupError()
|
||||
}
|
||||
TRANSPORT_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
fun isAppEligibleForBackup(
|
||||
targetPackage: PackageInfo,
|
||||
|
|
@ -232,6 +240,8 @@ internal class BackupCoordinator(
|
|||
data: ParcelFileDescriptor,
|
||||
flags: Int,
|
||||
): Int {
|
||||
if (shouldIgnoreBackupManager("performIncrementalBackup")) return TRANSPORT_OK
|
||||
|
||||
state.cancelReason = UNKNOWN_ERROR
|
||||
if (metadataManager.requiresInit) {
|
||||
// start a new restore set to upgrade from legacy format
|
||||
|
|
@ -282,6 +292,8 @@ internal class BackupCoordinator(
|
|||
fileDescriptor: ParcelFileDescriptor,
|
||||
flags: Int,
|
||||
): Int {
|
||||
if (shouldIgnoreBackupManager("performFullBackup")) return TRANSPORT_OK
|
||||
|
||||
state.cancelReason = UNKNOWN_ERROR
|
||||
val token = settingsManager.getToken() ?: error("no token in performFullBackup")
|
||||
val salt = metadataManager.salt
|
||||
|
|
@ -304,6 +316,8 @@ internal class BackupCoordinator(
|
|||
* It needs to tear down any ongoing backup state here.
|
||||
*/
|
||||
suspend fun cancelFullBackup() {
|
||||
if (shouldIgnoreBackupManager("cancelFullBackup")) return
|
||||
|
||||
val packageInfo = full.getCurrentPackage()
|
||||
?: throw AssertionError("Cancelling full backup, but no current package")
|
||||
Log.i(
|
||||
|
|
@ -359,6 +373,7 @@ internal class BackupCoordinator(
|
|||
* @return the same error codes as [performIncrementalBackup] or [performFullBackup].
|
||||
*/
|
||||
suspend fun finishBackup(): Int = when {
|
||||
shouldIgnoreBackupManager("finishBackup") -> TRANSPORT_OK
|
||||
kv.hasState() -> {
|
||||
check(!full.hasState()) {
|
||||
"K/V backup has state, but full backup has dangling state as well"
|
||||
|
|
@ -510,4 +525,20 @@ internal class BackupCoordinator(
|
|||
return getOutputStream(t, FILE_BACKUP_METADATA)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not backup is enabled, logging a warning if it is not.
|
||||
* When backup is disabled, the system treats this as an opt out and attempts to start
|
||||
* a wipe of backup data. (See frameworks/base/services/backup/java/com/android/server/backup/
|
||||
* UserBackupManagerService.java, lines 3195-3197, tag android-13.0.0_r8.)
|
||||
* This check exists to ensure that backups are not altered when backup is turned off.
|
||||
*
|
||||
* @param methodName the calling method to be included in logging.
|
||||
* @return true if the backup service is disabled, and false if it is not.
|
||||
*/
|
||||
private fun shouldIgnoreBackupManager(methodName: String): Boolean =
|
||||
if (!backupManager.isBackupEnabled) {
|
||||
Log.w(TAG, "Ignoring call to $methodName while backup is not enabled")
|
||||
true
|
||||
} else false
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ val backupModule = module {
|
|||
packageService = get(),
|
||||
metadataManager = get(),
|
||||
settingsManager = get(),
|
||||
nm = get()
|
||||
nm = get(),
|
||||
backupManager = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.app.backup.BackupDataInput
|
|||
import android.app.backup.BackupDataOutput
|
||||
import android.app.backup.BackupTransport.NO_MORE_DATA
|
||||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||
import android.app.backup.IBackupManager
|
||||
import android.app.backup.RestoreDescription
|
||||
import android.app.backup.RestoreDescription.TYPE_FULL_STREAM
|
||||
import android.os.ParcelFileDescriptor
|
||||
|
|
@ -68,6 +69,12 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
private val fullBackup = FullBackup(backupPlugin, settingsManager, inputFactory, cryptoImpl)
|
||||
private val apkBackup = mockk<ApkBackup>()
|
||||
private val packageService: PackageService = mockk()
|
||||
private val backupManager: IBackupManager = mockk()
|
||||
|
||||
init {
|
||||
every { backupManager.isBackupEnabled } returns true
|
||||
}
|
||||
|
||||
private val backup = BackupCoordinator(
|
||||
context,
|
||||
backupPlugin,
|
||||
|
|
@ -78,7 +85,8 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
|||
packageService,
|
||||
metadataManager,
|
||||
settingsManager,
|
||||
notificationManager
|
||||
notificationManager,
|
||||
backupManager,
|
||||
)
|
||||
|
||||
private val kvRestore = KVRestore(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.app.backup.BackupTransport.TRANSPORT_NOT_INITIALIZED
|
|||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
||||
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
||||
import android.app.backup.IBackupManager
|
||||
import android.content.pm.ApplicationInfo.FLAG_STOPPED
|
||||
import android.content.pm.PackageInfo
|
||||
import android.net.Uri
|
||||
|
|
@ -48,6 +49,11 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
private val apkBackup = mockk<ApkBackup>()
|
||||
private val packageService: PackageService = mockk()
|
||||
private val notificationManager = mockk<BackupNotificationManager>()
|
||||
private val backupManager = mockk<IBackupManager>()
|
||||
|
||||
init {
|
||||
every { backupManager.isBackupEnabled } returns true
|
||||
}
|
||||
|
||||
private val backup = BackupCoordinator(
|
||||
context,
|
||||
|
|
@ -59,7 +65,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
packageService,
|
||||
metadataManager,
|
||||
settingsManager,
|
||||
notificationManager
|
||||
notificationManager,
|
||||
backupManager,
|
||||
)
|
||||
|
||||
private val metadataOutputStream = mockk<OutputStream>()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue