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

Commit c240979f authored by JW Wang's avatar JW Wang
Browse files

Add unit tests (6/n)

Bug: 161121612
Test: atest PackageSessionVerifierTest
Change-Id: I8e3021ec7492489a9b60d69bf684d2a5e9168bc5
parent c0836c88
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import android.util.IntArray;
import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageHelper;
import com.android.server.LocalServices;
import com.android.server.pm.parsing.PackageParser2;
@@ -80,6 +81,15 @@ final class PackageSessionVerifier {
        mHandler = new Handler(looper);
    }

    @VisibleForTesting
    PackageSessionVerifier() {
        mContext = null;
        mPm = null;
        mApexManager = null;
        mPackageParserSupplier = null;
        mHandler = null;
    }

    /**
     * Runs verifications that are common to both staged and non-staged sessions.
     */
@@ -181,7 +191,8 @@ final class PackageSessionVerifier {
    /**
     * Stores staged-sessions for checking package overlapping and rollback conflicts.
     */
    private void storeSession(StagingManager.StagedSession session) {
    @VisibleForTesting
    void storeSession(StagingManager.StagedSession session) {
        if (session != null) {
            mStagedSessions.add(session);
        }
@@ -453,7 +464,8 @@ final class PackageSessionVerifier {
    /**
     * Fails this rebootless APEX session if the same package name found in any staged sessions.
     */
    private void checkRebootlessApex(PackageInstallerSession session)
    @VisibleForTesting
    void checkRebootlessApex(PackageInstallerSession session)
            throws PackageManagerException {
        if (session.isStaged() || !session.isApexSession()) {
            return;
@@ -483,13 +495,16 @@ final class PackageSessionVerifier {
     * supports checkpoint.
     */
    private void checkActiveSessions() throws PackageManagerException {
        final boolean supportsCheckpoint;
        try {
            supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
            checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint());
        } catch (RemoteException e) {
            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                    "Can't query fs-checkpoint status : " + e);
        }
    }

    @VisibleForTesting
    void checkActiveSessions(boolean supportsCheckpoint) throws PackageManagerException {
        int activeSessions = 0;
        for (StagingManager.StagedSession stagedSession : mStagedSessions) {
            if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
@@ -509,7 +524,8 @@ final class PackageSessionVerifier {
     * downgrade of SDK extension which in turn will result in dependency violation of other
     * non-rollback sessions.
     */
    private void checkRollbacks(StagingManager.StagedSession session)
    @VisibleForTesting
    void checkRollbacks(StagingManager.StagedSession session)
            throws PackageManagerException {
        for (StagingManager.StagedSession stagedSession : mStagedSessions) {
            if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
@@ -546,7 +562,8 @@ final class PackageSessionVerifier {
     * @param child The child session whose package name will be checked.
     *              This will equal to {@code parent} for a single-package session.
     */
    private void checkOverlaps(StagingManager.StagedSession parent,
    @VisibleForTesting
    void checkOverlaps(StagingManager.StagedSession parent,
            StagingManager.StagedSession child) throws PackageManagerException {
        final String packageName = child.getPackageName();
        if (packageName == null) {
+134 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.function.Predicate;

@Presubmit
@RunWith(JUnit4.class)
public class PackageSessionVerifierTest {
    private PackageSessionVerifier mSessionVerifier = new PackageSessionVerifier();

    @Test
    public void checkRebootlessApex() throws Exception {
        StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
        mSessionVerifier.storeSession(session1);

        // Should throw for package name conflicts
        PackageInstallerSession session2 = createSession(false, true, "com.foo");
        assertThrows(PackageManagerException.class,
                () -> mSessionVerifier.checkRebootlessApex(session2));

        // Shouldn't throw if no package name conflicts
        PackageInstallerSession session3 = createSession(false, true, "com.bar");
        mSessionVerifier.checkRebootlessApex(session3);
    }

    @Test
    public void checkActiveSessions() throws Exception {
        StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
        mSessionVerifier.storeSession(session1);
        // Shouldn't throw for a single session no matter if supporting checkpoint or not
        mSessionVerifier.checkActiveSessions(true);
        mSessionVerifier.checkActiveSessions(false);

        // Now we have multiple active sessions
        StagingManager.StagedSession session2 = createStagedSession(222, "com.bar", 2);
        mSessionVerifier.storeSession(session2);
        // Shouldn't throw if supporting checkpoint
        mSessionVerifier.checkActiveSessions(true);
        // Should throw if not supporting checkpoint
        assertThrows(PackageManagerException.class,
                () -> mSessionVerifier.checkActiveSessions(false));
    }

    @Test
    public void checkRollbacks() throws Exception {
        StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
        StagingManager.StagedSession session2 = createStagedSession(222, "com.bar", 2);
        session2.sessionParams().setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
        mSessionVerifier.storeSession(session1);
        mSessionVerifier.storeSession(session2);

        // Non-rollback session should fail
        mSessionVerifier.checkRollbacks(session2);
        verify(session1, times(1)).setSessionFailed(anyInt(), anyString());

        // Yet another non-rollback session should fail
        StagingManager.StagedSession session3 = createStagedSession(333, "com.baz", 3);
        assertThrows(PackageManagerException.class,
                () -> mSessionVerifier.checkRollbacks(session3));
    }

    @Test
    public void checkOverlaps() throws Exception {
        StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
        StagingManager.StagedSession session2 = createStagedSession(222, "com.foo", 2);
        mSessionVerifier.storeSession(session1);
        mSessionVerifier.storeSession(session2);
        // No exception should be thrown for the earlier session should not fail
        mSessionVerifier.checkOverlaps(session1, session1);
        // Later session should fail
        verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
        // Yet another later session should fail
        StagingManager.StagedSession session3 = createStagedSession(333, "com.foo", 3);
        assertThrows(PackageManagerException.class,
                () -> mSessionVerifier.checkOverlaps(session3, session3));
    }

    private PackageInstallerSession createSession(boolean isStaged, boolean isApex,
            String packageName) {
        PackageInstallerSession session = mock(PackageInstallerSession.class);
        when(session.isStaged()).thenReturn(isStaged);
        when(session.isApexSession()).thenReturn(isApex);
        when(session.getPackageName()).thenReturn(packageName);
        return session;
    }

    private StagingManager.StagedSession createStagedSession(int sessionId, String packageName,
            long committedMillis) {
        StagingManager.StagedSession session = mock(StagingManager.StagedSession.class);
        when(session.sessionId()).thenReturn(sessionId);
        when(session.getPackageName()).thenReturn(packageName);
        when(session.getCommittedMillis()).thenReturn(committedMillis);
        when(session.sessionContains(any())).then(invocation -> {
            Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
            return filter.test(session);
        });
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        when(session.sessionParams()).thenReturn(params);
        return session;
    }
}