summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java9
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java151
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