summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/backup/BackupTransport.java33
-rw-r--r--core/java/android/content/pm/PackageManager.java36
-rw-r--r--core/java/com/android/internal/backup/IBackupTransport.aidl11
-rw-r--r--services/backup/flags.aconfig9
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java112
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java25
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java24
-rw-r--r--services/backup/java/com/android/server/backup/transport/BackupTransportClient.java22
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java52
-rw-r--r--services/core/java/com/android/server/am/BackupRecord.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java53
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java197
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java108
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java21
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java102
20 files changed, 764 insertions, 55 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 15411f41be30..c6b0034e84ae 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -13569,6 +13569,7 @@ package android.content.pm {
field public static final String PROPERTY_MEDIA_CAPABILITIES = "android.media.PROPERTY_MEDIA_CAPABILITIES";
field public static final String PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES = "android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES";
field public static final String PROPERTY_SPECIAL_USE_FGS_SUBTYPE = "android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE";
+ field @FlaggedApi("com.android.server.backup.enable_restricted_mode_changes") public static final String PROPERTY_USE_RESTRICTED_BACKUP_MODE = "android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE";
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
field public static final int SIGNATURE_MATCH = 0; // 0x0
field public static final int SIGNATURE_NEITHER_SIGNED = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0ea00f552c06..de6b3ae78a56 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1937,6 +1937,7 @@ package android.app.backup {
method public android.os.IBinder getBinder();
method public long getCurrentRestoreSet();
method public int getNextFullRestoreDataChunk(android.os.ParcelFileDescriptor);
+ method @FlaggedApi("com.android.server.backup.enable_restricted_mode_changes") @NonNull public java.util.List<java.lang.String> getPackagesThatShouldNotUseRestrictedMode(@NonNull java.util.List<java.lang.String>, int);
method public int getRestoreData(android.os.ParcelFileDescriptor);
method public int getTransportFlags();
method public int initializeDevice();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 34a3ad19b270..a8412fa66609 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -358,7 +358,7 @@ interface IActivityManager {
@UnsupportedAppUsage
void resumeAppSwitches();
boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId,
- int backupDestination);
+ int backupDestination, boolean useRestrictedMode);
void backupAgentCreated(in String packageName, in IBinder agent, int userId);
void unbindBackupAgent(in ApplicationInfo appInfo);
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index dcac59c19df4..5004c02194ea 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -16,6 +16,8 @@
package android.app.backup;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Intent;
@@ -27,6 +29,7 @@ import android.os.RemoteException;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.ITransportStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import com.android.server.backup.Flags;
import java.util.Arrays;
import java.util.List;
@@ -671,6 +674,22 @@ public class BackupTransport {
}
/**
+ * Ask the transport whether packages that are about to be backed up or restored should not be
+ * put into a restricted mode by the framework and started normally instead.
+ *
+ * @param operationType 0 for backup, 1 for restore.
+ * @return a subset of the {@code packageNames} passed in, indicating
+ * which packages should NOT be put into restricted mode for the given operation type.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public List<String> getPackagesThatShouldNotUseRestrictedMode(
+ @NonNull List<String> packageNames,
+ @BackupAnnotations.OperationType int operationType) {
+ return List.of();
+ }
+
+ /**
* Bridge between the actual IBackupTransport implementation and the stable API. If the
* binder interface needs to change, we use this layer to translate so that we can
* (if appropriate) decouple those framework-side changes from the BackupTransport
@@ -977,5 +996,19 @@ public class BackupTransport {
resultFuture.cancel(/* mayInterruptIfRunning */ true);
}
}
+
+ @Override
+ @FlaggedApi(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void getPackagesThatShouldNotUseRestrictedMode(List<String> packageNames,
+ int operationType, AndroidFuture<List<String>> resultFuture) {
+ try {
+ List<String> result =
+ BackupTransport.this.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ operationType);
+ resultFuture.complete(result);
+ } catch (RuntimeException e) {
+ resultFuture.cancel(/* mayInterruptIfRunning */ true);
+ }
+ }
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 13b13b9e4179..37295ac94823 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -193,6 +193,42 @@ public abstract class PackageManager {
"android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES";
/**
+ * &lt;application&gt; level {@link android.content.pm.PackageManager.Property} tag
+ * specifying whether the app should be put into the "restricted" backup mode when it's started
+ * for backup and restore operations.
+ *
+ * <p> See <a
+ * href="https://developer.android.com/identity/data/autobackup#ImplementingBackupAgent"> for
+ * information about restricted mode</a>.
+ *
+ * <p> Starting with Android 16 apps may not be started in restricted mode based on this
+ * property.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * &lt;application&gt;
+ * &lt;property
+ * android:name="android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE"
+ * android:value="true|false"/&gt;
+ * &lt;/application&gt;
+ * </pre>
+ *
+ * <p>If this property is set, the operating system will respect it for now (see Note below).
+ * If it's not set, the behavior depends on the SDK level that the app is targeting. For apps
+ * targeting SDK level {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or lower, the
+ * property defaults to {@code true}. For apps targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA} or higher, the operating system will make a
+ * decision dynamically.
+ *
+ * <p>Note: It's not recommended to set this property to {@code true} unless absolutely
+ * necessary. In a future Android version, this property may be deprecated in favor of removing
+ * restricted mode completely.
+ */
+ @FlaggedApi(com.android.server.backup.Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public static final String PROPERTY_USE_RESTRICTED_BACKUP_MODE =
+ "android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE";
+
+ /**
* Application level property that an app can specify to opt-out from having private data
* directories both on the internal and external storages.
*
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 21c7baab4e83..469ab48385da 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -410,4 +410,15 @@ oneway interface IBackupTransport {
* however backups initiated by the framework will call this method to retrieve one.
*/
void getBackupManagerMonitor(in AndroidFuture<IBackupManagerMonitor> resultFuture);
+
+ /**
+ * Ask the transport whether packages that are about to be backed up or restored should not be
+ * put into a restricted mode by the framework and started normally instead. The
+ * {@code resultFuture} should be completed with a subset of the packages passed in, indicating
+ * which packages should NOT be put into restricted mode for the given operation type.
+ *
+ * @param operationType 0 for backup, 1 for restore.
+ */
+ void getPackagesThatShouldNotUseRestrictedMode(in List<String> packageNames, int operationType,
+ in AndroidFuture<List<String>> resultFuture);
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index d53f949753d8..fcb7934f7ca0 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -60,3 +60,12 @@ flag {
bug: "331749778"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_restricted_mode_changes"
+ namespace: "onboarding"
+ description: "Enables the new framework behavior of not putting apps in restricted mode for "
+ "B&R operations in certain cases."
+ bug: "376661510"
+ is_fixed_read_only: true
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 466d477992b3..5de2fb30ac78 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -43,6 +43,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.ApplicationThreadConstants;
import android.app.IActivityManager;
import android.app.IBackupAgent;
import android.app.PendingIntent;
@@ -59,6 +60,9 @@ import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -298,6 +302,15 @@ public class UserBackupManagerService {
private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+ /**
+ * Enables the OS making a decision on whether backup restricted mode should be used for apps
+ * that haven't explicitly opted in or out. See
+ * {@link PackageManager#PROPERTY_USE_RESTRICTED_BACKUP_MODE} for details.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
+ public static final long OS_DECIDES_BACKUP_RESTRICTED_MODE = 376661510;
+
// Time delay for initialization operations that can be delayed so as not to consume too much
// CPU on bring-up and increase time-to-UI.
private static final long INITIALIZATION_DELAY_MILLIS = 3000;
@@ -352,6 +365,9 @@ public class UserBackupManagerService {
// Backups that we haven't started yet. Keys are package names.
private final HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+ private final ArraySet<String> mRestoreNoRestrictedModePackages = new ArraySet<>();
+ private final ArraySet<String> mBackupNoRestrictedModePackages = new ArraySet<>();
+
// locking around the pending-backup management
private final Object mQueueLock = new Object();
@@ -523,7 +539,8 @@ public class UserBackupManagerService {
@VisibleForTesting
UserBackupManagerService(Context context, PackageManager packageManager,
LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler, BackupManagerConstants backupManagerConstants) {
+ BackupHandler backupHandler, BackupManagerConstants backupManagerConstants,
+ IActivityManager activityManager, ActivityManagerInternal activityManagerInternal) {
mContext = context;
mUserId = 0;
@@ -534,6 +551,8 @@ public class UserBackupManagerService {
mFullBackupQueue = new ArrayList<>();
mBackupHandler = backupHandler;
mConstants = backupManagerConstants;
+ mActivityManager = activityManager;
+ mActivityManagerInternal = activityManagerInternal;
mBaseStateDir = null;
mDataDir = null;
@@ -543,13 +562,11 @@ public class UserBackupManagerService {
mRunInitReceiver = null;
mRunInitIntent = null;
mAgentTimeoutParameters = null;
- mActivityManagerInternal = null;
mAlarmManager = null;
mWakelock = null;
mBackupPreferences = null;
mBackupPasswordManager = null;
mPackageManagerBinder = null;
- mActivityManager = null;
mBackupManagerBinder = null;
mScheduledBackupEligibility = null;
}
@@ -1651,9 +1668,11 @@ public class UserBackupManagerService {
synchronized (mAgentConnectLock) {
mConnecting = true;
mConnectedAgent = null;
+ boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(mode,
+ app.packageName);
try {
if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId,
- backupDestination)) {
+ backupDestination, useRestrictedMode)) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "awaiting agent for " + app));
// success; wait for the agent to arrive
@@ -3103,6 +3122,91 @@ public class UserBackupManagerService {
}
}
+ /**
+ * Marks the given set of packages as packages that should not be put into restricted mode if
+ * they are started for the given {@link BackupAnnotations.OperationType}.
+ */
+ public void setNoRestrictedModePackages(Set<String> packageNames,
+ @BackupAnnotations.OperationType int opType) {
+ if (opType == BackupAnnotations.OperationType.BACKUP) {
+ mBackupNoRestrictedModePackages.clear();
+ mBackupNoRestrictedModePackages.addAll(packageNames);
+ } else if (opType == BackupAnnotations.OperationType.RESTORE) {
+ mRestoreNoRestrictedModePackages.clear();
+ mRestoreNoRestrictedModePackages.addAll(packageNames);
+ } else {
+ throw new IllegalArgumentException("opType must be BACKUP or RESTORE");
+ }
+ }
+
+ /**
+ * Clears the list of packages that should not be put into restricted mode for either backup or
+ * restore.
+ */
+ public void clearNoRestrictedModePackages() {
+ mBackupNoRestrictedModePackages.clear();
+ mRestoreNoRestrictedModePackages.clear();
+ }
+
+ /**
+ * If the app has specified {@link PackageManager#PROPERTY_USE_RESTRICTED_BACKUP_MODE}, then
+ * its value is returned. If it hasn't and it targets an SDK below
+ * {@link Build.VERSION_CODES#BAKLAVA} then returns true. If it targets a newer SDK, then
+ * returns the decision made by the {@link android.app.backup.BackupTransport}.
+ *
+ * <p>When this method is called, we should have already asked the transport and cached its
+ * response in {@link #mBackupNoRestrictedModePackages} or
+ * {@link #mRestoreNoRestrictedModePackages} so this method will immediately return without
+ * any IPC to the transport.
+ */
+ private boolean shouldUseRestrictedBackupModeForPackage(
+ @BackupAnnotations.OperationType int mode, String packageName) {
+ if (!Flags.enableRestrictedModeChanges()) {
+ return true;
+ }
+
+ // Key/Value apps are never put in restricted mode.
+ if (mode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+ || mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE) {
+ return false;
+ }
+
+ try {
+ PackageManager.Property property = mPackageManager.getPropertyAsUser(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE,
+ packageName, /* className= */ null,
+ mUserId);
+ if (property.isBoolean()) {
+ // If the package has explicitly specified, we won't ask the transport.
+ return property.getBoolean();
+ } else {
+ Slog.w(TAG, PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE
+ + "must be a boolean.");
+ }
+ } catch (NameNotFoundException e) {
+ // This is expected when the package has not defined the property in its manifest.
+ }
+
+ // The package has not specified the property. The behavior depends on the package's
+ // targetSdk.
+ // <36 gets the old behavior of always using restricted mode.
+ if (!CompatChanges.isChangeEnabled(OS_DECIDES_BACKUP_RESTRICTED_MODE, packageName,
+ UserHandle.of(mUserId))) {
+ return true;
+ }
+
+ // Apps targeting >=36 get the behavior decided by the transport.
+ // By this point, we should have asked the transport and cached its decision.
+ if ((mode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ && mBackupNoRestrictedModePackages.contains(packageName))
+ || (mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL
+ && mRestoreNoRestrictedModePackages.contains(packageName))) {
+ Slog.d(TAG, "Transport requested no restricted mode for: " + packageName);
+ return false;
+ }
+ return true;
+ }
+
private boolean startConfirmationUi(int token, String action) {
try {
Intent confIntent = new Intent(action);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index cca166b0939c..be9cdc8692cb 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -16,6 +16,8 @@
package com.android.server.backup.fullbackup;
+import static android.app.backup.BackupAnnotations.OperationType.BACKUP;
+
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -34,6 +36,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -388,6 +391,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
}
+ // We ask the transport which packages should not be put in restricted mode and cache
+ // the result in UBMS to be used later when the apps are started for backup.
+ setNoRestrictedModePackages(transport, mPackages);
+
// Set up to send data to the transport
final int N = mPackages.size();
int chunkSizeInBytes = 8 * 1024; // 8KB
@@ -694,6 +701,9 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mUserBackupManagerService.scheduleNextFullBackupJob(backoff);
}
+ // Clear this to avoid using the memory until reboot.
+ mUserBackupManagerService.clearNoRestrictedModePackages();
+
Slog.i(TAG, "Full data backup pass finished.");
mUserBackupManagerService.getWakelock().release();
}
@@ -722,6 +732,21 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
}
+ private void setNoRestrictedModePackages(BackupTransportClient transport,
+ List<PackageInfo> packages) {
+ try {
+ Set<String> packageNames = new ArraySet<>();
+ for (int i = 0; i < packages.size(); i++) {
+ packageNames.add(packages.get(i).packageName);
+ }
+ packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ BACKUP);
+ mUserBackupManagerService.setNoRestrictedModePackages(packageNames, BACKUP);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failed to retrieve no restricted mode packages from transport");
+ }
+ }
+
// Run the backup and pipe it back to the given socket -- expects to run on
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e536876f6cc3..5ee51a5aa189 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -53,6 +53,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
@@ -482,6 +483,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
return;
}
+ // We ask the transport which packages should not be put in restricted mode and cache
+ // the result in UBMS to be used later when the apps are started for restore.
+ setNoRestrictedModePackages(transport, packages);
+
RestoreDescription desc = transport.nextRestorePackage();
if (desc == null) {
Slog.e(TAG, "No restore metadata available; halting");
@@ -1358,6 +1363,9 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// Clear any ongoing session timeout.
backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+ // Clear this to avoid using the memory until reboot.
+ backupManagerService.clearNoRestrictedModePackages();
+
// If we have a PM token, we must under all circumstances be sure to
// handshake when we've finished.
if (mPmToken > 0) {
@@ -1819,4 +1827,20 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
return packageInfo;
}
+
+ @VisibleForTesting
+ void setNoRestrictedModePackages(BackupTransportClient transport,
+ PackageInfo[] packages) {
+ try {
+ Set<String> packageNames = new ArraySet<>();
+ for (int i = 0; i < packages.length; i++) {
+ packageNames.add(packages[i].packageName);
+ }
+ packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ RESTORE);
+ backupManagerService.setNoRestrictedModePackages(packageNames, RESTORE);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failed to retrieve restricted mode packages from transport");
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
index daf3415229ea..373811fef802 100644
--- a/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -17,6 +17,7 @@
package com.android.server.backup.transport;
import android.annotation.Nullable;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.RestoreDescription;
@@ -26,6 +27,7 @@ import android.content.pm.PackageInfo;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
@@ -375,6 +377,26 @@ public class BackupTransportClient {
}
/**
+ * See
+ * {@link IBackupTransport#getPackagesThatShouldNotUseRestrictedMode(List, int, AndroidFuture)}.
+ */
+ public Set<String> getPackagesThatShouldNotUseRestrictedMode(Set<String> packageNames,
+ @BackupAnnotations.OperationType
+ int operationType) throws RemoteException {
+ AndroidFuture<List<String>> resultFuture = mTransportFutures.newFuture();
+ mTransportBinder.getPackagesThatShouldNotUseRestrictedMode(List.copyOf(packageNames),
+ operationType,
+ resultFuture);
+ List<String> resultList = getFutureResult(resultFuture);
+ Set<String> set = new ArraySet<>();
+ if (resultList == null) {
+ return set;
+ }
+ set.addAll(resultList);
+ return set;
+ }
+
+ /**
* Allows the {@link TransportConnection} to notify this client
* if the underlying transport has become unusable. If that happens
* we want to cancel all active futures or callbacks.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dfddc089e4a4..d3e5942834ee 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -478,7 +478,6 @@ import com.android.server.wm.WindowProcessController;
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.VMRuntime;
-
import libcore.util.EmptyArray;
import java.io.File;
@@ -4493,16 +4492,11 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, "Unattached app died before backup, skipping");
final int userId = app.userId;
final String packageName = app.info.packageName;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnectedForUser(userId, packageName);
- } catch (RemoteException e) {
- // Can't happen; the backup manager is local
- }
+ mHandler.post(() -> {
+ try {
+ getBackupManager().agentDisconnectedForUser(userId, packageName);
+ } catch (RemoteException e) {
+ // Can't happen; the backup manager is local
}
});
}
@@ -4673,7 +4667,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
&& ((backupTarget.backupMode == BackupRecord.RESTORE_FULL)
- || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
+ || (backupTarget.backupMode == BackupRecord.BACKUP_FULL))
+ && backupTarget.useRestrictedMode;
}
final ActiveInstrumentation instr = app.getActiveInstrumentation();
@@ -13499,16 +13494,11 @@ public class ActivityManagerService extends IActivityManager.Stub
if (backupTarget != null && pid == backupTarget.app.getPid()) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
+ backupTarget.appInfo + " died during backup");
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnectedForUser(app.userId, app.info.packageName);
- } catch (RemoteException e) {
- // can't happen; backup manager is local
- }
+ mHandler.post(() -> {
+ try {
+ getBackupManager().agentDisconnectedForUser(app.userId, app.info.packageName);
+ } catch (RemoteException e) {
+ // can't happen; backup manager is local
}
});
}
@@ -14011,7 +14001,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// instantiated. The backup agent will invoke backupAgentCreated() on the
// activity manager to announce its creation.
public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId,
- @BackupDestination int backupDestination) {
+ @BackupDestination int backupDestination, boolean useRestrictedMode) {
long startTimeNs = SystemClock.uptimeNanos();
if (DEBUG_BACKUP) {
Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode
@@ -14096,7 +14086,8 @@ public class ActivityManagerService extends IActivityManager.Stub
+ app.packageName + ": " + e);
}
- BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination);
+ BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination,
+ useRestrictedMode);
ComponentName hostingName =
(backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|| backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE)
@@ -14122,8 +14113,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// process, etc, then mark it as being in full backup so that certain calls to the
// process can be blocked. This is not reset to false anywhere because we kill the
// process after the full backup is done and the ProcessRecord will vaporize anyway.
- if (UserHandle.isApp(app.uid) &&
- backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) {
+ if (UserHandle.isApp(app.uid)
+ && backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ && r.useRestrictedMode) {
proc.setInFullBackup(true);
}
r.app = proc;
@@ -14221,9 +14213,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final long oldIdent = Binder.clearCallingIdentity();
try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentConnectedForUser(userId, agentPackageName, agent);
+ getBackupManager().agentConnectedForUser(userId, agentPackageName, agent);
} catch (RemoteException e) {
// can't happen; the backup manager service is local
} catch (Exception e) {
@@ -19353,4 +19343,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
return token;
}
+
+ private IBackupManager getBackupManager() {
+ return IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
}
diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java
index 0b056d7883bf..64cc6f0e66e3 100644
--- a/services/core/java/com/android/server/am/BackupRecord.java
+++ b/services/core/java/com/android/server/am/BackupRecord.java
@@ -32,15 +32,18 @@ final class BackupRecord {
final int userId; // user for which backup is performed
final int backupMode; // full backup / incremental / restore
@BackupDestination final int backupDestination; // see BackupAnnotations#BackupDestination
+ final boolean useRestrictedMode; // whether the app should be put into restricted backup mode
ProcessRecord app; // where this agent is running or null
// ----- Implementation -----
- BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _backupDestination) {
+ BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _backupDestination,
+ boolean _useRestrictedMode) {
appInfo = _appInfo;
backupMode = _backupMode;
userId = _userId;
backupDestination = _backupDestination;
+ useRestrictedMode = _useRestrictedMode;
}
public String toString() {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b51db137f293..98f738c38d63 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -351,7 +351,8 @@ class ProcessRecord implements WindowProcessListener {
private String[] mIsolatedEntryPointArgs;
/**
- * Process is currently hosting a backup agent for backup or restore.
+ * Process is currently hosting a backup agent for backup or restore. Note that this is only set
+ * when the process is put into restricted backup mode.
*/
@GuardedBy("mService")
private boolean mInFullBackup;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index dcbc23410fdb..5a872ea04bcc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -47,10 +47,8 @@ import static com.android.server.am.Flags.FLAG_AVOID_RESOLVING_TYPE;
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
-
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -80,6 +78,7 @@ import android.Manifest;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.ApplicationThreadConstants;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.ForegroundServiceDelegationOptions;
@@ -87,6 +86,7 @@ import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.SyncNotedAppOp;
+import android.app.backup.BackupAnnotations;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -111,6 +111,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -133,6 +134,7 @@ import com.android.server.am.ProcessList.IsolatedUidRange;
import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
import com.android.server.am.UidObserverController.ChangeRecord;
import com.android.server.appop.AppOpsService;
+import com.android.server.job.JobSchedulerInternal;
import com.android.server.notification.NotificationManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerService;
@@ -228,6 +230,7 @@ public class ActivityManagerServiceTest {
@Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
@Mock private NotificationManagerInternal mNotificationManagerInternal;
+ @Mock private JobSchedulerInternal mJobSchedulerInternal;
@Mock private ContentResolver mContentResolver;
private TestInjector mInjector;
@@ -249,6 +252,7 @@ public class ActivityManagerServiceTest {
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal);
+ LocalServices.addService(JobSchedulerInternal.class, mJobSchedulerInternal);
doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
.getSystemUiServiceComponent();
@@ -308,6 +312,7 @@ public class ActivityManagerServiceTest {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(NotificationManagerInternal.class);
+ LocalServices.removeServiceForTest(JobSchedulerInternal.class);
if (mMockingSession != null) {
mMockingSession.finishMocking();
@@ -1548,6 +1553,50 @@ public class ActivityManagerServiceTest {
eq(notificationId), anyInt());
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void bindBackupAgent_fullBackup_shouldUseRestrictedMode_setsInFullBackup()
+ throws Exception {
+ ActivityManagerService spyAms = spy(mAms);
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = TEST_PACKAGE;
+ applicationInfo.processName = TEST_PACKAGE;
+ applicationInfo.uid = TEST_UID;
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(TEST_PACKAGE),
+ anyLong(), anyInt());
+ ProcessRecord appRec = new ProcessRecord(mAms, applicationInfo, TAG, TEST_UID);
+ doReturn(appRec).when(spyAms).getProcessRecordLocked(eq(TEST_PACKAGE), eq(TEST_UID));
+
+ spyAms.bindBackupAgent(TEST_PACKAGE, ApplicationThreadConstants.BACKUP_MODE_FULL,
+ UserHandle.USER_SYSTEM,
+ BackupAnnotations.BackupDestination.CLOUD, /* shouldUseRestrictedMode= */
+ true);
+
+ assertThat(appRec.isInFullBackup()).isTrue();
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void bindBackupAgent_fullBackup_shouldNotUseRestrictedMode_doesNotSetInFullBackup()
+ throws Exception {
+ ActivityManagerService spyAms = spy(mAms);
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = TEST_PACKAGE;
+ applicationInfo.processName = TEST_PACKAGE;
+ applicationInfo.uid = TEST_UID;
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(TEST_PACKAGE),
+ anyLong(), anyInt());
+ ProcessRecord appRec = new ProcessRecord(mAms, applicationInfo, TAG, TEST_UID);
+ doReturn(appRec).when(spyAms).getProcessRecordLocked(eq(TEST_PACKAGE), eq(TEST_UID));
+
+ spyAms.bindBackupAgent(TEST_PACKAGE, ApplicationThreadConstants.BACKUP_MODE_FULL,
+ UserHandle.USER_SYSTEM,
+ BackupAnnotations.BackupDestination.CLOUD, /* shouldUseRestrictedMode= */
+ false);
+
+ assertThat(appRec.isInFullBackup()).isFalse();
+ }
+
private static class TestHandler extends Handler {
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index f82a86092064..94cf4cbc0f8c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -3306,7 +3306,7 @@ public class MockingOomAdjusterTests {
if (Flags.pushGlobalStateToOomadjuster()) {
mProcessStateController.setBackupTarget(app, app.userId);
} else {
- BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
+ BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0, true);
backupTarget.app = app;
doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 65286d9aabc7..07f2188d30eb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -18,9 +18,7 @@ package com.android.server.backup;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -32,20 +30,27 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.ApplicationThreadConstants;
+import android.app.IActivityManager;
import android.app.backup.BackupAgent;
-import android.app.backup.BackupAnnotations;
import android.app.backup.BackupAnnotations.BackupDestination;
+import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.FeatureFlagUtils;
@@ -68,7 +73,9 @@ import com.google.common.collect.ImmutableSet;
import org.junit.After;
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;
@@ -77,8 +84,12 @@ import org.mockito.quality.Strictness;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import java.util.function.IntConsumer;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class UserBackupManagerServiceTest {
@@ -88,6 +99,11 @@ public class UserBackupManagerServiceTest {
private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 100;
@UserIdInt private static final int USER_ID = 0;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock IBackupManagerMonitor mBackupManagerMonitor;
@Mock IBackupObserver mBackupObserver;
@Mock PackageManager mPackageManager;
@@ -99,10 +115,14 @@ public class UserBackupManagerServiceTest {
@Mock JobScheduler mJobScheduler;
@Mock BackupHandler mBackupHandler;
@Mock BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
+ @Mock IActivityManager mActivityManager;
+ @Mock
+ ActivityManagerInternal mActivityManagerInternal;
private TestableContext mContext;
private MockitoSession mSession;
private TestBackupService mService;
+ private ApplicationInfo mTestPackageApplicationInfo;
@Before
public void setUp() throws Exception {
@@ -120,12 +140,14 @@ public class UserBackupManagerServiceTest {
mContext.getTestablePermissions().setPermission(android.Manifest.permission.BACKUP,
PackageManager.PERMISSION_GRANTED);
- mService = new TestBackupService(mContext, mPackageManager, mOperationStorage,
- mTransportManager, mBackupHandler);
+ mService = new TestBackupService();
mService.setEnabled(true);
mService.setSetupComplete(true);
mService.enqueueFullBackup("com.test.backup.app", /* lastBackedUp= */ 0);
- }
+
+ mTestPackageApplicationInfo = new ApplicationInfo();
+ mTestPackageApplicationInfo.packageName = TEST_PACKAGE;
+ }
@After
public void tearDown() {
@@ -298,9 +320,160 @@ public class UserBackupManagerServiceTest {
new DataTypeResult(/* dataType */ "type_2"));
mService.reportDelayedRestoreResult(TEST_PACKAGE, results);
-
verify(mBackupManagerMonitorEventSender).sendAgentLoggingResults(
- eq(packageInfo), eq(results), eq(BackupAnnotations.OperationType.RESTORE));
+ eq(packageInfo), eq(results), eq(OperationType.RESTORE));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_restrictedModeChangesFlagOff_shouldUseRestrictedMode()
+ throws Exception {
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_keyValueBackup_shouldNotUseRestrictedMode()
+ throws Exception {
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_keyValueRestore_shouldNotUseRestrictedMode()
+ throws Exception {
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_packageOptedIn_shouldUseRestrictedMode()
+ throws Exception {
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenReturn(new PackageManager.Property(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE, /* value= */ true,
+ TEST_PACKAGE, /* className= */ null));
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_packageOptedOut_shouldNotUseRestrictedMode()
+ throws Exception {
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenReturn(new PackageManager.Property(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE, /* value= */ false,
+ TEST_PACKAGE, /* className= */ null));
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @DisableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_targetSdkBelowB_shouldUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_targetSdkB_notInList_shouldUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+ mService.clearNoRestrictedModePackages();
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_forRestore_targetSdkB_inList_shouldNotUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+ mService.setNoRestrictedModePackages(Set.of(TEST_PACKAGE), OperationType.RESTORE);
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_forBackup_targetSdkB_inList_shouldNotUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+ mService.setNoRestrictedModePackages(Set.of(TEST_PACKAGE), OperationType.BACKUP);
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
}
private static PackageInfo getPackageInfo(String packageName) {
@@ -316,11 +489,9 @@ public class UserBackupManagerServiceTest {
private volatile Thread mWorkerThread = null;
- TestBackupService(Context context, PackageManager packageManager,
- LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler) {
- super(context, packageManager, operationStorage, transportManager, backupHandler,
- createConstants(context));
+ TestBackupService() {
+ super(mContext, mPackageManager, mOperationStorage, mTransportManager, mBackupHandler,
+ createConstants(mContext), mActivityManager, mActivityManagerInternal);
}
private static BackupManagerConstants createConstants(Context context) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
index 94742537ed1a..331057398949 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
@@ -18,34 +18,95 @@ package com.android.server.backup.fullbackup;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupAnnotations;
+import android.app.backup.BackupTransport;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.transport.BackupTransportClient;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.utils.BackupEligibilityRules;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class PerformFullTransportBackupTaskTest {
+ private static final String TEST_PACKAGE_1 = "package1";
+ private static final String TEST_PACKAGE_2 = "package2";
+
+ @Mock
+ BackupAgentTimeoutParameters mBackupAgentTimeoutParameters;
+ @Mock
+ BackupEligibilityRules mBackupEligibilityRules;
@Mock
UserBackupManagerService mBackupManagerService;
@Mock
+ BackupTransportClient mBackupTransportClient;
+ @Mock
+ CountDownLatch mLatch;
+ @Mock
+ OperationStorage mOperationStorage;
+ @Mock
+ PackageManager mPackageManager;
+ @Mock
+ TransportConnection mTransportConnection;
+ @Mock
TransportManager mTransportManager;
+ @Mock
+ UserBackupManagerService.BackupWakeLock mWakeLock;
+
+ private final List<String> mEligiblePackages = new ArrayList<>();
+
+ private PerformFullTransportBackupTask mTask;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mBackupManagerService.getPackageManager()).thenReturn(mPackageManager);
+ when(mBackupManagerService.getQueueLock()).thenReturn("something!");
+ when(mBackupManagerService.isEnabled()).thenReturn(true);
+ when(mBackupManagerService.getWakelock()).thenReturn(mWakeLock);
+ when(mBackupManagerService.isSetupComplete()).thenReturn(true);
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(
+ mBackupAgentTimeoutParameters);
when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+ when(mTransportManager.getCurrentTransportClient(any())).thenReturn(mTransportConnection);
+ when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransportClient);
+ when(mTransportConnection.connect(any())).thenReturn(mBackupTransportClient);
+ when(mBackupTransportClient.performFullBackup(any(), any(), anyInt())).thenReturn(
+ BackupTransport.TRANSPORT_ERROR);
+ when(mBackupEligibilityRules.appIsEligibleForBackup(
+ argThat(app -> mEligiblePackages.contains(app.packageName)))).thenReturn(
+ true);
+ when(mBackupEligibilityRules.appGetsFullBackup(
+ argThat(app -> mEligiblePackages.contains(app.packageName)))).thenReturn(
+ true);
}
@Test
@@ -70,4 +131,49 @@ public class PerformFullTransportBackupTaskTest {
/* backupEligibilityRules */ null);
});
}
+
+ @Test
+ public void run_setsAndClearsNoRestrictedModePackages() throws Exception {
+ mockPackageEligibleForFullBackup(TEST_PACKAGE_1);
+ mockPackageEligibleForFullBackup(TEST_PACKAGE_2);
+ createTask(new String[] {TEST_PACKAGE_1, TEST_PACKAGE_2});
+ when(mBackupTransportClient.getPackagesThatShouldNotUseRestrictedMode(any(),
+ anyInt())).thenReturn(Set.of("package1"));
+
+ mTask.run();
+
+ InOrder inOrder = inOrder(mBackupManagerService);
+ inOrder.verify(mBackupManagerService).setNoRestrictedModePackages(
+ eq(Set.of("package1")),
+ eq(BackupAnnotations.OperationType.BACKUP));
+ inOrder.verify(mBackupManagerService).clearNoRestrictedModePackages();
+ }
+
+ private void createTask(String[] packageNames) {
+ mTask = PerformFullTransportBackupTask
+ .newWithCurrentTransport(
+ mBackupManagerService,
+ mOperationStorage,
+ /* observer */ null,
+ /* whichPackages */ packageNames,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ mLatch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ /* caller */ null,
+ mBackupEligibilityRules);
+ }
+
+ private void mockPackageEligibleForFullBackup(String packageName) throws Exception {
+ mEligiblePackages.add(packageName);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = appInfo;
+ when(mPackageManager.getPackageInfoAsUser(eq(packageName), anyInt(), anyInt())).thenReturn(
+ packageInfo);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 414532b88e22..055adf68ee0f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -23,8 +23,10 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
@@ -91,6 +93,8 @@ public class PerformUnifiedRestoreTaskTest {
private UserBackupManagerService mBackupManagerService;
@Mock
private TransportConnection mTransportConnection;
+ @Mock
+ private BackupTransportClient mBackupTransportClient;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -151,6 +155,23 @@ public class PerformUnifiedRestoreTaskTest {
}
@Test
+ public void setNoRestrictedModePackages_callsTransportAndSetsValue() throws Exception {
+ PackageInfo packageInfo1 = new PackageInfo();
+ packageInfo1.packageName = "package1";
+ PackageInfo packageInfo2 = new PackageInfo();
+ packageInfo2.packageName = "package2";
+ when(mBackupTransportClient.getPackagesThatShouldNotUseRestrictedMode(any(),
+ anyInt())).thenReturn(Set.of("package1"));
+
+ mRestoreTask.setNoRestrictedModePackages(mBackupTransportClient,
+ new PackageInfo[]{packageInfo1, packageInfo2});
+
+ verify(mBackupManagerService).setNoRestrictedModePackages(
+ eq(Set.of("package1")),
+ eq(BackupAnnotations.OperationType.RESTORE));
+ }
+
+ @Test
public void testFilterExcludedKeys() throws Exception {
when(mBackupManagerService.getExcludedRestoreKeys(eq(PACKAGE_NAME)))
.thenReturn(mExcludedkeys);
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
index 2d7d46f83c47..13e32078f609 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
@@ -19,7 +19,14 @@ package com.android.server.backup.transport;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.RestoreDescription;
@@ -38,15 +45,31 @@ import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.ITransportStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Set;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class BackupTransportClientTest {
+ @Mock
+ IBackupTransport mMockBackupTransport;
+
+ private BackupTransportClient mMockingTransportClient;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMockingTransportClient = new BackupTransportClient(
+ mMockBackupTransport);
+ }
+
private static class TestFuturesFakeTransportBinder extends FakeTransportBinderBase {
public final Object mLock = new Object();
@@ -128,6 +151,70 @@ public class BackupTransportClientTest {
thread.join();
}
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_passesSetAsListToBinder()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(List.of("package1", "package2"));
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of("package1", "package2"),
+ OperationType.BACKUP);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(
+ argThat(list -> Set.copyOf(list).equals(Set.of("package1", "package2"))),
+ eq(OperationType.BACKUP), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_forRestore_callsBinderForRestore()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.RESTORE);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(),
+ eq(OperationType.RESTORE), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_forBackup_callsBinderForBackup()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.BACKUP);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(),
+ eq(OperationType.BACKUP), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_nullResult_returnsEmptySet()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ Set<String> result = mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.BACKUP);
+
+ assertThat(result).isEqualTo(Set.of());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_returnsResultAsSet()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(List.of("package1", "package2"));
+
+ Set<String> result = mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of("package1", "package2"),
+ OperationType.BACKUP);
+
+ assertThat(result).isEqualTo(Set.of("package1", "package2"));
+ }
+
private static class TestCallbacksFakeTransportBinder extends FakeTransportBinderBase {
public final Object mLock = new Object();
@@ -158,7 +245,6 @@ public class BackupTransportClientTest {
assertThat(status).isEqualTo(123);
}
-
@Test
public void testFinishBackup_completesLater_returnsStatus() throws Exception {
TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
@@ -211,6 +297,14 @@ public class BackupTransportClientTest {
thread.join();
}
+ private void mockGetPackagesThatShouldNotUseRestrictedModeReturn(List<String> returnList)
+ throws Exception {
+ doAnswer(
+ i -> ((AndroidFuture<List<String>>) i.getArguments()[2]).complete(returnList)).when(
+ mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(), anyInt(),
+ any());
+ }
+
// Convenience layer so we only need to fake specific methods useful for each test case.
private static class FakeTransportBinderBase implements IBackupTransport {
@Override public void name(AndroidFuture<String> f) throws RemoteException {}
@@ -258,6 +352,10 @@ public class BackupTransportClientTest {
@Override
public void getBackupManagerMonitor(AndroidFuture<IBackupManagerMonitor> resultFuture)
throws RemoteException {}
+ @Override
+ public void getPackagesThatShouldNotUseRestrictedMode(List<String> packageNames,
+ int operationType, AndroidFuture<List<String>> resultFuture)
+ throws RemoteException {}
@Override public IBinder asBinder() {
return null;
}