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_OK
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
||||||
|
import android.app.backup.IBackupManager
|
||||||
import android.app.backup.RestoreSet
|
import android.app.backup.RestoreSet
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
|
|
@ -72,6 +73,7 @@ internal class BackupCoordinator(
|
||||||
private val metadataManager: MetadataManager,
|
private val metadataManager: MetadataManager,
|
||||||
private val settingsManager: SettingsManager,
|
private val settingsManager: SettingsManager,
|
||||||
private val nm: BackupNotificationManager,
|
private val nm: BackupNotificationManager,
|
||||||
|
private val backupManager: IBackupManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val state = CoordinatorState(
|
private val state = CoordinatorState(
|
||||||
|
|
@ -115,7 +117,10 @@ internal class BackupCoordinator(
|
||||||
* @return One of [TRANSPORT_OK] (OK so far) or
|
* @return One of [TRANSPORT_OK] (OK so far) or
|
||||||
* [TRANSPORT_ERROR] (to retry following network error or other failure).
|
* [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()
|
val token = settingsManager.getToken()
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
Log.i(TAG, "No RestoreSet started, initialization is no-op.")
|
Log.i(TAG, "No RestoreSet started, initialization is no-op.")
|
||||||
|
|
@ -134,9 +139,12 @@ internal class BackupCoordinator(
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "Error initializing device", e)
|
Log.e(TAG, "Error initializing device", e)
|
||||||
// Show error notification if we needed init or were ready for backups
|
// 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
|
TRANSPORT_ERROR
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun isAppEligibleForBackup(
|
fun isAppEligibleForBackup(
|
||||||
targetPackage: PackageInfo,
|
targetPackage: PackageInfo,
|
||||||
|
|
@ -232,6 +240,8 @@ internal class BackupCoordinator(
|
||||||
data: ParcelFileDescriptor,
|
data: ParcelFileDescriptor,
|
||||||
flags: Int,
|
flags: Int,
|
||||||
): Int {
|
): Int {
|
||||||
|
if (shouldIgnoreBackupManager("performIncrementalBackup")) return TRANSPORT_OK
|
||||||
|
|
||||||
state.cancelReason = UNKNOWN_ERROR
|
state.cancelReason = UNKNOWN_ERROR
|
||||||
if (metadataManager.requiresInit) {
|
if (metadataManager.requiresInit) {
|
||||||
// start a new restore set to upgrade from legacy format
|
// start a new restore set to upgrade from legacy format
|
||||||
|
|
@ -282,6 +292,8 @@ internal class BackupCoordinator(
|
||||||
fileDescriptor: ParcelFileDescriptor,
|
fileDescriptor: ParcelFileDescriptor,
|
||||||
flags: Int,
|
flags: Int,
|
||||||
): Int {
|
): Int {
|
||||||
|
if (shouldIgnoreBackupManager("performFullBackup")) return TRANSPORT_OK
|
||||||
|
|
||||||
state.cancelReason = UNKNOWN_ERROR
|
state.cancelReason = UNKNOWN_ERROR
|
||||||
val token = settingsManager.getToken() ?: error("no token in performFullBackup")
|
val token = settingsManager.getToken() ?: error("no token in performFullBackup")
|
||||||
val salt = metadataManager.salt
|
val salt = metadataManager.salt
|
||||||
|
|
@ -304,6 +316,8 @@ internal class BackupCoordinator(
|
||||||
* It needs to tear down any ongoing backup state here.
|
* It needs to tear down any ongoing backup state here.
|
||||||
*/
|
*/
|
||||||
suspend fun cancelFullBackup() {
|
suspend fun cancelFullBackup() {
|
||||||
|
if (shouldIgnoreBackupManager("cancelFullBackup")) return
|
||||||
|
|
||||||
val packageInfo = full.getCurrentPackage()
|
val packageInfo = full.getCurrentPackage()
|
||||||
?: throw AssertionError("Cancelling full backup, but no current package")
|
?: throw AssertionError("Cancelling full backup, but no current package")
|
||||||
Log.i(
|
Log.i(
|
||||||
|
|
@ -359,6 +373,7 @@ internal class BackupCoordinator(
|
||||||
* @return the same error codes as [performIncrementalBackup] or [performFullBackup].
|
* @return the same error codes as [performIncrementalBackup] or [performFullBackup].
|
||||||
*/
|
*/
|
||||||
suspend fun finishBackup(): Int = when {
|
suspend fun finishBackup(): Int = when {
|
||||||
|
shouldIgnoreBackupManager("finishBackup") -> TRANSPORT_OK
|
||||||
kv.hasState() -> {
|
kv.hasState() -> {
|
||||||
check(!full.hasState()) {
|
check(!full.hasState()) {
|
||||||
"K/V backup has state, but full backup has dangling state as well"
|
"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)
|
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(),
|
packageService = get(),
|
||||||
metadataManager = get(),
|
metadataManager = get(),
|
||||||
settingsManager = 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.BackupDataOutput
|
||||||
import android.app.backup.BackupTransport.NO_MORE_DATA
|
import android.app.backup.BackupTransport.NO_MORE_DATA
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||||
|
import android.app.backup.IBackupManager
|
||||||
import android.app.backup.RestoreDescription
|
import android.app.backup.RestoreDescription
|
||||||
import android.app.backup.RestoreDescription.TYPE_FULL_STREAM
|
import android.app.backup.RestoreDescription.TYPE_FULL_STREAM
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
|
|
@ -68,6 +69,12 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
private val fullBackup = FullBackup(backupPlugin, settingsManager, inputFactory, cryptoImpl)
|
private val fullBackup = FullBackup(backupPlugin, settingsManager, inputFactory, cryptoImpl)
|
||||||
private val apkBackup = mockk<ApkBackup>()
|
private val apkBackup = mockk<ApkBackup>()
|
||||||
private val packageService: PackageService = mockk()
|
private val packageService: PackageService = mockk()
|
||||||
|
private val backupManager: IBackupManager = mockk()
|
||||||
|
|
||||||
|
init {
|
||||||
|
every { backupManager.isBackupEnabled } returns true
|
||||||
|
}
|
||||||
|
|
||||||
private val backup = BackupCoordinator(
|
private val backup = BackupCoordinator(
|
||||||
context,
|
context,
|
||||||
backupPlugin,
|
backupPlugin,
|
||||||
|
|
@ -78,7 +85,8 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
packageService,
|
packageService,
|
||||||
metadataManager,
|
metadataManager,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
notificationManager
|
notificationManager,
|
||||||
|
backupManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val kvRestore = KVRestore(
|
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_OK
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
||||||
|
import android.app.backup.IBackupManager
|
||||||
import android.content.pm.ApplicationInfo.FLAG_STOPPED
|
import android.content.pm.ApplicationInfo.FLAG_STOPPED
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
@ -48,6 +49,11 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
private val apkBackup = mockk<ApkBackup>()
|
private val apkBackup = mockk<ApkBackup>()
|
||||||
private val packageService: PackageService = mockk()
|
private val packageService: PackageService = mockk()
|
||||||
private val notificationManager = mockk<BackupNotificationManager>()
|
private val notificationManager = mockk<BackupNotificationManager>()
|
||||||
|
private val backupManager = mockk<IBackupManager>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
every { backupManager.isBackupEnabled } returns true
|
||||||
|
}
|
||||||
|
|
||||||
private val backup = BackupCoordinator(
|
private val backup = BackupCoordinator(
|
||||||
context,
|
context,
|
||||||
|
|
@ -59,7 +65,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
packageService,
|
packageService,
|
||||||
metadataManager,
|
metadataManager,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
notificationManager
|
notificationManager,
|
||||||
|
backupManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val metadataOutputStream = mockk<OutputStream>()
|
private val metadataOutputStream = mockk<OutputStream>()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue