diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 389832815d777..50d1785c60596 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2444,6 +2444,18 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_ENABLE_ROLLBACK = "android.intent.action.PACKAGE_ENABLE_ROLLBACK"; + /** + * Broadcast Action: Sent to the system rollback manager when the rollback for a certain + * package needs to be cancelled. + * + *
This intent is sent by PackageManagerService to notify RollbackManager + * that enabling a specific rollback has timed out. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CANCEL_ENABLE_ROLLBACK = + "android.intent.action.CANCEL_ENABLE_ROLLBACK"; /** * Broadcast Action: A rollback has been committed. * diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java index d54a6fe0a7b2f..9a10a0c4766b8 100644 --- a/core/java/android/content/rollback/RollbackManager.java +++ b/core/java/android/content/rollback/RollbackManager.java @@ -40,7 +40,7 @@ import java.util.List; * used to initiate rollback of those packages for a limited time period after * upgrade. * - * @see PackageInstaller.SessionParams#setEnableRollback() + * @see PackageInstaller.SessionParams#setEnableRollback(boolean) * @hide */ @SystemApi @TestApi @@ -52,7 +52,7 @@ public final class RollbackManager { /** * Lifetime duration of rollback packages in millis. A rollback will be available for * at most that duration of time after a package is installed with - * {@link PackageInstaller.SessionParams#setEnableRollback()}. + * {@link PackageInstaller.SessionParams#setEnableRollback(boolean)}. * *
If flag value is negative, the default value will be assigned.
*
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8714bf2505bb5..57b7704558171 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -43,6 +43,7 @@
RollbackData could be invalidated and cancelled if RollbackManager receives + * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}. + * + *
The main underlying assumption here is that if enabling the rollback times out, then + * {@link PackageManager} will NOT send + * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts + * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}. + */ + public boolean isCancelled = false; + NewRollback(RollbackData data, int[] packageSessionIds) { this.data = data; this.packageSessionIds = packageSessionIds; } + + public void addToken(int token) { + mTokens.add(token); + } + + public boolean hasToken(int token) { + return mTokens.indexOf(token) != -1; + } } NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) { diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index 13b5b9ac104e9..beff0c6bc3088 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -35,6 +35,7 @@ import android.content.pm.VersionedPackage; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.provider.DeviceConfig; +import android.provider.Settings; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -60,6 +61,12 @@ public class RollbackTest { private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B"; private static final String INSTRUMENTED_APP = "com.android.tests.rollback"; + // copied from PackageManagerService#PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS + // TODO: find a better place for the property so that it can be imported in tests + // maybe android.content.pm.PackageManager? + private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = + "enable_rollback_timeout"; + /** * Test basic rollbacks. */ @@ -952,6 +959,38 @@ public class RollbackTest { } } + @Test + public void testEnableRollbackTimeoutFailsRollback() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS, + Manifest.permission.MANAGE_ROLLBACKS, + Manifest.permission.WRITE_DEVICE_CONFIG); + + //setting the timeout to a very short amount that will definitely be triggered + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS, + Long.toString(1), false /* makeDefault*/); + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + RollbackTestUtils.install("RollbackTestAppAv2.apk", true); + + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); + } finally { + //setting the timeout back to default + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS, + null, false /* makeDefault*/); + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + // Helper function to test that the given rollback info is a rollback for // the atomic set {A2, B2} -> {A1, B1}. private void assertRollbackInfoForAandB(RollbackInfo rollback) {