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

Commit 8debcf17 authored by JW Wang's avatar JW Wang Committed by Android (Google) Code Review
Browse files

Merge "Duplicate packages should fail to install"

parents cc38149b d1193502
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -1637,7 +1637,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     * If session should be sealed, then it's sealed to prevent further modification.
     * If the session can't be sealed then it's destroyed.
     *
     * Additionally for staged APEX sessions read+validate the package and populate req'd fields.
     * Additionally for staged APEX/APK sessions read+validate the package and populate req'd
     * fields.
     *
     * <p> This is meant to be called after all of the sessions are loaded and added to
     * PackageInstallerService
@@ -1670,6 +1671,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    // APEX installations rely on certain fields to be populated after reboot.
                    // E.g. mPackageName.
                    validateApexInstallLocked();
                } else {
                    // Populate mPackageName for this APK session which is required by the staging
                    // manager to check duplicate apk-in-apex.
                    PackageInstallerSession parent = allSessions.get(mParentSessionId);
                    if (parent != null && parent.isStagedSessionReady()) {
                        validateApkInstallLocked();
                    }
                }
            } catch (PackageManagerException e) {
                Slog.e(TAG, "Package not valid", e);
+37 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -84,6 +85,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -650,6 +652,7 @@ public class StagingManager {
        try {
            if (hasApex) {
                checkInstallationOfApkInApexSuccessful(session);
                checkDuplicateApkInApex(session);
                snapshotAndRestoreForApexSession(session);
                Slog.i(TAG, "APEX packages in session " + session.sessionId
                        + " were successfully activated. Proceeding with APK packages, if any");
@@ -829,6 +832,40 @@ public class StagingManager {
        return null;
    }

    /**
     * Throws a PackageManagerException if there are duplicate packages in apk and apk-in-apex.
     */
    private void checkDuplicateApkInApex(@NonNull PackageInstallerSession session)
            throws PackageManagerException {
        if (!session.isMultiPackage()) {
            return;
        }
        final int[] childSessionIds = session.getChildSessionIds();
        final Set<String> apkNames = new ArraySet<>();
        synchronized (mStagedSessions) {
            for (int id : childSessionIds) {
                final PackageInstallerSession s = mStagedSessions.get(id);
                if (!isApexSession(s)) {
                    apkNames.add(s.getPackageName());
                }
            }
        }
        final List<PackageInstallerSession> apexSessions = extractApexSessions(session);
        for (PackageInstallerSession apexSession : apexSessions) {
            String packageName = apexSession.getPackageName();
            for (String apkInApex : mApexManager.getApksInApex(packageName)) {
                if (!apkNames.add(apkInApex)) {
                    throw new PackageManagerException(
                            SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                            "Package: " + packageName + " in session: "
                                    + apexSession.sessionId + " has duplicate apk-in-apex: "
                                    + apkInApex, null);

                }
            }
        }
    }

    private void installApksInSession(@NonNull PackageInstallerSession session)
            throws PackageManagerException {

+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ android_test_helper_app {
    srcs: ["app/src/**/*.java"],
    static_libs: ["androidx.test.rules", "cts-install-lib"],
    test_suites: ["general-tests"],
    java_resources: [
        ":com.android.apex.apkrollback.test_v2",
    ],
}

java_test_host {
+23 −0
Original line number Diff line number Diff line
@@ -49,6 +49,11 @@ import java.util.function.Consumer;
public class StagedInstallInternalTest {

    private static final String TAG = StagedInstallInternalTest.class.getSimpleName();
    private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
    private static final TestApp TEST_APEX_WITH_APK_V1 = new TestApp("TestApexWithApkV1",
            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 File mTestStateFile = new File(
            InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
@@ -81,6 +86,24 @@ public class StagedInstallInternalTest {
        Files.deleteIfExists(mTestStateFile.toPath());
    }

    @Test
    public void testDuplicateApkInApexShouldFail_Commit() throws Exception {
        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
        // Duplicate packages(TestApp.A) in TEST_APEX_WITH_APK_V2(apk-in-apex) and TestApp.A2(apk)
        // should fail to install.
        int sessionId = Install.multi(TEST_APEX_WITH_APK_V2, TestApp.A2).setStaged().commit();
        storeSessionId(sessionId);
    }

    @Test
    public void testDuplicateApkInApexShouldFail_Verify() throws Exception {
        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
        int sessionId = retrieveLastSessionId();
        PackageInstaller.SessionInfo info =
                InstallUtils.getPackageInstaller().getSessionInfo(sessionId);
        assertThat(info.isStagedSessionFailed()).isTrue();
    }

    @Test
    public void testSystemServerRestartDoesNotAffectStagedSessions_Commit() throws Exception {
        int sessionId = Install.single(TestApp.A1).setStaged().commit();
+55 −0
Original line number Diff line number Diff line
@@ -24,11 +24,14 @@ import static org.junit.Assume.assumeTrue;

import android.cts.install.lib.host.InstallUtilsHost;

import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tests.rollback.host.AbandonSessionsRule;
import com.android.tests.util.ModuleTestUtils;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.ProcessInfo;

import org.junit.After;
@@ -49,6 +52,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
    public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
    private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex";
    private static final String APK_A = "TestAppAv1.apk";
    private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";

    private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this);
    private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
@@ -74,6 +78,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
        } catch (AssertionError e) {
            Log.e(TAG, e);
        }
        deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
                "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
    }

    @Before
@@ -86,6 +92,55 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
        cleanUp();
    }

    /**
     * Deletes files and reboots the device if necessary.
     * @param files the paths of files which might contain wildcards
     */
    private void deleteFiles(String... files) throws Exception {
        boolean found = false;
        for (String file : files) {
            CommandResult result = getDevice().executeShellV2Command("ls " + file);
            if (result.getStatus() == CommandStatus.SUCCESS) {
                found = true;
                break;
            }
        }

        if (found) {
            if (!getDevice().isAdbRoot()) {
                getDevice().enableAdbRoot();
            }
            getDevice().remountSystemWritable();
            for (String file : files) {
                getDevice().executeShellCommand("rm -rf " + file);
            }
            getDevice().reboot();
        }
    }

    private void pushTestApex() throws Exception {
        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();
    }

    /**
     * Tests that duplicate packages in apk-in-apex and apk should fail to install.
     */
    @Test
    public void testDuplicateApkInApexShouldFail() throws Exception {
        pushTestApex();
        runPhase("testDuplicateApkInApexShouldFail_Commit");
        getDevice().reboot();
        runPhase("testDuplicateApkInApexShouldFail_Verify");
    }

    @Test
    public void testSystemServerRestartDoesNotAffectStagedSessions() throws Exception {
        runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Commit");