Merge "Update eligibility rules for adb backup"

This commit is contained in:
Ruslan Tkhakokhov
2020-12-07 14:10:38 +00:00
committed by Android (Google) Code Review
8 changed files with 180 additions and 17 deletions

View File

@@ -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;

View File

@@ -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();

View File

@@ -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"],
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;
}
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;
}
}
/**

View File

@@ -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",

View File

@@ -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);
@@ -233,6 +243,82 @@ public class BackupEligibilityRulesTest {
assertThat(isEligible).isFalse();
}
@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();
@@ -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 */ "");
}
}