diff options
| author | 2020-12-04 12:41:11 +0000 | |
|---|---|---|
| committer | 2020-12-04 20:57:52 +0000 | |
| commit | 735cbd5ad026781b1f65b657eb1bbba53a25626c (patch) | |
| tree | 8fe7b23828d5e757e54706d107b751f111d73c79 | |
| parent | 1447bab54ada0c1f552c3528fe5e0a5d492484e0 (diff) | |
Update eligibility rules for adb backup
1. Adb backup is enabled by default for "android" package that corresponds to SystemBackupAgent (no change from current behavior)
2. System and privileged apps can use android.backup.ALLOW_ADB_BACKUP manifest property to enable / disable adb backup. Disabled by default.
3. Other apps can only use adb backup when running in debuggable mode.
Bug: 171032338
Test: 1. atest BackupEligibilityRulesTest
2.1. Run adb backup / restore for SystemBackupAgent and verify
success (running in system_server)
2.2. Run adb backup / restore for NexusLauncher with
"allowAdbBackup=true" and verify success (privileged app)
2.3. Run adb backup / restore for a non-privileged debuggable app
and verify success.
Change-Id: Ifefe6d888377d3ac9482928b27c86b2e562ad8fa
8 files changed, 180 insertions, 17 deletions
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 44a4b78e1afa..673de8fa7c8c 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -205,13 +205,16 @@ public class BackupManager { @Retention(RetentionPolicy.SOURCE) @IntDef({ OperationType.BACKUP, - OperationType.MIGRATION + OperationType.MIGRATION, + OperationType.ADB_BACKUP, }) public @interface OperationType { - // A regular backup / restore operation. + // A backup / restore to / from an off-device location, e.g. cloud. int BACKUP = 0; - // A full migration: all app data for non-system apps is eligible. + // A direct transfer to another device. int MIGRATION = 1; + // Backup via adb, data saved on the host machine. + int ADB_BACKUP = 3; } private Context mContext; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 19e4d148aee8..d550c7c68033 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4131,6 +4131,14 @@ public abstract class PackageManager { */ public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; + /** + * A manifest property to control app's participation in {@code adb backup}. Should only + * be used by system / privileged apps. + * + * @hide + */ + public static final String PROPERTY_ALLOW_ADB_BACKUP = "android.backup.ALLOW_ADB_BACKUP"; + /** {@hide} */ public int getUserId() { return UserHandle.myUserId(); diff --git a/services/backup/Android.bp b/services/backup/Android.bp index b5444f485a1c..68376c5ad178 100644 --- a/services/backup/Android.bp +++ b/services/backup/Android.bp @@ -10,5 +10,5 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.backup-sources"], libs: ["services.core"], - static_libs: ["backuplib"], + static_libs: ["backuplib", "app-compat-annotations"], } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 2ff66b564ec9..136cd22fad83 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -3030,9 +3030,11 @@ public class UserBackupManagerService { } Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup...")); + BackupEligibilityRules eligibilityRules = getEligibilityRulesForOperation( + OperationType.ADB_BACKUP); AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs, includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue, - pkgList, mScheduledBackupEligibility); + pkgList, eligibilityRules); final int token = generateRandomIntegerToken(); synchronized (mAdbBackupRestoreConfirmations) { mAdbBackupRestoreConfirmations.put(token, params); diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index c94286ffcffa..e03150eb824f 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -24,6 +24,7 @@ import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC; import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION; +import android.app.backup.BackupManager; import android.app.backup.IFullBackupRestoreObserver; import android.content.pm.PackageManagerInternal; import android.os.ParcelFileDescriptor; @@ -104,11 +105,13 @@ public class PerformAdbRestoreTask implements Runnable { return; } + BackupEligibilityRules eligibilityRules = new BackupEligibilityRules( + mBackupManagerService.getPackageManager(), + LocalServices.getService(PackageManagerInternal.class), + mBackupManagerService.getUserId(), BackupManager.OperationType.ADB_BACKUP); FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null, mObserver, null, null, true, 0 /*unused*/, true, - BackupEligibilityRules.forBackup(mBackupManagerService.getPackageManager(), - LocalServices.getService(PackageManagerInternal.class), - mBackupManagerService.getUserId())); + eligibilityRules); FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine, tarInputStream); mEngineThread.run(); diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java index 73ba1f19c092..2078492e67ab 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java +++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java @@ -25,12 +25,16 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; import android.app.backup.BackupManager.OperationType; import android.app.backup.BackupTransport; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.app.compat.CompatChanges; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningInfo; +import android.os.Build; import android.os.UserHandle; import android.util.Slog; @@ -41,6 +45,7 @@ import com.android.server.backup.transport.TransportClient; import com.google.android.collect.Sets; +import java.util.Objects; import java.util.Set; /** @@ -57,6 +62,15 @@ public class BackupEligibilityRules { private final int mUserId; @OperationType private final int mOperationType; + /** + * When this change is enabled, {@code adb backup} is automatically turned on for apps + * running as debuggable ({@code android:debuggable} set to {@code true}) and unavailable to + * any other apps. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + static final long RESTRICT_ADB_BACKUP = 171032338L; + public static BackupEligibilityRules forBackup(PackageManager packageManager, PackageManagerInternal packageManagerInternal, int userId) { @@ -134,12 +148,53 @@ public class BackupEligibilityRules { * @return boolean indicating whether backup is allowed. */ public boolean isAppBackupAllowed(ApplicationInfo app) { - if (mOperationType == OperationType.MIGRATION && !UserHandle.isCore(app.uid)) { - // Backup / restore of all apps is force allowed during device-to-device migration. - return true; - } + boolean isSystemApp = UserHandle.isCore(app.uid); + boolean allowBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; + switch (mOperationType) { + case OperationType.MIGRATION: + // Backup / restore of all non-system apps is force allowed during + // device-to-device migration. + return !isSystemApp || allowBackup; + case OperationType.ADB_BACKUP: + String packageName = app.packageName; + if (packageName == null) { + Slog.w(TAG, "Invalid ApplicationInfo object"); + return false; + } + + if (!CompatChanges.isChangeEnabled(RESTRICT_ADB_BACKUP, packageName, + UserHandle.of(mUserId))) { + return allowBackup; + } + + if (PLATFORM_PACKAGE_NAME.equals(packageName)) { + // Always enable adb backup for SystemBackupAgent in "android" package (this is + // done to avoid breaking existing integration tests and might change in the + // future). + return true; + } - return (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0; + boolean isPrivileged = (app.flags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; + boolean isDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + if (isSystemApp || isPrivileged) { + try { + return mPackageManager.getProperty(PackageManager.PROPERTY_ALLOW_ADB_BACKUP, + packageName).getBoolean(); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Failed to read allowAdbBackup property for + " + + packageName); + return false; + } + } else { + // All other apps can use adb backup only when running in debuggable mode. + return isDebuggable; + } + case OperationType.BACKUP: + return allowBackup; + default: + Slog.w(TAG, "Unknown operation type:" + mOperationType); + return false; + } } /** diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 343b156e443a..78b471b7d66c 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -33,6 +33,7 @@ android_test { "androidx.test.ext.truth", "androidx.test.runner", "androidx.test.rules", + "platform-compat-test-rules", "mockito-target-minus-junit4", "platform-test-annotations", "ShortcutManagerTestUtils", diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java index 444155d12b3f..738527e1df0b 100644 --- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java @@ -18,14 +18,17 @@ package com.android.server.backup.utils; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.backup.BackupManager.OperationType; +import android.compat.testing.PlatformCompatChangeRule; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.Signature; @@ -38,9 +41,15 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.backup.UserBackupManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,17 +59,19 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class BackupEligibilityRulesTest { private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent"; - private static final String TEST_PACKAGE_NAME = "test_package"; + private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests"; private static final Signature SIGNATURE_1 = generateSignature((byte) 1); private static final Signature SIGNATURE_2 = generateSignature((byte) 2); private static final Signature SIGNATURE_3 = generateSignature((byte) 3); private static final Signature SIGNATURE_4 = generateSignature((byte) 4); + @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); + @Mock private PackageManagerInternal mMockPackageManagerInternal; @Mock private PackageManager mPackageManager; - private BackupEligibilityRules mBackupEligibilityRules; + private BackupEligibilityRules mBackupEligibilityRules; private int mUserId; @Before @@ -225,7 +236,6 @@ public class BackupEligibilityRulesTest { throws Exception { ApplicationInfo applicationInfo = getApplicationInfo(Process.SYSTEM_UID, /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME); - BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( OperationType.MIGRATION); boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); @@ -234,6 +244,82 @@ public class BackupEligibilityRulesTest { } @Test + @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_adbBackupNotAllowed_returnsFalseForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + when(mPackageManager.getProperty(eq(PackageManager.PROPERTY_ALLOW_ADB_BACKUP), + eq(TEST_PACKAGE_NAME))).thenReturn(getAdbBackupProperty( + /* allowAdbBackup */ false)); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isFalse(); + } + + @Test + @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_adbBackupAllowed_returnsTrueForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + when(mPackageManager.getProperty(eq(PackageManager.PROPERTY_ALLOW_ADB_BACKUP), + eq(TEST_PACKAGE_NAME))).thenReturn(getAdbBackupProperty( + /* allowAdbBackup */ true)); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_debuggableNonPrivilegedApp_returnsTrueForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ ApplicationInfo.FLAG_DEBUGGABLE, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + @DisableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_allowBackupTrueBeforeS_returnsTrueForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + ApplicationInfo.FLAG_ALLOW_BACKUP, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isTrue(); + } + + @Test + @DisableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP}) + public void appIsEligibleForBackup_allowBackupFalseBeforeS_returnsFalseForAdbBackup() + throws Exception { + ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID, + /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME); + BackupEligibilityRules eligibilityRules = getBackupEligibilityRules( + OperationType.ADB_BACKUP); + + boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo); + + assertThat(isEligible).isFalse(); + } + + @Test public void appIsDisabled_stateDefaultManifestEnabled_returnsFalse() throws Exception { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.flags = 0; @@ -789,10 +875,15 @@ public class BackupEligibilityRulesTest { private static ApplicationInfo getApplicationInfo(int appUid, int flags, String backupAgentName) { ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.flags = 0; + applicationInfo.flags = flags; applicationInfo.packageName = TEST_PACKAGE_NAME; applicationInfo.uid = appUid; applicationInfo.backupAgentName = backupAgentName; return applicationInfo; } + + private static Property getAdbBackupProperty(boolean allowAdbBackup) { + return new Property(PackageManager.PROPERTY_ALLOW_ADB_BACKUP, allowAdbBackup, + TEST_PACKAGE_NAME, /* className */ ""); + } } |