Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d56ce000 authored by Mohammad Samiul Islam's avatar Mohammad Samiul Islam
Browse files

Make RollbackPackageHealthObserver observe apk-in-apex

Due to b/147666157, this CL will only work as long as an apk is not
embedded in two different apex.

Bug: 142712057
Test: StagedRollbackTest#testRollbackApexWithApkCrashing
Change-Id: I60f7f70b627fd380094a11373259debddd5dc532
parent d69517eb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -353,7 +353,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
+17 −0
Original line number Diff line number Diff line
@@ -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;
+14 −1
Original line number Diff line number Diff line
@@ -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 {
@@ -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
+40 −4
Original line number Diff line number Diff line
@@ -445,8 +445,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 {
@@ -468,7 +469,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());
@@ -476,7 +477,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);

@@ -493,6 +494,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);
+28 −0
Original line number Diff line number Diff line
@@ -226,6 +226,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";