Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e5a1c22 authored by Ruslan Tkhakokhov's avatar Ruslan Tkhakokhov
Browse files

[FSD2D] Add migration-aware methods to AppBackupUtils

This CL will be part of a series to implement backup / restore of all
app data during a device-to-device migration. Detailed overview of the changes at go/br-fsd2d-design-doc-app-data.

Bug: 160407842
Test: atest AppBackupUtilsTest
Change-Id: I35ce627f1d372437d8ba8495029c6df15db31552
parent c6a61b2e
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app.backup;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -34,6 +35,8 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;

/**
@@ -195,6 +198,19 @@ public class BackupManager {
    @SystemApi
    public static final int ERROR_TRANSPORT_INVALID = -2;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
        OperationType.BACKUP,
        OperationType.MIGRATION
    })
    public @interface OperationType {
        // A regular backup / restore operation.
        int BACKUP = 0;
        // A full migration: all app data for non-system apps is eligible.
        int MIGRATION = 1;
    }

    private Context mContext;
    @UnsupportedAppUsage
    private static IBackupManager sService;
+33 −3
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_A
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.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -66,14 +67,23 @@ public class AppBackupUtils {
     */
    public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) {
        return appIsEligibleForBackup(
                app, LocalServices.getService(PackageManagerInternal.class), userId);
                app, LocalServices.getService(PackageManagerInternal.class), userId,
                OperationType.BACKUP);
    }

    public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId,
            @OperationType int operationType) {
        return appIsEligibleForBackup(
                app, LocalServices.getService(PackageManagerInternal.class), userId, operationType);
    }

    @VisibleForTesting
    static boolean appIsEligibleForBackup(
            ApplicationInfo app, PackageManagerInternal packageManager, int userId) {
            ApplicationInfo app, PackageManagerInternal packageManager, int userId,
            @OperationType int operationType) {
        // 1. their manifest states android:allowBackup="false"
        if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
        boolean appAllowsBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
        if (!appAllowsBackup && !forceFullBackup(app.uid, operationType)) {
            return false;
        }

@@ -189,6 +199,16 @@ public class AppBackupUtils {
     * policy!
     */
    public static boolean appGetsFullBackup(PackageInfo pkg) {
        return appGetsFullBackup(pkg, OperationType.BACKUP);
    }

    @VisibleForTesting
    public static boolean appGetsFullBackup(PackageInfo pkg, @OperationType int operationType) {
        if (forceFullBackup(pkg.applicationInfo.uid, operationType)) {
            // If this is a migration, all non-system packages get full backup.
            return true;
        }

        if (pkg.applicationInfo.backupAgentName != null) {
            // If it has an agent, it gets full backups only if it says so
            return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
@@ -198,6 +218,16 @@ public class AppBackupUtils {
        return true;
    }

    public static boolean appIgnoresIncludeExcludeRules(ApplicationInfo app,
            @OperationType int operationType) {
        return forceFullBackup(app.uid, operationType);
    }

    private static boolean forceFullBackup(int appUid, @OperationType int operationType) {
        return operationType == OperationType.MIGRATION &&
                !UserHandle.isCore(appUid);
    }

    /**
     * Returns whether the app is only capable of doing key/value. We say it's not if it allows full
     * backup, and it is otherwise.
+116 −12
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.app.backup.BackupManager;
import android.app.backup.BackupManager.OperationType;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -74,7 +76,7 @@ public class AppBackupUtilsTest {
        applicationInfo.packageName = TEST_PACKAGE_NAME;

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isFalse();
    }
@@ -89,7 +91,7 @@ public class AppBackupUtilsTest {
        applicationInfo.packageName = TEST_PACKAGE_NAME;

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isFalse();
    }
@@ -103,7 +105,7 @@ public class AppBackupUtilsTest {
        applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isFalse();
    }
@@ -120,7 +122,7 @@ public class AppBackupUtilsTest {
                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isTrue();
    }
@@ -137,7 +139,7 @@ public class AppBackupUtilsTest {
                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isTrue();
    }
@@ -154,7 +156,7 @@ public class AppBackupUtilsTest {
                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isTrue();
    }
@@ -171,7 +173,7 @@ public class AppBackupUtilsTest {
                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isFalse();
    }
@@ -188,7 +190,7 @@ public class AppBackupUtilsTest {
                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isFalse();
    }
@@ -205,7 +207,31 @@ public class AppBackupUtilsTest {
                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId);
                mMockPackageManagerInternal, mUserId, OperationType.BACKUP);

        assertThat(isEligible).isFalse();
    }

    @Test
    public void appIsEligibleForBackup_backupNotAllowedAndInMigration_returnsTrue()
            throws Exception {
        ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
                /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId, OperationType.MIGRATION);

        assertThat(isEligible).isTrue();
    }

    @Test
    public void appIsEligibleForBackup_backupNotAllowedForSystemAppAndInMigration_returnsFalse()
            throws Exception {
        ApplicationInfo applicationInfo = getApplicationInfo(Process.SYSTEM_UID,
                /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME);

        boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                mMockPackageManagerInternal, mUserId, OperationType.MIGRATION);

        assertThat(isEligible).isFalse();
    }
@@ -337,7 +363,7 @@ public class AppBackupUtilsTest {
        packageInfo.applicationInfo = new ApplicationInfo();
        packageInfo.applicationInfo.backupAgentName = null;

        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo, OperationType.BACKUP);

        assertThat(result).isTrue();
    }
@@ -350,7 +376,7 @@ public class AppBackupUtilsTest {
        packageInfo.applicationInfo.backupAgentName = "backup.agent";
        packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;

        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo, OperationType.BACKUP);

        assertThat(result).isTrue();
    }
@@ -363,7 +389,31 @@ public class AppBackupUtilsTest {
        packageInfo.applicationInfo.backupAgentName = "backup.agent";
        packageInfo.applicationInfo.flags = ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;

        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo);
        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo, OperationType.BACKUP);

        assertThat(result).isFalse();
    }

    @Test
    public void appGetsFullBackup_withCustomBackupAgentAndWithoutFullBackupOnlyFlagAndInMigration_returnsTrue()
            throws Exception {
        PackageInfo packageInfo = new PackageInfo();
        packageInfo.applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
                ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY, CUSTOM_BACKUP_AGENT_NAME);

        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo, OperationType.MIGRATION);

        assertThat(result).isTrue();
    }

    @Test
    public void appGetsFullBackup_systemAppWithCustomBackupAgentAndWithoutFullBackupOnlyFlagAndInMigration_returnsFalse()
            throws Exception {
        PackageInfo packageInfo = new PackageInfo();
        packageInfo.applicationInfo = getApplicationInfo(Process.SYSTEM_UID,
                ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY, CUSTOM_BACKUP_AGENT_NAME);

        boolean result = AppBackupUtils.appGetsFullBackup(packageInfo, OperationType.MIGRATION);

        assertThat(result).isFalse();
    }
@@ -405,6 +455,50 @@ public class AppBackupUtilsTest {
        assertThat(result).isTrue();
    }

    @Test
    public void appIgnoresIncludeExcludeRules_systemAppAndInMigration_returnsFalse() {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.uid = Process.SYSTEM_UID;

        boolean result = AppBackupUtils.appIgnoresIncludeExcludeRules(applicationInfo,
                OperationType.MIGRATION);

        assertThat(result).isFalse();
    }

    @Test
    public void appIgnoresIncludeExcludeRules_systemAppInBackup_returnsFalse() {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.uid = Process.SYSTEM_UID;

        boolean result = AppBackupUtils.appIgnoresIncludeExcludeRules(applicationInfo,
                OperationType.BACKUP);

        assertThat(result).isFalse();
    }

    @Test
    public void appIgnoresIncludeExcludeRules_nonSystemAppInMigration_returnsTrue() {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.uid = Process.FIRST_APPLICATION_UID;

        boolean result = AppBackupUtils.appIgnoresIncludeExcludeRules(applicationInfo,
                OperationType.MIGRATION);

        assertThat(result).isTrue();
    }

    @Test
    public void appIgnoresIncludeExcludeRules_nonSystemInBackup_returnsFalse() {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.uid = Process.FIRST_APPLICATION_UID;

        boolean result = AppBackupUtils.appIgnoresIncludeExcludeRules(applicationInfo,
                OperationType.BACKUP);

        assertThat(result).isFalse();
    }

    @Test
    public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
        boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null,
@@ -693,4 +787,14 @@ public class AppBackupUtilsTest {
        signatureBytes[0] = i;
        return new Signature(signatureBytes);
    }

    private static ApplicationInfo getApplicationInfo(int appUid, int flags,
            String backupAgentName) {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.flags = 0;
        applicationInfo.packageName = TEST_PACKAGE_NAME;
        applicationInfo.uid = appUid;
        applicationInfo.backupAgentName = backupAgentName;
        return applicationInfo;
    }
}