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

Commit 24038f61 authored by Mohammad Samiul Islam's avatar Mohammad Samiul Islam Committed by Android (Google) Code Review
Browse files

Merge changes I5c190938,I9abec5d2,If1007966 into rvc-qpr-dev

* changes:
  [DO NOT MERGE] Prevent extra sessions owned by staged install from living across restarts
  Prevent exceptions during staged install from crashing system server
  Prevent exceptions in pre-reboot verification from crashing system server
parents 6d5bc48a 4d60cca9
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -393,6 +393,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                            Slog.w(TAG, "Abandoning old session created at "
                                        + session.createdMillis);
                            valid = false;
                        } else if (isExtraSessionForStagedInstall(session)) {
                            valid = false;
                        } else {
                            valid = true;
                        }
@@ -423,6 +425,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
        }
    }

    // Extra sessions are created during staged install on temporary basis. They should not be
    // allowed to live across system server restart.
    private boolean isExtraSessionForStagedInstall(PackageInstallerSession session) {
        return (session.params.installFlags & PackageManager.INSTALL_DRY_RUN) != 0
                || (session.params.installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0;
    }

    @GuardedBy("mSessions")
    private void addHistoricalSessionLocked(PackageInstallerSession session) {
        CharArrayWriter writer = new CharArrayWriter();
+96 −55
Original line number Diff line number Diff line
@@ -564,7 +564,8 @@ public class StagingManager {
        }
    }

    private void resumeSession(@NonNull PackageInstallerSession session) {
    private void resumeSession(@NonNull PackageInstallerSession session)
            throws PackageManagerException {
        Slog.d(TAG, "Resuming session " + session.sessionId);

        final boolean hasApex = sessionContainsApex(session);
@@ -628,10 +629,8 @@ public class StagingManager {
            if (apexSessionInfo == null) {
                final String errorMsg = "apexd did not know anything about a staged session "
                        + "supposed to be activated";
                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        errorMsg);
                abortCheckpoint(session.sessionId, errorMsg);
                return;
                throw new PackageManagerException(
                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
            }
            if (isApexSessionFailed(apexSessionInfo)) {
                String errorMsg = "APEX activation failed. Check logcat messages from apexd "
@@ -640,10 +639,8 @@ public class StagingManager {
                    errorMsg = "Session reverted due to crashing native process: "
                            + mNativeFailureReason;
                }
                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        errorMsg);
                abortCheckpoint(session.sessionId, errorMsg);
                return;
                throw new PackageManagerException(
                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
            }
            if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
                // Apexd did not apply the session for some unknown reason. There is no
@@ -651,14 +648,11 @@ public class StagingManager {
                // it as failed.
                final String errorMsg = "Staged session " + session.sessionId + "at boot "
                        + "didn't activate nor fail. Marking it as failed anyway.";
                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        errorMsg);
                abortCheckpoint(session.sessionId, errorMsg);
                return;
                throw new PackageManagerException(
                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
            }
        }
        // Handle apk and apk-in-apex installation
        try {
        if (hasApex) {
            checkInstallationOfApkInApexSuccessful(session);
            snapshotAndRestoreForApexSession(session);
@@ -668,25 +662,6 @@ public class StagingManager {
        // The APEX part of the session is activated, proceed with the installation of APKs.
        Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
        installApksInSession(session);
        } catch (PackageManagerException e) {
            session.setStagedSessionFailed(e.error, e.getMessage());
            abortCheckpoint(session.sessionId, e.getMessage());

            // If checkpoint is not supported, we have to handle failure for one staged session.
            if (!hasApex) {
                return;
            }

            if (!mApexManager.revertActiveSessions()) {
                Slog.e(TAG, "Failed to abort APEXd session");
            } else {
                Slog.e(TAG,
                        "Successfully aborted apexd session. Rebooting device in order to revert "
                                + "to the previous state of APEXd.");
                mPowerManager.reboot(null);
            }
            return;
        }

        Slog.d(TAG, "Marking session " + session.sessionId + " as applied");
        session.setStagedSessionApplied();
@@ -722,6 +697,25 @@ public class StagingManager {
        return ret;
    }

    void onInstallationFailure(PackageInstallerSession session, PackageManagerException e) {
        session.setStagedSessionFailed(e.error, e.getMessage());
        abortCheckpoint(session.sessionId, e.getMessage());

        // If checkpoint is not supported, we have to handle failure for one staged session.
        if (!sessionContainsApex(session)) {
            return;
        }

        if (!mApexManager.revertActiveSessions()) {
            Slog.e(TAG, "Failed to abort APEXd session");
        } else {
            Slog.e(TAG,
                    "Successfully aborted apexd session. Rebooting device in order to revert "
                            + "to the previous state of APEXd.");
            mPowerManager.reboot(null);
        }
    }

    @NonNull
    private PackageInstallerSession createAndWriteApkSession(
            @NonNull PackageInstallerSession originalSession, boolean preReboot)
@@ -1072,6 +1066,26 @@ public class StagingManager {
        return true;
    }

    /**
     * Ensure that there is no active apex session staged in apexd for the given session.
     *
     * @return returns true if it is ensured that there is no active apex session, otherwise false
     */
    private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
        if (!sessionContainsApex(session)) {
            return true;
        }
        final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
        if (apexSession == null || isApexSessionFinalized(apexSession)) {
            return true;
        }
        try {
            return mApexManager.abortStagedSession(session.sessionId);
        } catch (PackageManagerException ignore) {
            return false;
        }
    }

    private boolean isApexSessionFinalized(ApexSessionInfo session) {
        /* checking if the session is in a final state, i.e., not active anymore */
        return session.isUnknown || session.isActivationFailed || session.isSuccess
@@ -1165,7 +1179,16 @@ public class StagingManager {
        } else {
            // Session had already being marked ready. Start the checks to verify if there is any
            // follow-up work.
            try {
                resumeSession(session);
            } catch (PackageManagerException e) {
                onInstallationFailure(session, e);
            } catch (Exception e) {
                Slog.e(TAG, "Staged install failed due to unhandled exception", e);
                onInstallationFailure(session, new PackageManagerException(
                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        "Staged install failed due to unhandled exception: " + e));
            }
        }
    }

@@ -1303,6 +1326,7 @@ public class StagingManager {
                onPreRebootVerificationComplete(sessionId);
                return;
            }
            try {
                switch (msg.what) {
                    case MSG_PRE_REBOOT_VERIFICATION_START:
                        handlePreRebootVerification_Start(session);
@@ -1317,6 +1341,12 @@ public class StagingManager {
                        handlePreRebootVerification_End(session);
                        break;
                }
            } catch (Exception e) {
                Slog.e(TAG, "Pre-reboot verification failed due to unhandled exception", e);
                onPreRebootVerificationFailure(session,
                        SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                        "Pre-reboot verification failed due to unhandled exception: " + e);
            }
        }

        // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
@@ -1352,6 +1382,17 @@ public class StagingManager {
            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
        }

        private void onPreRebootVerificationFailure(PackageInstallerSession session,
                @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
            if (!ensureActiveApexSessionIsAborted(session)) {
                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
                // Safe to ignore active apex session abortion failure since session will be marked
                // failed on next step and staging directory for session will be deleted.
            }
            session.setStagedSessionFailed(errorCode, errorMessage);
            onPreRebootVerificationComplete(session.sessionId);
        }

        // Things to do when pre-reboot verification completes for a particular sessionId
        private void onPreRebootVerificationComplete(int sessionId) {
            // Remove it from mVerificationRunning so that verification is considered complete