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
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 44a4b78..673de8f 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -205,13 +205,16 @@
     @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 19e4d14..d550c7c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4131,6 +4131,14 @@
      */
     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 b5444f4..68376c5 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -10,5 +10,5 @@
     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 2ff66b5..136cd22f 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 @@
             }
             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 c94286f..e03150e 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.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 @@
                 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 73ba1f1..2078492 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 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.google.android.collect.Sets;
 
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -57,6 +62,15 @@
     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 @@
     * @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;
+                }
 
-        return (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
+                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;
+                }
+
+                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 343b156..78b471b 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -33,6 +33,7 @@
         "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 444155d..738527e 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 @@
 
 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.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 @@
 @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 @@
             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 @@
     }
 
     @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 @@
     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 */ "");
+    }
 }