summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/content/pm/PackageInstaller.java9
-rw-r--r--non-updatable-api/current.txt1
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java12
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java57
5 files changed, 60 insertions, 20 deletions
diff --git a/api/current.txt b/api/current.txt
index 75ea7d7adc4c..eace0d6fcfe4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11900,6 +11900,7 @@ package android.content.pm {
field public static final int INVALID_ID = -1; // 0xffffffff
field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+ field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df9db278e095..bed7b26034e5 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2075,7 +2075,8 @@ public class PackageInstaller {
STAGED_SESSION_NO_ERROR,
STAGED_SESSION_VERIFICATION_FAILED,
STAGED_SESSION_ACTIVATION_FAILED,
- STAGED_SESSION_UNKNOWN})
+ STAGED_SESSION_UNKNOWN,
+ STAGED_SESSION_OTHER_ERROR})
@Retention(RetentionPolicy.SOURCE)
public @interface StagedSessionErrorCode{}
/**
@@ -2101,6 +2102,12 @@ public class PackageInstaller {
*/
public static final int STAGED_SESSION_UNKNOWN = 3;
+ /**
+ * Constant indicating that a known error occurred while processing this staged session, but
+ * the error could not be matched to other categories.
+ */
+ public static final int STAGED_SESSION_OTHER_ERROR = 4;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 66637ef93598..22ef33d9a434 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11900,6 +11900,7 @@ package android.content.pm {
field public static final int INVALID_ID = -1; // 0xffffffff
field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+ field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 92da005babfa..882986de09e9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3181,7 +3181,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** {@hide} */
void setStagedSessionReady() {
synchronized (mLock) {
- if (mDestroyed) return; // Do not allow destroyed staged session to change state
+ // Do not allow destroyed/failed staged session to change state
+ if (mDestroyed || mStagedSessionFailed) return;
mStagedSessionReady = true;
mStagedSessionApplied = false;
mStagedSessionFailed = false;
@@ -3192,10 +3193,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/** {@hide} */
- void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
- String errorMessage) {
+ void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
synchronized (mLock) {
- if (mDestroyed) return; // Do not allow destroyed staged session to change state
+ // Do not allow destroyed/failed staged session to change state
+ if (mDestroyed || mStagedSessionFailed) return;
mStagedSessionReady = false;
mStagedSessionApplied = false;
mStagedSessionFailed = true;
@@ -3210,7 +3211,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** {@hide} */
void setStagedSessionApplied() {
synchronized (mLock) {
- if (mDestroyed) return; // Do not allow destroyed staged session to change state
+ // Do not allow destroyed/failed staged session to change state
+ if (mDestroyed || mStagedSessionFailed) return;
mStagedSessionReady = false;
mStagedSessionApplied = true;
mStagedSessionFailed = false;
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 12591b48ad52..34afbfed36ff 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -870,10 +870,21 @@ public class StagingManager {
mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
}
- private int parentOrOwnSessionId(PackageInstallerSession session) {
+ private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
}
+ private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
+ return session.hasParentSessionId()
+ ? getStagedSession(session.getParentSessionId())
+ : session;
+ }
+
+ private boolean isRollback(PackageInstallerSession session) {
+ final PackageInstallerSession root = getParentSessionOrSelf(session);
+ return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ }
+
/**
* <p> Check if the session provided is non-overlapping with the active staged sessions.
*
@@ -899,6 +910,8 @@ public class StagingManager {
boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
Context.STORAGE_SERVICE)).isCheckpointSupported();
+ final boolean isRollback = isRollback(session);
+
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -913,8 +926,8 @@ public class StagingManager {
}
// Check if stagedSession has an active parent session or not
if (stagedSession.hasParentSessionId()) {
- int parentId = stagedSession.getParentSessionId();
- PackageInstallerSession parentSession = mStagedSessions.get(parentId);
+ final int parentId = stagedSession.getParentSessionId();
+ final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
if (parentSession == null || parentSession.isStagedAndInTerminalState()
|| parentSession.isDestroyed()) {
// Parent session has been abandoned or terminated already
@@ -930,21 +943,37 @@ public class StagingManager {
continue;
}
- // If session is not among the active sessions, then it cannot have same package
- // name as any of the active sessions.
+ // New session cannot have same package name as one of the active sessions
if (session.getPackageName().equals(stagedSession.getPackageName())) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
- "Package: " + session.getPackageName() + " in session: "
- + session.sessionId + " has been staged already by session: "
- + stagedSession.sessionId, null);
+ if (isRollback) {
+ // If the new session is a rollback, then it gets priority. The existing
+ // session is failed to unblock rollback.
+ final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+ if (!ensureActiveApexSessionIsAborted(root)) {
+ Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
+ // Safe to ignore active apex session abort failure since session
+ // will be marked failed on next step and staging directory for session
+ // will be deleted.
+ }
+ root.setStagedSessionFailed(
+ SessionInfo.STAGED_SESSION_OTHER_ERROR,
+ "Session was blocking rollback session: " + session.sessionId);
+ Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
+ + "blocking rollback session: " + session.sessionId);
+ } else {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+ "Package: " + session.getPackageName() + " in session: "
+ + session.sessionId + " has been staged already by session:"
+ + " " + stagedSession.sessionId, null);
+ }
}
// Staging multiple root sessions is not allowed if device doesn't support
// checkpoint. If session and stagedSession do not have common ancestor, they are
// from two different root sessions.
- if (!supportsCheckpoint
- && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+ if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
+ != getSessionIdForParentOrSelf(stagedSession)) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Cannot stage multiple sessions without checkpoint support", null);
@@ -1260,8 +1289,8 @@ public class StagingManager {
+ sessionId);
return;
}
- if (session.isDestroyed()) {
- // No point in running verification on a destroyed session
+ if (session.isDestroyed() || session.isStagedSessionFailed()) {
+ // No point in running verification on a destroyed/failed session
onPreRebootVerificationComplete(sessionId);
return;
}