Loading services/core/java/com/android/server/rollback/Rollback.java +1 −1 Original line number Diff line number Diff line Loading @@ -360,7 +360,7 @@ class Rollback { */ boolean enableForPackageInApex(String packageName, long installedVersion, int rollbackDataPolicy) { // TODO(b/142712057): Extract the new version number of apk-in-apex // TODO(b/147666157): Extract the new version number of apk-in-apex // The new version for the apk-in-apex is set to 0 for now. If the package is then further // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced() // will be called and this rollback will be deleted. Other ways of package update have not Loading services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +17 −0 Original line number Diff line number Diff line Loading @@ -213,6 +213,23 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) { return rollback; } // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have // to rely on complicated reasoning as below // Due to b/147666157, for apk in apex, we do not know the version we are rolling // back from. But if a package X is embedded in apex A exclusively (not embedded in // any other apex), which is not guaranteed, then it is sufficient to check only // package names here, as the version of failedPackage and the PackageRollbackInfo // can't be different. If failedPackage has a higher version, then it must have // been updated somehow. There are two ways: it was updated by an update of apex A // or updated directly as apk. In both cases, this rollback would have gotten // expired when onPackageReplaced() was called. Since the rollback exists, it has // same version as failedPackage. if (packageRollback.isApkInApex() && packageRollback.getVersionRolledBackFrom().getPackageName() .equals(failedPackage.getPackageName())) { return rollback; } } } return null; Loading tests/RollbackTest/Android.bp +14 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,10 @@ android_test { static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"], test_suites: ["general-tests"], test_config: "RollbackTest.xml", java_resources: [":com.android.apex.apkrollback.test_v2"], java_resources: [ ":com.android.apex.apkrollback.test_v2", ":com.android.apex.apkrollback.test_v2Crashing" ], } java_test_host { Loading Loading @@ -80,3 +83,13 @@ apex { apps: ["TestAppAv2"], installable: false, } apex { name: "com.android.apex.apkrollback.test_v2Crashing", manifest: "testdata/manifest_v2.json", androidManifest: "testdata/AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.apkrollback.test.key", apps: ["TestAppACrashingV2"], installable: false, } No newline at end of file tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +40 −4 Original line number Diff line number Diff line Loading @@ -500,8 +500,9 @@ public class StagedRollbackTest { APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex"); private static final TestApp TEST_APP_A_V2_UNKNOWN = new TestApp("Av2Unknown", TestApp.A, 0, /*isApex*/false, "TestAppAv2.apk"); private static final TestApp TEST_APEX_WITH_APK_V2_CRASHING = new TestApp( "TestApexWithApkV2Crashing", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2Crashing.apex"); @Test public void testRollbackApexWithApk_Phase1() throws Exception { Loading @@ -523,7 +524,7 @@ public class StagedRollbackTest { assertThat(available).isStaged(); assertThat(available).packagesContainsExactly( Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1)); Rollback.from(TestApp.A, 0).to(TestApp.A1)); RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2); RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); Loading @@ -531,7 +532,7 @@ public class StagedRollbackTest { assertThat(committed).isStaged(); assertThat(committed).packagesContainsExactly( Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1)); Rollback.from(TestApp.A, 0).to(TestApp.A1)); assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2); assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); Loading @@ -548,6 +549,41 @@ public class StagedRollbackTest { InstallUtils.processUserData(TestApp.A); } /** * Installs an apex with an apk that can crash. */ @Test public void testRollbackApexWithApkCrashing_Phase1() throws Exception { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); int sessionId = Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged() .setEnableRollback().commit(); InstallUtils.waitForSessionReady(sessionId); } /** * Verifies rollback has been enabled successfully. Then makes TestApp.A crash. */ @Test public void testRollbackApexWithApkCrashing_Phase2() throws Exception { assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME); assertThat(available).isStaged(); assertThat(available).packagesContainsExactly( Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TestApp.A, 0).to(TestApp.A1)); // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback RollbackUtils.sendCrashBroadcast(TestApp.A, 5); } @Test public void testRollbackApexWithApkCrashing_Phase3() throws Exception { assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); } private static void runShellCommand(String cmd) { ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeShellCommand(cmd); Loading tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +28 −0 Original line number Diff line number Diff line Loading @@ -261,6 +261,34 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { runPhase("testRollbackApexWithApk_Phase3"); } /** * Tests that RollbackPackageHealthObserver is observing apk-in-apex. */ @Test public void testRollbackApexWithApkCrashing() throws Exception { getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; final File apex = buildHelper.getTestFile(fileName); if (!getDevice().isAdbRoot()) { getDevice().enableAdbRoot(); } getDevice().remountSystemWritable(); assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); getDevice().reboot(); // Install an apex with apk that crashes runPhase("testRollbackApexWithApkCrashing_Phase1"); getDevice().reboot(); // Verify apex was installed and then crash the apk runPhase("testRollbackApexWithApkCrashing_Phase2"); // Wait for crash to trigger rollback assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); getDevice().waitForDeviceAvailable(); // Verify rollback occurred due to crash of apk-in-apex runPhase("testRollbackApexWithApkCrashing_Phase3"); } private void crashProcess(String processName, int numberOfCrashes) throws Exception { String pid = ""; String lastPid = "invalid"; Loading Loading
services/core/java/com/android/server/rollback/Rollback.java +1 −1 Original line number Diff line number Diff line Loading @@ -360,7 +360,7 @@ class Rollback { */ boolean enableForPackageInApex(String packageName, long installedVersion, int rollbackDataPolicy) { // TODO(b/142712057): Extract the new version number of apk-in-apex // TODO(b/147666157): Extract the new version number of apk-in-apex // The new version for the apk-in-apex is set to 0 for now. If the package is then further // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced() // will be called and this rollback will be deleted. Other ways of package update have not Loading
services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +17 −0 Original line number Diff line number Diff line Loading @@ -213,6 +213,23 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) { return rollback; } // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have // to rely on complicated reasoning as below // Due to b/147666157, for apk in apex, we do not know the version we are rolling // back from. But if a package X is embedded in apex A exclusively (not embedded in // any other apex), which is not guaranteed, then it is sufficient to check only // package names here, as the version of failedPackage and the PackageRollbackInfo // can't be different. If failedPackage has a higher version, then it must have // been updated somehow. There are two ways: it was updated by an update of apex A // or updated directly as apk. In both cases, this rollback would have gotten // expired when onPackageReplaced() was called. Since the rollback exists, it has // same version as failedPackage. if (packageRollback.isApkInApex() && packageRollback.getVersionRolledBackFrom().getPackageName() .equals(failedPackage.getPackageName())) { return rollback; } } } return null; Loading
tests/RollbackTest/Android.bp +14 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,10 @@ android_test { static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"], test_suites: ["general-tests"], test_config: "RollbackTest.xml", java_resources: [":com.android.apex.apkrollback.test_v2"], java_resources: [ ":com.android.apex.apkrollback.test_v2", ":com.android.apex.apkrollback.test_v2Crashing" ], } java_test_host { Loading Loading @@ -80,3 +83,13 @@ apex { apps: ["TestAppAv2"], installable: false, } apex { name: "com.android.apex.apkrollback.test_v2Crashing", manifest: "testdata/manifest_v2.json", androidManifest: "testdata/AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.apkrollback.test.key", apps: ["TestAppACrashingV2"], installable: false, } No newline at end of file
tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +40 −4 Original line number Diff line number Diff line Loading @@ -500,8 +500,9 @@ public class StagedRollbackTest { APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex"); private static final TestApp TEST_APP_A_V2_UNKNOWN = new TestApp("Av2Unknown", TestApp.A, 0, /*isApex*/false, "TestAppAv2.apk"); private static final TestApp TEST_APEX_WITH_APK_V2_CRASHING = new TestApp( "TestApexWithApkV2Crashing", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2Crashing.apex"); @Test public void testRollbackApexWithApk_Phase1() throws Exception { Loading @@ -523,7 +524,7 @@ public class StagedRollbackTest { assertThat(available).isStaged(); assertThat(available).packagesContainsExactly( Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1)); Rollback.from(TestApp.A, 0).to(TestApp.A1)); RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2); RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); Loading @@ -531,7 +532,7 @@ public class StagedRollbackTest { assertThat(committed).isStaged(); assertThat(committed).packagesContainsExactly( Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1)); Rollback.from(TestApp.A, 0).to(TestApp.A1)); assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2); assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); Loading @@ -548,6 +549,41 @@ public class StagedRollbackTest { InstallUtils.processUserData(TestApp.A); } /** * Installs an apex with an apk that can crash. */ @Test public void testRollbackApexWithApkCrashing_Phase1() throws Exception { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); int sessionId = Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged() .setEnableRollback().commit(); InstallUtils.waitForSessionReady(sessionId); } /** * Verifies rollback has been enabled successfully. Then makes TestApp.A crash. */ @Test public void testRollbackApexWithApkCrashing_Phase2() throws Exception { assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME); assertThat(available).isStaged(); assertThat(available).packagesContainsExactly( Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TestApp.A, 0).to(TestApp.A1)); // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback RollbackUtils.sendCrashBroadcast(TestApp.A, 5); } @Test public void testRollbackApexWithApkCrashing_Phase3() throws Exception { assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); } private static void runShellCommand(String cmd) { ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeShellCommand(cmd); Loading
tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +28 −0 Original line number Diff line number Diff line Loading @@ -261,6 +261,34 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { runPhase("testRollbackApexWithApk_Phase3"); } /** * Tests that RollbackPackageHealthObserver is observing apk-in-apex. */ @Test public void testRollbackApexWithApkCrashing() throws Exception { getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; final File apex = buildHelper.getTestFile(fileName); if (!getDevice().isAdbRoot()) { getDevice().enableAdbRoot(); } getDevice().remountSystemWritable(); assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); getDevice().reboot(); // Install an apex with apk that crashes runPhase("testRollbackApexWithApkCrashing_Phase1"); getDevice().reboot(); // Verify apex was installed and then crash the apk runPhase("testRollbackApexWithApkCrashing_Phase2"); // Wait for crash to trigger rollback assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5))); getDevice().waitForDeviceAvailable(); // Verify rollback occurred due to crash of apk-in-apex runPhase("testRollbackApexWithApkCrashing_Phase3"); } private void crashProcess(String processName, int numberOfCrashes) throws Exception { String pid = ""; String lastPid = "invalid"; Loading