Merge changes from topic "abandon-race" into rvc-dev
* changes: Fix racing condition between session abandonment and its verification Persist destroyed staged sessions until they are cleaned up
This commit is contained in:
committed by
Android (Google) Code Review
commit
6b58f8ca37
@@ -676,7 +676,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
|
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
|
||||||
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
|
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
|
||||||
installSource, params, createdMillis,
|
installSource, params, createdMillis,
|
||||||
stageDir, stageCid, null, false, false, false, null, SessionInfo.INVALID_ID,
|
stageDir, stageCid, null, false, false, false, false, null, SessionInfo.INVALID_ID,
|
||||||
false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
|
false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
|
||||||
|
|
||||||
synchronized (mSessions) {
|
synchronized (mSessions) {
|
||||||
@@ -830,7 +830,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
synchronized (mSessions) {
|
synchronized (mSessions) {
|
||||||
final PackageInstallerSession session = mSessions.get(sessionId);
|
final PackageInstallerSession session = mSessions.get(sessionId);
|
||||||
|
|
||||||
return session != null
|
return (session != null && !(session.isStaged() && session.isDestroyed()))
|
||||||
? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid())
|
? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid())
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
@@ -851,7 +851,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
synchronized (mSessions) {
|
synchronized (mSessions) {
|
||||||
for (int i = 0; i < mSessions.size(); i++) {
|
for (int i = 0; i < mSessions.size(); i++) {
|
||||||
final PackageInstallerSession session = mSessions.valueAt(i);
|
final PackageInstallerSession session = mSessions.valueAt(i);
|
||||||
if (session.userId == userId && !session.hasParentSessionId()) {
|
if (session.userId == userId && !session.hasParentSessionId()
|
||||||
|
&& !(session.isStaged() && session.isDestroyed())) {
|
||||||
result.add(session.generateInfoForCaller(false, callingUid));
|
result.add(session.generateInfoForCaller(false, callingUid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1269,7 +1270,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
public void onStagedSessionChanged(PackageInstallerSession session) {
|
public void onStagedSessionChanged(PackageInstallerSession session) {
|
||||||
session.markUpdated();
|
session.markUpdated();
|
||||||
writeSessionsAsync();
|
writeSessionsAsync();
|
||||||
if (mOkToSendBroadcasts) {
|
if (mOkToSendBroadcasts && !session.isDestroyed()) {
|
||||||
// we don't scrub the data here as this is sent only to the installer several
|
// we don't scrub the data here as this is sent only to the installer several
|
||||||
// privileged system packages
|
// privileged system packages
|
||||||
mPm.sendSessionUpdatedBroadcast(
|
mPm.sendSessionUpdatedBroadcast(
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
|
private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
|
||||||
private static final String ATTR_PREPARED = "prepared";
|
private static final String ATTR_PREPARED = "prepared";
|
||||||
private static final String ATTR_COMMITTED = "committed";
|
private static final String ATTR_COMMITTED = "committed";
|
||||||
|
private static final String ATTR_DESTROYED = "destroyed";
|
||||||
private static final String ATTR_SEALED = "sealed";
|
private static final String ATTR_SEALED = "sealed";
|
||||||
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
|
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
|
||||||
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
|
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
|
||||||
@@ -533,7 +534,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
|
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
|
||||||
SessionParams params, long createdMillis,
|
SessionParams params, long createdMillis,
|
||||||
File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
|
File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
|
||||||
boolean committed, boolean sealed,
|
boolean committed, boolean destroyed, boolean sealed,
|
||||||
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
|
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
|
||||||
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
|
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
|
||||||
String stagedSessionErrorMessage) {
|
String stagedSessionErrorMessage) {
|
||||||
@@ -579,6 +580,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
|
|
||||||
mPrepared = prepared;
|
mPrepared = prepared;
|
||||||
mCommitted = committed;
|
mCommitted = committed;
|
||||||
|
mDestroyed = destroyed;
|
||||||
mStagedSessionReady = isReady;
|
mStagedSessionReady = isReady;
|
||||||
mStagedSessionFailed = isFailed;
|
mStagedSessionFailed = isFailed;
|
||||||
mStagedSessionApplied = isApplied;
|
mStagedSessionApplied = isApplied;
|
||||||
@@ -713,6 +715,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@hide} */
|
||||||
|
boolean isDestroyed() {
|
||||||
|
synchronized (mLock) {
|
||||||
|
return mDestroyed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns true if a staged session has reached a final state and can be forgotten about */
|
/** Returns true if a staged session has reached a final state and can be forgotten about */
|
||||||
public boolean isStagedAndInTerminalState() {
|
public boolean isStagedAndInTerminalState() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -1067,6 +1076,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the caller is the owner of this session. Otherwise throw a
|
||||||
|
* {@link SecurityException}.
|
||||||
|
*/
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private void assertCallerIsOwnerOrRootOrSystemLocked() {
|
||||||
|
final int callingUid = Binder.getCallingUid();
|
||||||
|
if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid
|
||||||
|
&& callingUid != Process.SYSTEM_UID) {
|
||||||
|
throw new SecurityException("Session does not belong to uid " + callingUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If anybody is reading or writing data of the session, throw an {@link SecurityException}.
|
* If anybody is reading or writing data of the session, throw an {@link SecurityException}.
|
||||||
*/
|
*/
|
||||||
@@ -2552,7 +2574,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
+ mParentSessionId + " and may not be abandoned directly.");
|
+ mParentSessionId + " and may not be abandoned directly.");
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
if (params.isStaged && mDestroyed) {
|
||||||
|
// If a user abandons staged session in an unsafe state, then system will try to
|
||||||
|
// abandon the destroyed staged session when it is safe on behalf of the user.
|
||||||
|
assertCallerIsOwnerOrRootOrSystemLocked();
|
||||||
|
} else {
|
||||||
assertCallerIsOwnerOrRootLocked();
|
assertCallerIsOwnerOrRootLocked();
|
||||||
|
}
|
||||||
|
|
||||||
if (isStagedAndInTerminalState()) {
|
if (isStagedAndInTerminalState()) {
|
||||||
// We keep the session in the database if it's in a finalized state. It will be
|
// We keep the session in the database if it's in a finalized state. It will be
|
||||||
@@ -2562,11 +2590,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mCommitted && params.isStaged) {
|
if (mCommitted && params.isStaged) {
|
||||||
synchronized (mLock) {
|
|
||||||
mDestroyed = true;
|
mDestroyed = true;
|
||||||
|
if (!mStagingManager.abortCommittedSessionLocked(this)) {
|
||||||
|
// Do not clean up the staged session from system. It is not safe yet.
|
||||||
|
mCallback.onStagedSessionChanged(this);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
mStagingManager.abortCommittedSession(this);
|
|
||||||
|
|
||||||
cleanStageDir();
|
cleanStageDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2926,6 +2955,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
void setStagedSessionReady() {
|
void setStagedSessionReady() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
if (mDestroyed) return; // Do not allow destroyed staged session to change state
|
||||||
mStagedSessionReady = true;
|
mStagedSessionReady = true;
|
||||||
mStagedSessionApplied = false;
|
mStagedSessionApplied = false;
|
||||||
mStagedSessionFailed = false;
|
mStagedSessionFailed = false;
|
||||||
@@ -2939,6 +2969,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
|
void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
|
||||||
String errorMessage) {
|
String errorMessage) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
if (mDestroyed) return; // Do not allow destroyed staged session to change state
|
||||||
mStagedSessionReady = false;
|
mStagedSessionReady = false;
|
||||||
mStagedSessionApplied = false;
|
mStagedSessionApplied = false;
|
||||||
mStagedSessionFailed = true;
|
mStagedSessionFailed = true;
|
||||||
@@ -2953,6 +2984,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
void setStagedSessionApplied() {
|
void setStagedSessionApplied() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
if (mDestroyed) return; // Do not allow destroyed staged session to change state
|
||||||
mStagedSessionReady = false;
|
mStagedSessionReady = false;
|
||||||
mStagedSessionApplied = true;
|
mStagedSessionApplied = true;
|
||||||
mStagedSessionFailed = false;
|
mStagedSessionFailed = false;
|
||||||
@@ -3197,7 +3229,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
*/
|
*/
|
||||||
void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
|
void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mDestroyed) {
|
if (mDestroyed && !params.isStaged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3223,6 +3255,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
}
|
}
|
||||||
writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
|
writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
|
||||||
writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
|
writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
|
||||||
|
writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
|
||||||
writeBooleanAttribute(out, ATTR_SEALED, isSealed());
|
writeBooleanAttribute(out, ATTR_SEALED, isSealed());
|
||||||
|
|
||||||
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
|
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
|
||||||
@@ -3352,6 +3385,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
|
final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
|
||||||
final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
|
final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
|
||||||
final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED);
|
final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED);
|
||||||
|
final boolean destroyed = readBooleanAttribute(in, ATTR_DESTROYED);
|
||||||
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
|
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
|
||||||
final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
|
final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
|
||||||
SessionInfo.INVALID_ID);
|
SessionInfo.INVALID_ID);
|
||||||
@@ -3473,7 +3507,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
return new PackageInstallerSession(callback, context, pm, sessionProvider,
|
return new PackageInstallerSession(callback, context, pm, sessionProvider,
|
||||||
installerThread, stagingManager, sessionId, userId, installerUid,
|
installerThread, stagingManager, sessionId, userId, installerUid,
|
||||||
installSource, params, createdMillis, stageDir, stageCid, fileArray,
|
installSource, params, createdMillis, stageDir, stageCid, fileArray,
|
||||||
prepared, committed, sealed, childSessionIdsArray, parentSessionId,
|
prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
|
||||||
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
|
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import android.text.TextUtils;
|
|||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
import android.util.SparseBooleanArray;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.util.apk.ApkSignatureVerifier;
|
import android.util.apk.ApkSignatureVerifier;
|
||||||
|
|
||||||
@@ -137,6 +138,9 @@ public class StagingManager {
|
|||||||
synchronized (mStagedSessions) {
|
synchronized (mStagedSessions) {
|
||||||
for (int i = 0; i < mStagedSessions.size(); i++) {
|
for (int i = 0; i < mStagedSessions.size(); i++) {
|
||||||
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
|
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
|
||||||
|
if (stagedSession.isDestroyed()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
|
result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +206,7 @@ public class StagingManager {
|
|||||||
final IntArray childSessionIds = new IntArray();
|
final IntArray childSessionIds = new IntArray();
|
||||||
if (session.isMultiPackage()) {
|
if (session.isMultiPackage()) {
|
||||||
for (int id : session.getChildSessionIds()) {
|
for (int id : session.getChildSessionIds()) {
|
||||||
if (isApexSession(mStagedSessions.get(id))) {
|
if (isApexSession(getStagedSession(id))) {
|
||||||
childSessionIds.add(id);
|
childSessionIds.add(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -797,6 +801,8 @@ public class StagingManager {
|
|||||||
+ session.sessionId + " [" + errorMessage + "]");
|
+ session.sessionId + " [" + errorMessage + "]");
|
||||||
session.setStagedSessionFailed(
|
session.setStagedSessionFailed(
|
||||||
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
|
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
|
||||||
|
mPreRebootVerificationHandler.onPreRebootVerificationComplete(
|
||||||
|
session.sessionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
|
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
|
||||||
@@ -880,7 +886,8 @@ public class StagingManager {
|
|||||||
synchronized (mStagedSessions) {
|
synchronized (mStagedSessions) {
|
||||||
for (int i = 0; i < mStagedSessions.size(); i++) {
|
for (int i = 0; i < mStagedSessions.size(); i++) {
|
||||||
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
|
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
|
||||||
if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()) {
|
if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()
|
||||||
|
|| stagedSession.isDestroyed()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (stagedSession.isMultiPackage()) {
|
if (stagedSession.isMultiPackage()) {
|
||||||
@@ -943,29 +950,70 @@ public class StagingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void abortCommittedSession(@NonNull PackageInstallerSession session) {
|
/**
|
||||||
|
* <p>Abort committed staged session
|
||||||
|
*
|
||||||
|
* <p>This method must be called while holding {@link PackageInstallerSession.mLock}.
|
||||||
|
*
|
||||||
|
* <p>The method returns {@code false} to indicate it is not safe to clean up the session from
|
||||||
|
* system yet. When it is safe, the method returns {@code true}.
|
||||||
|
*
|
||||||
|
* <p> When it is safe to clean up, {@link StagingManager} will call
|
||||||
|
* {@link PackageInstallerSession#abandon()} on the session again.
|
||||||
|
*
|
||||||
|
* @return {@code true} if it is safe to cleanup the session resources, otherwise {@code false}.
|
||||||
|
*/
|
||||||
|
boolean abortCommittedSessionLocked(@NonNull PackageInstallerSession session) {
|
||||||
|
int sessionId = session.sessionId;
|
||||||
if (session.isStagedSessionApplied()) {
|
if (session.isStagedSessionApplied()) {
|
||||||
Slog.w(TAG, "Cannot abort applied session : " + session.sessionId);
|
Slog.w(TAG, "Cannot abort applied session : " + sessionId);
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
if (!session.isDestroyed()) {
|
||||||
|
throw new IllegalStateException("Committed session must be destroyed before aborting it"
|
||||||
|
+ " from StagingManager");
|
||||||
|
}
|
||||||
|
if (getStagedSession(sessionId) == null) {
|
||||||
|
Slog.w(TAG, "Session " + sessionId + " has been abandoned already");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
abortSession(session);
|
|
||||||
|
|
||||||
boolean hasApex = sessionContainsApex(session);
|
// If pre-reboot verification is running, then return false. StagingManager will call
|
||||||
if (hasApex) {
|
// abandon again when pre-reboot verification ends.
|
||||||
ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
|
if (mPreRebootVerificationHandler.isVerificationRunning(sessionId)) {
|
||||||
|
Slog.w(TAG, "Session " + sessionId + " aborted before pre-reboot "
|
||||||
|
+ "verification completed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A session could be marked ready once its pre-reboot verification ends
|
||||||
|
if (session.isStagedSessionReady()) {
|
||||||
|
if (sessionContainsApex(session)) {
|
||||||
|
try {
|
||||||
|
ApexSessionInfo apexSession =
|
||||||
|
mApexManager.getStagedSessionInfo(session.sessionId);
|
||||||
if (apexSession == null || isApexSessionFinalized(apexSession)) {
|
if (apexSession == null || isApexSessionFinalized(apexSession)) {
|
||||||
Slog.w(TAG,
|
Slog.w(TAG,
|
||||||
"Cannot abort session " + session.sessionId
|
"Cannot abort session " + session.sessionId
|
||||||
+ " because it is not active or APEXD is not reachable");
|
+ " because it is not active.");
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
try {
|
|
||||||
mApexManager.abortStagedSession(session.sessionId);
|
mApexManager.abortStagedSession(session.sessionId);
|
||||||
} catch (Exception ignore) {
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Failed to contact apexd service. The apex might still be staged. We can still
|
||||||
|
// safely cleanup the staged session since pre-reboot verification is complete.
|
||||||
|
// Also, cleaning up the stageDir prevents the apex from being activated.
|
||||||
|
Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Session was successfully aborted from apexd (if required) and pre-reboot verification
|
||||||
|
// is also complete. It is now safe to clean up the session from system.
|
||||||
|
abortSession(session);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isApexSessionFinalized(ApexSessionInfo session) {
|
private boolean isApexSessionFinalized(ApexSessionInfo session) {
|
||||||
/* checking if the session is in a final state, i.e., not active anymore */
|
/* checking if the session is in a final state, i.e., not active anymore */
|
||||||
return session.isUnknown || session.isActivationFailed || session.isSuccess
|
return session.isUnknown || session.isActivationFailed || session.isSuccess
|
||||||
@@ -1042,6 +1090,11 @@ public class StagingManager {
|
|||||||
// Final states, nothing to do.
|
// Final states, nothing to do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (session.isDestroyed()) {
|
||||||
|
// Device rebooted before abandoned session was cleaned up.
|
||||||
|
session.abandon();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!session.isStagedSessionReady()) {
|
if (!session.isStagedSessionReady()) {
|
||||||
// The framework got restarted before the pre-reboot verification could complete,
|
// The framework got restarted before the pre-reboot verification could complete,
|
||||||
// restart the verification.
|
// restart the verification.
|
||||||
@@ -1124,10 +1177,20 @@ public class StagingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PackageInstallerSession getStagedSession(int sessionId) {
|
||||||
|
PackageInstallerSession session;
|
||||||
|
synchronized (mStagedSessions) {
|
||||||
|
session = mStagedSessions.get(sessionId);
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
private final class PreRebootVerificationHandler extends Handler {
|
private final class PreRebootVerificationHandler extends Handler {
|
||||||
// Hold session ids before handler gets ready to do the verification.
|
// Hold session ids before handler gets ready to do the verification.
|
||||||
private IntArray mPendingSessionIds;
|
private IntArray mPendingSessionIds;
|
||||||
private boolean mIsReady;
|
private boolean mIsReady;
|
||||||
|
@GuardedBy("mVerificationRunning")
|
||||||
|
private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray();
|
||||||
|
|
||||||
PreRebootVerificationHandler(Looper looper) {
|
PreRebootVerificationHandler(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
@@ -1155,13 +1218,15 @@ public class StagingManager {
|
|||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
final int sessionId = msg.arg1;
|
final int sessionId = msg.arg1;
|
||||||
final PackageInstallerSession session;
|
final PackageInstallerSession session = getStagedSession(sessionId);
|
||||||
synchronized (mStagedSessions) {
|
|
||||||
session = mStagedSessions.get(sessionId);
|
|
||||||
}
|
|
||||||
// Maybe session was aborted before pre-reboot verification was complete
|
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
Slog.d(TAG, "Stopping pre-reboot verification for sessionId: " + sessionId);
|
Slog.wtf(TAG, "Session disappeared in the middle of pre-reboot verification: "
|
||||||
|
+ sessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (session.isDestroyed()) {
|
||||||
|
// No point in running verification on a destroyed session
|
||||||
|
onPreRebootVerificationComplete(sessionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
@@ -1200,9 +1265,40 @@ public class StagingManager {
|
|||||||
mPendingSessionIds.add(sessionId);
|
mPendingSessionIds.add(sessionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PackageInstallerSession session = getStagedSession(sessionId);
|
||||||
|
synchronized (mVerificationRunning) {
|
||||||
|
// Do not start verification on a session that has been abandoned
|
||||||
|
if (session == null || session.isDestroyed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
|
||||||
|
mVerificationRunning.put(sessionId, true);
|
||||||
|
}
|
||||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
|
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
synchronized (mVerificationRunning) {
|
||||||
|
Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
|
||||||
|
mVerificationRunning.delete(sessionId);
|
||||||
|
}
|
||||||
|
// Check if the session was destroyed while pre-reboot verification was running. If so,
|
||||||
|
// abandon it again.
|
||||||
|
PackageInstallerSession session = getStagedSession(sessionId);
|
||||||
|
if (session != null && session.isDestroyed()) {
|
||||||
|
session.abandon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isVerificationRunning(int sessionId) {
|
||||||
|
synchronized (mVerificationRunning) {
|
||||||
|
return mVerificationRunning.get(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyPreRebootVerification_Start_Complete(int sessionId) {
|
private void notifyPreRebootVerification_Start_Complete(int sessionId) {
|
||||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
|
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1317,6 @@ public class StagingManager {
|
|||||||
* See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
|
* See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
|
||||||
*/
|
*/
|
||||||
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
|
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
|
||||||
Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
|
|
||||||
|
|
||||||
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
|
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
|
||||||
// If rollback is enabled for this session, we call through to the RollbackManager
|
// 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
|
// with the list of sessions it must enable rollback for. Note that
|
||||||
@@ -1269,6 +1363,7 @@ public class StagingManager {
|
|||||||
}
|
}
|
||||||
} catch (PackageManagerException e) {
|
} catch (PackageManagerException e) {
|
||||||
session.setStagedSessionFailed(e.error, e.getMessage());
|
session.setStagedSessionFailed(e.error, e.getMessage());
|
||||||
|
onPreRebootVerificationComplete(session.sessionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1301,6 +1396,7 @@ public class StagingManager {
|
|||||||
// TODO(b/118865310): abort the session on apexd.
|
// TODO(b/118865310): abort the session on apexd.
|
||||||
} catch (PackageManagerException e) {
|
} catch (PackageManagerException e) {
|
||||||
session.setStagedSessionFailed(e.error, e.getMessage());
|
session.setStagedSessionFailed(e.error, e.getMessage());
|
||||||
|
onPreRebootVerificationComplete(session.sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1323,9 +1419,18 @@ public class StagingManager {
|
|||||||
Slog.e(TAG, "Failed to get hold of StorageManager", e);
|
Slog.e(TAG, "Failed to get hold of StorageManager", e);
|
||||||
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
|
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
|
||||||
"Failed to get hold of StorageManager");
|
"Failed to get hold of StorageManager");
|
||||||
|
onPreRebootVerificationComplete(session.sessionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop pre-reboot verification before marking session ready. From this point on, if we
|
||||||
|
// abandon the session then it will be cleaned up immediately. If session is abandoned
|
||||||
|
// after this point, then even if for some reason system tries to install the session
|
||||||
|
// or activate its apex, there won't be any files to work with as they will be cleaned
|
||||||
|
// up by the system as part of abandonment. If session is abandoned before this point,
|
||||||
|
// then the session is already destroyed and cannot be marked ready anymore.
|
||||||
|
onPreRebootVerificationComplete(session.sessionId);
|
||||||
|
|
||||||
// Proactively mark session as ready before calling apexd. Although this call order
|
// Proactively mark session as ready before calling apexd. Although this call order
|
||||||
// looks counter-intuitive, this is the easiest way to ensure that session won't end up
|
// looks counter-intuitive, this is the easiest way to ensure that session won't end up
|
||||||
// in the inconsistent state:
|
// in the inconsistent state:
|
||||||
@@ -1337,15 +1442,16 @@ public class StagingManager {
|
|||||||
// only apex part of the train will be applied, leaving device in an inconsistent state.
|
// only apex part of the train will be applied, leaving device in an inconsistent state.
|
||||||
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
|
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
|
||||||
session.setStagedSessionReady();
|
session.setStagedSessionReady();
|
||||||
|
if (session.isStagedSessionReady()) {
|
||||||
final boolean hasApex = sessionContainsApex(session);
|
final boolean hasApex = sessionContainsApex(session);
|
||||||
if (!hasApex) {
|
if (hasApex) {
|
||||||
// Session doesn't contain apex, nothing to do.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
mApexManager.markStagedSessionReady(session.sessionId);
|
mApexManager.markStagedSessionReady(session.sessionId);
|
||||||
} catch (PackageManagerException e) {
|
} catch (PackageManagerException e) {
|
||||||
session.setStagedSessionFailed(e.error, e.getMessage());
|
session.setStagedSessionFailed(e.error, e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ public class PackageInstallerSessionTest {
|
|||||||
/* files */ null,
|
/* files */ null,
|
||||||
/* prepared */ true,
|
/* prepared */ true,
|
||||||
/* committed */ true,
|
/* committed */ true,
|
||||||
|
/* destroyed */ staged ? true : false,
|
||||||
/* sealed */ false, // Setting to true would trigger some PM logic.
|
/* sealed */ false, // Setting to true would trigger some PM logic.
|
||||||
/* childSessionIds */ childSessionIds != null ? childSessionIds : new int[0],
|
/* childSessionIds */ childSessionIds != null ? childSessionIds : new int[0],
|
||||||
/* parentSessionId */ parentSessionId,
|
/* parentSessionId */ parentSessionId,
|
||||||
|
|||||||
Reference in New Issue
Block a user