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

Commit f1d9180e authored by Dario Freni's avatar Dario Freni Committed by android-build-merger
Browse files

Merge "Verify staged APKs pre-reboot." into qt-dev

am: e5e87626

Change-Id: Id30f05784a99abbe2a4d4d21c8fc9b04f2b61525
parents 62a1a7d8 e5e87626
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -727,6 +727,7 @@ public abstract class PackageManager {
            INSTALL_ENABLE_ROLLBACK,
            INSTALL_ALLOW_DOWNGRADE,
            INSTALL_STAGED,
            INSTALL_DRY_RUN,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface InstallFlags {}
@@ -904,6 +905,14 @@ public abstract class PackageManager {
     */
    public static final int INSTALL_STAGED = 0x00200000;

    /**
     * Flag parameter for {@link #installPackage} to indicate that package should only be verified
     * but not installed.
     *
     * @hide
     */
    public static final int INSTALL_DRY_RUN = 0x00800000;

    /** @hide */
    @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
            DONT_KILL_APP
+16 −0
Original line number Diff line number Diff line
@@ -15566,6 +15566,22 @@ public class PackageManagerService extends IPackageManager.Stub
        @Override
        void handleReturnCode() {
            if (mVerificationCompleted && mEnableRollbackCompleted) {
                if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
                    String packageName = "";
                    try {
                        PackageLite packageInfo =
                                new PackageParser().parsePackageLite(origin.file, 0);
                        packageName = packageInfo.packageName;
                    } catch (PackageParserException e) {
                        Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e);
                    }
                    try {
                        observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
                    } catch (RemoteException e) {
                        Slog.i(TAG, "Observer no longer exists.");
                    }
                    return;
                }
                if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                    mRet = mArgs.copyApk();
                }
+55 −27
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
@@ -201,10 +202,6 @@ public class StagingManager {
    private void preRebootVerification(@NonNull PackageInstallerSession session) {
        boolean success = true;

        // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
        // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
        // right away.

        final ApexInfoList apexInfoList = new ApexInfoList();
        // APEX checks. For single-package sessions, check if they contain an APEX. For
        // multi-package sessions, find all the child sessions that contain an APEX.
@@ -230,6 +227,16 @@ public class StagingManager {
            return;
        }

        if (sessionContainsApk(session)) {
            if (!installApksInSession(session, /* preReboot */ true)) {
                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                        "APK verification failed. Check logcat messages for "
                                + "more information.");
                // TODO(b/118865310): abort the session on apexd.
                return;
            }
        }

        if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) {
            // For APEXes, we validate the signature here before we mark the session as ready,
            // so we fail the session early if there is a signature mismatch. For APKs, the
@@ -275,21 +282,31 @@ public class StagingManager {
        }
    }

    private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {

    private boolean sessionContains(@NonNull PackageInstallerSession session,
                                    Predicate<PackageInstallerSession> filter) {
        if (!session.isMultiPackage()) {
            return isApexSession(session);
            return filter.test(session);
        }
        synchronized (mStagedSessions) {
            return !(Arrays.stream(session.getChildSessionIds())
                    // Retrieve cached sessions matching ids.
                    .mapToObj(i -> mStagedSessions.get(i))
                    // Filter only the ones containing APEX.
                    .filter(childSession -> isApexSession(childSession))
                    .filter(childSession -> filter.test(childSession))
                    .collect(Collectors.toList())
                    .isEmpty());
        }
    }

    private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
        return sessionContains(session, (s) -> isApexSession(s));
    }

    private boolean sessionContainsApk(@NonNull PackageInstallerSession session) {
        return sessionContains(session, (s) -> !isApexSession(s));
    }

    private void resumeSession(@NonNull PackageInstallerSession session) {
        boolean hasApex = sessionContainsApex(session);
        if (hasApex) {
@@ -326,7 +343,7 @@ public class StagingManager {
            }
        }
        // The APEX part of the session is activated, proceed with the installation of APKs.
        if (!installApksInSession(session)) {
        if (!installApksInSession(session, /* preReboot */ false)) {
            session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                    "Staged installation of APKs failed. Check logcat messages for"
                        + "more information.");
@@ -343,7 +360,6 @@ public class StagingManager {
                                + "to the previous state of APEXd.");
                mPowerManager.reboot(null);
            }

            return;
        }

@@ -366,7 +382,7 @@ public class StagingManager {
    }

    private PackageInstallerSession createAndWriteApkSession(
            @NonNull PackageInstallerSession originalSession) {
            @NonNull PackageInstallerSession originalSession, boolean preReboot) {
        if (originalSession.stageDir == null) {
            Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
            return null;
@@ -379,9 +395,14 @@ public class StagingManager {

        PackageInstaller.SessionParams params = originalSession.params.copy();
        params.isStaged = false;
        params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
        params.installFlags |= PackageManager.INSTALL_STAGED;
        // TODO(b/129744602): use the userid from the original session.
        if (preReboot) {
            params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
            params.installFlags |= PackageManager.INSTALL_DRY_RUN;
        } else {
            params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
        }
        int apkSessionId = mPi.createSession(
                params, originalSession.getInstallerPackageName(),
                0 /* UserHandle.SYSTEM */);
@@ -408,8 +429,9 @@ public class StagingManager {
    }

    private boolean commitApkSession(@NonNull PackageInstallerSession apkSession,
                                     int originalSessionId) {
                                     int originalSessionId, boolean preReboot) {

        if (!preReboot) {
            if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                // If rollback is available for this session, notify the rollback
                // manager of the apk session so it can properly enable rollback.
@@ -421,6 +443,7 @@ public class StagingManager {
                    // Cannot happen, the rollback manager is in the same process.
                }
            }
        }

        final LocalIntentReceiver receiver = new LocalIntentReceiver();
        apkSession.commit(receiver.getIntentSender(), false);
@@ -435,14 +458,15 @@ public class StagingManager {
        return false;
    }

    private boolean installApksInSession(@NonNull PackageInstallerSession session) {
    private boolean installApksInSession(@NonNull PackageInstallerSession session,
                                         boolean preReboot) {
        if (!session.isMultiPackage() && !isApexSession(session)) {
            // APK single-packaged staged session. Do a regular install.
            PackageInstallerSession apkSession = createAndWriteApkSession(session);
            PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
            if (apkSession == null) {
                return false;
            }
            return commitApkSession(apkSession, session.sessionId);
            return commitApkSession(apkSession, session.sessionId, preReboot);
        } else if (session.isMultiPackage()) {
            // For multi-package staged sessions containing APKs, we identify which child sessions
            // contain an APK, and with those then create a new multi-package group of sessions,
@@ -464,6 +488,9 @@ public class StagingManager {
            }
            PackageInstaller.SessionParams params = session.params.copy();
            params.isStaged = false;
            if (preReboot) {
                params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
            }
            // TODO(b/129744602): use the userid from the original session.
            int apkParentSessionId = mPi.createSession(
                    params, session.getInstallerPackageName(),
@@ -478,7 +505,8 @@ public class StagingManager {
            }

            for (PackageInstallerSession sessionToClone : childSessions) {
                PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone);
                PackageInstallerSession apkChildSession =
                        createAndWriteApkSession(sessionToClone, preReboot);
                if (apkChildSession == null) {
                    return false;
                }
@@ -489,7 +517,7 @@ public class StagingManager {
                    return false;
                }
            }
            return commitApkSession(apkParentSession, session.sessionId);
            return commitApkSession(apkParentSession, session.sessionId, preReboot);
        }
        // APEX single-package staged session, nothing to do.
        return true;