diff options
| author | 2020-11-03 21:09:12 +0000 | |
|---|---|---|
| committer | 2020-11-03 21:09:12 +0000 | |
| commit | 24038f61c59e012a35655a6a4b9f28535a5dc91d (patch) | |
| tree | 5cfd0dc52ec58099509882710e6f0924fc444bad | |
| parent | 6d5bc48a77e462f926603cb8767d31ea4e3d373d (diff) | |
| parent | 4d60cca9f7061611e6b621202bac8d74b7c1c826 (diff) | |
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
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageInstallerService.java | 9 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/StagingManager.java | 151 |
2 files changed, 105 insertions, 55 deletions
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 330f99523507..56ef468407c4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -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(); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 600357b7697f..f779b6e2cc82 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -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,42 +648,20 @@ 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); - Slog.i(TAG, "APEX packages in session " + session.sessionId - + " were successfully activated. Proceeding with APK packages, if any"); - } - // 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; + if (hasApex) { + checkInstallationOfApkInApexSuccessful(session); + snapshotAndRestoreForApexSession(session); + Slog.i(TAG, "APEX packages in session " + session.sessionId + + " were successfully activated. Proceeding with APK packages, if any"); } + // 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); 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. - resumeSession(session); + 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,19 +1326,26 @@ public class StagingManager { onPreRebootVerificationComplete(sessionId); return; } - switch (msg.what) { - case MSG_PRE_REBOOT_VERIFICATION_START: - handlePreRebootVerification_Start(session); - break; - case MSG_PRE_REBOOT_VERIFICATION_APEX: - handlePreRebootVerification_Apex(session); - break; - case MSG_PRE_REBOOT_VERIFICATION_APK: - handlePreRebootVerification_Apk(session); - break; - case MSG_PRE_REBOOT_VERIFICATION_END: - handlePreRebootVerification_End(session); - break; + try { + switch (msg.what) { + case MSG_PRE_REBOOT_VERIFICATION_START: + handlePreRebootVerification_Start(session); + break; + case MSG_PRE_REBOOT_VERIFICATION_APEX: + handlePreRebootVerification_Apex(session); + break; + case MSG_PRE_REBOOT_VERIFICATION_APK: + handlePreRebootVerification_Apk(session); + break; + case MSG_PRE_REBOOT_VERIFICATION_END: + 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); } } @@ -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 |