Prevent exceptions in pre-reboot verification from crashing system server

An unhandled exception during pre-reboot verification will cause system
server to crash and restart. Upon restart, pre-reboot verification will
be retried and it will cause system server to crash again. Thus creating
a loop.

Instead of allowing the exception to crash the system server, we now
catch it and fail the corresponding staged session with appropriate
message.

Bug: 170784748
Test: manual
Test: verified that without this fix, any unhandled exception sends
system server into a crash loop

Change-Id: Ie3343681601c072add0b2d50cbaa2e088a27d94b
Merged-In: If100796687906be67bc02ea63a78e1b6e291ce7a
(cherry picked from commit 5a2548589560404f9fce4914216564b0333895d4)
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index dd71331..96dd001 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -202,85 +202,92 @@
     }
 
     private void preRebootVerification(@NonNull PackageInstallerSession session) {
-        boolean success = true;
+        try {
+            boolean success = true;
 
-        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.
-        if (!session.isMultiPackage()
-                && isApexSession(session)) {
-            success = submitSessionToApexService(session, null, apexInfoList);
+            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.
+            if (!session.isMultiPackage()
+                    && isApexSession(session)) {
+                success = submitSessionToApexService(session, null, apexInfoList);
 
-        } else if (session.isMultiPackage()) {
-            List<PackageInstallerSession> childSessions =
-                    Arrays.stream(session.getChildSessionIds())
-                            // Retrieve cached sessions matching ids.
-                            .mapToObj(i -> mStagedSessions.get(i))
-                            // Filter only the ones containing APEX.
-                            .filter(childSession -> isApexSession(childSession))
-                            .collect(Collectors.toList());
-            if (!childSessions.isEmpty()) {
-                success = submitSessionToApexService(session, childSessions, apexInfoList);
-            } // else this is a staged multi-package session with no APEX files.
-        }
+            } else if (session.isMultiPackage()) {
+                List<PackageInstallerSession> childSessions =
+                        Arrays.stream(session.getChildSessionIds())
+                                // Retrieve cached sessions matching ids.
+                                .mapToObj(i -> mStagedSessions.get(i))
+                                // Filter only the ones containing APEX.
+                                .filter(childSession -> isApexSession(childSession))
+                                .collect(Collectors.toList());
+                if (!childSessions.isEmpty()) {
+                    success = submitSessionToApexService(session, childSessions, apexInfoList);
+                } // else this is a staged multi-package session with no APEX files.
+            }
 
-        if (!success) {
-            // submitSessionToApexService will populate error.
-            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.
+            if (!success) {
+                // submitSessionToApexService will populate error.
                 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
-            // signature verification will be done by the package manager at the point at which
-            // it applies the staged install.
-            for (ApexInfo apexPackage : apexInfoList.apexInfos) {
-                if (!validateApexSignature(apexPackage.packagePath,
-                        apexPackage.packageName)) {
+            if (sessionContainsApk(session)) {
+                if (!installApksInSession(session, /* preReboot */ true)) {
                     session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                            "APK-container signature verification failed for package "
-                                    + apexPackage.packageName + ". Signature of file "
-                                    + apexPackage.packagePath + " does not match the signature of "
-                                    + " the package already installed.");
+                            "APK verification failed. Check logcat messages for "
+                                    + "more information.");
                     // TODO(b/118865310): abort the session on apexd.
                     return;
                 }
             }
-        }
 
-        if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
-            // If rollback is enabled for this session, we call through to the RollbackManager
-            // with the list of sessions it must enable rollback for. Note that notifyStagedSession
-            // is a synchronous operation.
-            final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
-            try {
-                // NOTE: To stay consistent with the non-staged install flow, we don't fail the
-                // entire install if rollbacks can't be enabled.
-                if (!rm.notifyStagedSession(session.sessionId)) {
-                    Slog.e(TAG, "Unable to enable rollback for session: " + session.sessionId);
+            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
+                // signature verification will be done by the package manager at the point at which
+                // it applies the staged install.
+                for (ApexInfo apexPackage : apexInfoList.apexInfos) {
+                    if (!validateApexSignature(apexPackage.packagePath,
+                            apexPackage.packageName)) {
+                        session.setStagedSessionFailed(
+                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                                "APK-container signature verification failed for package "
+                                        + apexPackage.packageName + ". Signature of file "
+                                        + apexPackage.packagePath + " does not match the signature"
+                                        + " of the package already installed.");
+                        // TODO(b/118865310): abort the session on apexd.
+                        return;
+                    }
                 }
-            } catch (RemoteException re) {
-                // Cannot happen, the rollback manager is in the same process.
             }
-        }
 
-        session.setStagedSessionReady();
-        if (sessionContainsApex(session)
-                && !mApexManager.markStagedSessionReady(session.sessionId)) {
+            if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+                // If rollback is enabled for this session, we call through to the RollbackManager
+                // with the list of sessions it must enable rollback for. Note that
+                // notifyStagedSession is a synchronous operation.
+                final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                        ServiceManager.getService(Context.ROLLBACK_SERVICE));
+                try {
+                    // NOTE: To stay consistent with the non-staged install flow, we don't fail the
+                    // entire install if rollbacks can't be enabled.
+                    if (!rm.notifyStagedSession(session.sessionId)) {
+                        Slog.e(TAG, "Unable to enable rollback for session: " + session.sessionId);
+                    }
+                } catch (RemoteException re) {
+                    // Cannot happen, the rollback manager is in the same process.
+                }
+            }
+
+            session.setStagedSessionReady();
+            if (sessionContainsApex(session)
+                    && !mApexManager.markStagedSessionReady(session.sessionId)) {
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                        "APEX staging failed, check logcat messages from apexd for more "
+                                + "details.");
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Pre-reboot verification failed due to unhandled exception", e);
             session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                            "APEX staging failed, check logcat messages from apexd for more "
-                            + "details.");
+                    "Pre-reboot verification failed due to unhandled exception: " + e);
         }
     }