Make RollbackManager @SystemApi.
Require callers hold a new MANAGE_ROLLBACKS permission to interact with
the RollbackManager.
Bug: 112431924
Test: atest RollbackTest, with new test for permissions added,
and with selinux in permissive mode.
Test: atest CtsPermission2TestCases:PermissionPolicyTest
Change-Id: I73f4f3457d85be580670cd69c89066d2cc348186
diff --git a/api/system-current.txt b/api/system-current.txt
index 47bb88a..9515b46 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -94,6 +94,7 @@
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+ field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final java.lang.String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
@@ -1038,6 +1039,7 @@
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
field public static final java.lang.String PERMISSION_SERVICE = "permission";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+ field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final java.lang.String STATS_MANAGER = "stats";
field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
@@ -1069,6 +1071,7 @@
field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
field public static final java.lang.String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
+ field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
@@ -1204,6 +1207,7 @@
method public void setAllocateAggressive(boolean);
method public void setAllowDowngrade(boolean);
method public void setDontKillApp(boolean);
+ method public void setEnableRollback();
method public void setGrantedRuntimePermissions(java.lang.String[]);
method public void setInstallAsInstantApp(boolean);
method public void setInstallAsVirtualPreload();
@@ -1398,6 +1402,41 @@
}
+package android.content.rollback {
+
+ public final class PackageRollbackInfo implements android.os.Parcelable {
+ ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
+ field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
+ field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
+ field public final java.lang.String packageName;
+ }
+
+ public static class PackageRollbackInfo.PackageVersion {
+ ctor public PackageRollbackInfo.PackageVersion(long);
+ field public final long versionCode;
+ }
+
+ public final class RollbackInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
+ field public final android.content.rollback.PackageRollbackInfo targetPackage;
+ }
+
+ public final class RollbackManager {
+ method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
+ method public void expireRollbackForPackage(java.lang.String);
+ method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
+ method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
+ method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
+ method public void reloadPersistedData();
+ }
+
+}
+
package android.hardware {
public final class Sensor {
diff --git a/api/test-current.txt b/api/test-current.txt
index c1e4162..71a06f1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -329,11 +329,6 @@
method public android.os.UserHandle getUser();
method public int getUserId();
method public void setAutofillCompatibilityEnabled(boolean);
- field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
- }
-
- public class Intent implements java.lang.Cloneable android.os.Parcelable {
- field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
}
}
@@ -356,10 +351,6 @@
ctor public LauncherApps(android.content.Context);
}
- public static class PackageInstaller.SessionParams implements android.os.Parcelable {
- method public void setEnableRollback();
- }
-
public abstract class PackageManager {
method public abstract boolean arePermissionsIndividuallyControlled();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -407,42 +398,6 @@
}
-package android.content.rollback {
-
- public final class PackageRollbackInfo implements android.os.Parcelable {
- ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
- field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
- field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
- field public final java.lang.String packageName;
- }
-
- public static class PackageRollbackInfo.PackageVersion {
- ctor public PackageRollbackInfo.PackageVersion(long);
- field public final long versionCode;
- }
-
- public final class RollbackInfo implements android.os.Parcelable {
- ctor public RollbackInfo(android.content.rollback.PackageRollbackInfo);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
- field public final android.content.rollback.PackageRollbackInfo targetPackage;
- }
-
- public final class RollbackManager {
- method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
- method public void expireRollbackForPackage(java.lang.String);
- method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
- method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
- method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
- method public void reloadPersistedData();
- }
-
-}
-
package android.database.sqlite {
public class SQLiteCompatibilityWalFlags {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 125c4c6..0aa6a8c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4009,9 +4009,9 @@
* with the rollback manager
*
* @see #getSystemService(String)
- * @hide TODO(ruhler): hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
- @TestApi
+ @SystemApi
public static final String ROLLBACK_SERVICE = "rollback";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1e3908c..4d0f4cd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -2268,9 +2267,9 @@
* <p class="note">This is a protected intent that can only be sent
* by the system.
*
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
- @TestApi
+ @SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED =
"android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index a2fd83f..94b7c45 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -24,7 +24,6 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -1428,9 +1427,9 @@
/**
* Request that rollbacks be enabled for the given upgrade.
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
- @TestApi
+ @SystemApi
public void setEnableRollback() {
installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
}
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 0c05765..2040024 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -16,7 +16,7 @@
package android.content.rollback;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,9 +25,9 @@
/**
* Information about a rollback available for a particular package.
*
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
-@TestApi
+@SystemApi
public final class PackageRollbackInfo implements Parcelable {
/**
* The name of a package being rolled back.
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index 5fa4e57..66df4fe 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -16,7 +16,7 @@
package android.content.rollback;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,9 +24,9 @@
* Information about a set of packages that can be, or already have been
* rolled back together.
*
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
-@TestApi
+@SystemApi
public final class RollbackInfo implements Parcelable {
/**
@@ -39,6 +39,7 @@
// TODO: Add a flag to indicate if reboot is required, when rollback of
// staged installs is supported.
+ /** @hide */
public RollbackInfo(PackageRollbackInfo targetPackage) {
this.targetPackage = targetPackage;
}
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 294151a..c1c0bc1 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -18,8 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.Context;
import android.content.IntentSender;
import android.os.RemoteException;
@@ -33,12 +34,10 @@
* used to initiate rollback of those packages for a limited time period after
* upgrade.
*
- * TODO: Require an appropriate permission for apps to use these APIs.
- *
* @see PackageInstaller.SessionParams#setEnableRollback()
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
*/
-@TestApi
+@SystemApi
@SystemService(Context.ROLLBACK_SERVICE)
public final class RollbackManager {
private final String mCallerPackageName;
@@ -63,7 +62,10 @@
* @param packageName name of the package to get the availble RollbackInfo for.
* @return the rollback available for the package, or null if no rollback
* is available for the package.
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public @Nullable RollbackInfo getAvailableRollback(@NonNull String packageName) {
try {
return mBinder.getAvailableRollback(packageName);
@@ -78,7 +80,10 @@
* about the rollback available for a particular package.
*
* @return the names of packages that are available for rollback.
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public @NonNull List<String> getPackagesWithAvailableRollbacks() {
try {
return mBinder.getPackagesWithAvailableRollbacks().getList();
@@ -103,7 +108,10 @@
* rolled back from.
*
* @return the recently executed rollbacks
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public @NonNull List<RollbackInfo> getRecentlyExecutedRollbacks() {
try {
return mBinder.getRecentlyExecutedRollbacks().getList();
@@ -127,7 +135,10 @@
*
* @param rollback to execute
* @param statusReceiver where to deliver the results
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public void executeRollback(@NonNull RollbackInfo rollback,
@NonNull IntentSender statusReceiver) {
try {
@@ -143,9 +154,10 @@
* across device reboot, by simulating what happens on reboot without
* actually rebooting the device.
*
- * @hide
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
- @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public void reloadPersistedData() {
try {
mBinder.reloadPersistedData();
@@ -160,10 +172,10 @@
* expiring rollback data.
*
* @param packageName the name of the package to expire data for.
- *
- * @hide
+ * @throws SecurityException if the caller does not have the
+ * MANAGE_ROLLBACKS permission.
*/
- @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
public void expireRollbackForPackage(@NonNull String packageName) {
try {
mBinder.expireRollbackForPackage(packageName);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b16c16d..be37ca9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3854,6 +3854,10 @@
<permission android:name="android.permission.PACKAGE_ROLLBACK_AGENT"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows managing apk level rollbacks. -->
+ <permission android:name="android.permission.MANAGE_ROLLBACKS"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
<permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
android:protectionLevel="signature|verifier" />
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8cfc2a6..bff2c84 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -82,6 +82,7 @@
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java
index 5bf2040..248e6c9 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerService.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.rollback;
+import android.Manifest;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -42,7 +43,6 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.SELinux;
import android.util.Log;
@@ -203,6 +203,10 @@
@Override
public RollbackInfo getAvailableRollback(String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "getAvailableRollback");
+
PackageRollbackInfo.PackageVersion installedVersion =
getInstalledPackageVersion(packageName);
if (installedVersion == null) {
@@ -228,6 +232,10 @@
@Override
public StringParceledListSlice getPackagesWithAvailableRollbacks() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "getPackagesWithAvailableRollbacks");
+
// TODO: This may return packages whose rollback is out of date or
// expired. Presumably that's okay because the package rollback could
// be expired anyway between when the caller calls this method and
@@ -245,6 +253,10 @@
@Override
public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "getRecentlyExecutedRollbacks");
+
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
@@ -255,11 +267,13 @@
@Override
public void executeRollback(RollbackInfo rollback, String callerPackageName,
IntentSender statusReceiver) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "executeRollback");
+
final int callingUid = Binder.getCallingUid();
- if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
- AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- appOps.checkPackage(callingUid, callerPackageName);
- }
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ appOps.checkPackage(callingUid, callerPackageName);
getHandler().post(() ->
executeRollbackInternal(rollback, callerPackageName, statusReceiver));
@@ -362,10 +376,8 @@
addRecentlyExecutedRollback(rollback);
sendSuccess(statusReceiver);
- // TODO: Restrict permissions for who can listen for this
- // broadcast?
Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
- Uri.fromParts("package", packageName, null));
+ Uri.fromParts("package", packageName, Manifest.permission.MANAGE_ROLLBACKS));
// TODO: This call emits the warning "Calling a method in the
// system process without a qualified user". Fix that.
@@ -379,6 +391,10 @@
@Override
public void reloadPersistedData() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "reloadPersistedData");
+
synchronized (mLock) {
mAvailableRollbacks = null;
mRecentlyExecutedRollbacks = null;
@@ -388,6 +404,10 @@
@Override
public void expireRollbackForPackage(String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ROLLBACKS,
+ "expireRollbackForPackage");
+
// TODO: Should this take a package version number in addition to
// package name? For now, just remove all rollbacks matching the
// package name. This method is only currently used to facilitate
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index f2bd407..8aadde9 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -43,9 +43,8 @@
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
LOCAL_JAVA_RESOURCE_FILES := $(ROLLBACK_TEST_APP_V1) $(ROLLBACK_TEST_APP_V2)
-LOCAL_SDK_VERSION := test_current
+LOCAL_SDK_VERSION := system_current
LOCAL_TEST_CONFIG := RollbackTest.xml
include $(BUILD_PACKAGE)
diff --git a/tests/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/AndroidManifest.xml
index d1535a3..e57a768 100644
--- a/tests/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/AndroidManifest.xml
@@ -17,9 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.rollback" >
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- <uses-permission android:name="android.permission.DELETE_PACKAGES" />
-
<application>
<receiver android:name="com.android.tests.rollback.LocalIntentSender"
android:exported="true" />
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 02c1ce2..503dbde 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -16,6 +16,7 @@
package com.android.tests.rollback;
+import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -71,85 +72,94 @@
};
context.registerReceiver(enableRollbackReceiver, enableRollbackFilter);
- // Register a broadcast receiver for notification when the rollback is
- // done executing.
- RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
- RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
- // Uninstall com.android.tests.rollback.testapp
- RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
- assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ // Register a broadcast receiver for notification when the rollback is
+ // done executing.
+ RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
- // TODO: There is currently a race condition between when the app is
- // uninstalled and when rollback manager deletes the rollback. Fix it
- // so that's not the case!
- for (int i = 0; i < 5; ++i) {
- for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(info.targetPackage.packageName)) {
- Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
- Thread.sleep(1000);
- break;
+ // Uninstall com.android.tests.rollback.testapp
+ RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
+ assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+
+ // TODO: There is currently a race condition between when the app is
+ // uninstalled and when rollback manager deletes the rollback. Fix it
+ // so that's not the case!
+ for (int i = 0; i < 5; ++i) {
+ for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_PACKAGE_NAME.equals(info.targetPackage.packageName)) {
+ Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
+ Thread.sleep(1000);
+ break;
+ }
}
}
- }
- // The app should not be available for rollback.
- assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
- assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ // The app should not be available for rollback.
+ assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
+ assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- // There should be no recently executed rollbacks for this package.
- for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
- assertNotEquals(TEST_APP_PACKAGE_NAME, info.targetPackage.packageName);
- }
-
- // Install v1 of the app (without rollbacks enabled).
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
- assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
- // Upgrade from v1 to v2, with rollbacks enabled.
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
- assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
- // The app should now be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
-
- // We should not have received any rollback requests yet.
- // TODO: Possibly flaky if, by chance, some other app on device
- // happens to be rolled back at the same time?
- assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
-
- // Roll back the app.
- RollbackTestUtils.rollback(rollback);
- assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
- // Verify we received a broadcast for the rollback.
- // TODO: Race condition between the timeout and when the broadcast is
- // received could lead to test flakiness.
- Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
- assertNotNull(broadcast);
- assertEquals(TEST_APP_PACKAGE_NAME, broadcast.getData().getSchemeSpecificPart());
- assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
-
- // Verify the recent rollback has been recorded.
- rollback = null;
- for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
- assertNull(rollback);
- rollback = r;
+ // There should be no recently executed rollbacks for this package.
+ for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+ assertNotEquals(TEST_APP_PACKAGE_NAME, info.targetPackage.packageName);
}
- }
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- broadcastReceiver.unregister();
- context.unregisterReceiver(enableRollbackReceiver);
+ // Install v1 of the app (without rollbacks enabled).
+ RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+
+ // Upgrade from v1 to v2, with rollbacks enabled.
+ RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+
+ // The app should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+ // We should not have received any rollback requests yet.
+ // TODO: Possibly flaky if, by chance, some other app on device
+ // happens to be rolled back at the same time?
+ assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+ // Roll back the app.
+ RollbackTestUtils.rollback(rollback);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+
+ // Verify we received a broadcast for the rollback.
+ // TODO: Race condition between the timeout and when the broadcast is
+ // received could lead to test flakiness.
+ Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
+ assertNotNull(broadcast);
+ assertEquals(TEST_APP_PACKAGE_NAME, broadcast.getData().getSchemeSpecificPart());
+ assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+ // Verify the recent rollback has been recorded.
+ rollback = null;
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
+ assertNull(rollback);
+ rollback = r;
+ }
+ }
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+ broadcastReceiver.unregister();
+ context.unregisterReceiver(enableRollbackReceiver);
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
}
/**
@@ -157,65 +167,74 @@
*/
@Test
public void testRollbackDataPersistence() throws Exception {
- RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
- // Prep installation of com.android.tests.rollback.testapp
- RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
- assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
- // The app should now be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ // Prep installation of com.android.tests.rollback.testapp
+ RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
+ RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
- // Reload the persisted data.
- rm.reloadPersistedData();
+ // The app should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // The app should still be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ // Reload the persisted data.
+ rm.reloadPersistedData();
- // Roll back the app.
- RollbackTestUtils.rollback(rollback);
- assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ // The app should still be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // Verify the recent rollback has been recorded.
- rollback = null;
- for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
- assertNull(rollback);
- rollback = r;
+ // Roll back the app.
+ RollbackTestUtils.rollback(rollback);
+ assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+
+ // Verify the recent rollback has been recorded.
+ rollback = null;
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
+ assertNull(rollback);
+ rollback = r;
+ }
}
- }
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // Reload the persisted data.
- rm.reloadPersistedData();
+ // Reload the persisted data.
+ rm.reloadPersistedData();
- // Verify the recent rollback is still recorded.
- rollback = null;
- for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
- if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
- assertNull(rollback);
- rollback = r;
+ // Verify the recent rollback is still recorded.
+ rollback = null;
+ for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+ if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
+ assertNull(rollback);
+ rollback = r;
+ }
}
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
}
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
}
/**
@@ -224,26 +243,35 @@
*/
@Test
public void testRollbackExpiration() throws Exception {
- RollbackManager rm = RollbackTestUtils.getRollbackManager();
- RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
- assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+ try {
+ RollbackTestUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.MANAGE_ROLLBACKS);
- // The app should now be available for rollback.
- assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
- RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
- assertNotNull(rollback);
- assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
- assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
- assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+ RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
+ RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+ assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
- // Expire the rollback.
- rm.expireRollbackForPackage(TEST_APP_PACKAGE_NAME);
+ // The app should now be available for rollback.
+ assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
+ assertNotNull(rollback);
+ assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
+ assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+ assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
- // The rollback should no longer be available.
- assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
- assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ // Expire the rollback.
+ rm.expireRollbackForPackage(TEST_APP_PACKAGE_NAME);
+
+ // The rollback should no longer be available.
+ assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
+ assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+ } finally {
+ RollbackTestUtils.dropShellPermissionIdentity();
+ }
}
/**
@@ -270,4 +298,59 @@
// called, even when the test fails?
broadcastReceiver.unregister();
}
+
+ /**
+ * Test that the MANAGE_ROLLBACKS permission is required to call
+ * RollbackManager APIs.
+ */
+ @Test
+ public void testManageRollbacksPermission() throws Exception {
+ // We shouldn't be allowed to call any of the RollbackManager APIs
+ // without the MANAGE_ROLLBACKS permission.
+ RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+ try {
+ rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.getPackagesWithAvailableRollbacks();
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.getRecentlyExecutedRollbacks();
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ // TODO: What if the implementation checks arguments for non-null
+ // first? Then this test isn't valid.
+ rm.executeRollback(null, null);
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.reloadPersistedData();
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+
+ try {
+ rm.expireRollbackForPackage(TEST_APP_PACKAGE_NAME);
+ fail("expected SecurityException");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+ }
}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index c5ce3fa..f16c917 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -135,15 +135,13 @@
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}
- // TODO: Unused
- static void adoptShellPermissionIdentity() {
+ static void adoptShellPermissionIdentity(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
.getUiAutomation()
- .adoptShellPermissionIdentity();
+ .adoptShellPermissionIdentity(permissions);
}
- // TODO: Unused
static void dropShellPermissionIdentity() {
InstrumentationRegistry
.getInstrumentation()