Merge changes Ibbf7f77c,I7e80c13f,I4eeeea28
* changes: Some cleanup (3/n) Move some fields/methods into StagedSession (2/n) Extract staged session code to a separate class (1/n)
This commit is contained in:
@@ -291,23 +291,23 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
||||
}
|
||||
|
||||
void restoreAndApplyStagedSessionIfNeeded() {
|
||||
List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
|
||||
List<StagingManager.StagedSession> stagedSessionsToRestore = new ArrayList<>();
|
||||
synchronized (mSessions) {
|
||||
for (int i = 0; i < mSessions.size(); i++) {
|
||||
final PackageInstallerSession session = mSessions.valueAt(i);
|
||||
if (session.isStaged()) {
|
||||
stagedSessionsToRestore.add(session);
|
||||
stagedSessionsToRestore.add(session.mStagedSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
|
||||
// atomic install which needs to query sessions, which requires lock on mSessions.
|
||||
boolean isDeviceUpgrading = mPm.isDeviceUpgrading();
|
||||
for (PackageInstallerSession session : stagedSessionsToRestore) {
|
||||
if (!session.isStagedAndInTerminalState() && session.hasParentSessionId()
|
||||
for (StagingManager.StagedSession session : stagedSessionsToRestore) {
|
||||
if (!session.isInTerminalState() && session.hasParentSessionId()
|
||||
&& getSession(session.getParentSessionId()) == null) {
|
||||
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||
"An orphan staged session " + session.sessionId + " is found, "
|
||||
session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||
"An orphan staged session " + session.sessionId() + " is found, "
|
||||
+ "parent " + session.getParentSessionId() + " is missing");
|
||||
}
|
||||
mStagingManager.restoreSession(session, isDeviceUpgrading);
|
||||
@@ -1399,7 +1399,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
||||
@Override
|
||||
public void run() {
|
||||
if (session.isStaged() && !success) {
|
||||
mStagingManager.abortSession(session);
|
||||
mStagingManager.abortSession(session.mStagedSession);
|
||||
}
|
||||
synchronized (mSessions) {
|
||||
if (!session.isStaged() || !success) {
|
||||
|
||||
@@ -429,30 +429,341 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
@GuardedBy("mLock")
|
||||
private ArrayMap<String, List<CertifiedChecksum>> mChecksums = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean mStagedSessionApplied;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mStagedSessionReady;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mStagedSessionFailed;
|
||||
@GuardedBy("mLock")
|
||||
private int mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
@GuardedBy("mLock")
|
||||
private String mStagedSessionErrorMessage;
|
||||
|
||||
/**
|
||||
* The callback to run when pre-reboot verification has ended. Used by {@link #abandonStaged()}
|
||||
* to delay session clean-up until it is safe to do so.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
private Runnable mPendingAbandonCallback;
|
||||
/**
|
||||
* {@code true} if pre-reboot verification is ongoing which means it is not safe for
|
||||
* {@link #abandon()} to clean up staging directories.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private boolean mInPreRebootVerification;
|
||||
final StagedSession mStagedSession;
|
||||
|
||||
@VisibleForTesting
|
||||
public class StagedSession implements StagingManager.StagedSession {
|
||||
@GuardedBy("mLock")
|
||||
private boolean mSessionApplied;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mSessionReady;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mSessionFailed;
|
||||
@GuardedBy("mLock")
|
||||
private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
@GuardedBy("mLock")
|
||||
private String mSessionErrorMessage;
|
||||
|
||||
/**
|
||||
* The callback to run when pre-reboot verification has ended. Used by {@link #abandon()}
|
||||
* to delay session clean-up until it is safe to do so.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
@Nullable
|
||||
private Runnable mPendingAbandonCallback;
|
||||
/**
|
||||
* {@code true} if pre-reboot verification is ongoing which means it is not safe for
|
||||
* {@link #abandon()} to clean up staging directories.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private boolean mInPreRebootVerification;
|
||||
|
||||
StagedSession(boolean isReady, boolean isApplied, boolean isFailed, int errorCode,
|
||||
String errorMessage) {
|
||||
mSessionReady = isReady;
|
||||
mSessionApplied = isApplied;
|
||||
mSessionFailed = isFailed;
|
||||
mSessionErrorCode = errorCode;
|
||||
mSessionErrorMessage = errorMessage != null ? errorMessage : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StagingManager.StagedSession> getChildSessions() {
|
||||
if (!params.isMultiPackage) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
int size = mChildSessions.size();
|
||||
List<StagingManager.StagedSession> childSessions = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
childSessions.add(mChildSessions.valueAt(i).mStagedSession);
|
||||
}
|
||||
return childSessions;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionParams sessionParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiPackage() {
|
||||
return params.isMultiPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApexSession() {
|
||||
return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsApexSession() {
|
||||
return sessionContains((s) -> s.isApexSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
return PackageInstallerSession.this.getPackageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionReady() {
|
||||
synchronized (mLock) {
|
||||
// Do not allow destroyed/failed staged session to change state
|
||||
if (mDestroyed || mSessionFailed) return;
|
||||
mSessionReady = true;
|
||||
mSessionApplied = false;
|
||||
mSessionFailed = false;
|
||||
mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
mSessionErrorMessage = "";
|
||||
}
|
||||
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionFailed(int errorCode, String errorMessage) {
|
||||
List<PackageInstallerSession> childSessions;
|
||||
synchronized (mLock) {
|
||||
// Do not allow destroyed/failed staged session to change state
|
||||
if (mDestroyed || mSessionFailed) return;
|
||||
mSessionReady = false;
|
||||
mSessionApplied = false;
|
||||
mSessionFailed = true;
|
||||
mSessionErrorCode = errorCode;
|
||||
mSessionErrorMessage = errorMessage;
|
||||
Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
|
||||
childSessions = getChildSessionsLocked();
|
||||
}
|
||||
cleanStageDir(childSessions);
|
||||
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionApplied() {
|
||||
List<PackageInstallerSession> childSessions;
|
||||
synchronized (mLock) {
|
||||
// Do not allow destroyed/failed staged session to change state
|
||||
if (mDestroyed || mSessionFailed) return;
|
||||
mSessionReady = false;
|
||||
mSessionApplied = true;
|
||||
mSessionFailed = false;
|
||||
mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
mSessionErrorMessage = "";
|
||||
Slog.d(TAG, "Marking session " + sessionId + " as applied");
|
||||
childSessions = getChildSessionsLocked();
|
||||
}
|
||||
cleanStageDir(childSessions);
|
||||
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsApkSession() {
|
||||
return PackageInstallerSession.this.containsApkSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs apks of staged session while skipping the verification process for a committed
|
||||
* and ready session.
|
||||
*/
|
||||
@Override
|
||||
public void installSession(IntentSender statusReceiver) {
|
||||
assertCallerIsOwnerOrRootOrSystemLocked();
|
||||
assertNotChildLocked("StagedSession#installSession");
|
||||
Preconditions.checkArgument(isCommitted() && isSessionReady());
|
||||
|
||||
// Since staged sessions are installed during boot, the original reference to status
|
||||
// receiver from the owner has already been lost. We can safely replace it with a
|
||||
// status receiver from the system without effecting the flow.
|
||||
updateRemoteStatusReceiver(statusReceiver);
|
||||
install();
|
||||
}
|
||||
|
||||
private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
|
||||
synchronized (mLock) {
|
||||
mRemoteStatusReceiver = remoteStatusReceiver;
|
||||
if (isMultiPackage()) {
|
||||
final IntentSender childIntentSender = new ChildStatusIntentReceiver(
|
||||
mChildSessions.clone(), remoteStatusReceiver).getIntentSender();
|
||||
for (int i = mChildSessions.size() - 1; i >= 0; --i) {
|
||||
mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasParentSessionId() {
|
||||
return PackageInstallerSession.this.hasParentSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParentSessionId() {
|
||||
return PackageInstallerSession.this.getParentSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommitted() {
|
||||
return PackageInstallerSession.this.isCommitted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInTerminalState() {
|
||||
synchronized (mLock) {
|
||||
return mSessionApplied || mSessionFailed;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return PackageInstallerSession.this.isDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCommittedMillis() {
|
||||
return PackageInstallerSession.this.getCommittedMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) {
|
||||
return PackageInstallerSession.this.sessionContains(s -> filter.test(s.mStagedSession));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSessionReady() {
|
||||
synchronized (mLock) {
|
||||
return mSessionReady;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSessionApplied() {
|
||||
synchronized (mLock) {
|
||||
return mSessionApplied;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSessionFailed() {
|
||||
synchronized (mLock) {
|
||||
return mSessionFailed;
|
||||
}
|
||||
}
|
||||
|
||||
@StagedSessionErrorCode int getSessionErrorCode() {
|
||||
synchronized (mLock) {
|
||||
return mSessionErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
String getSessionErrorMessage() {
|
||||
synchronized (mLock) {
|
||||
return mSessionErrorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abandon() {
|
||||
final Runnable r;
|
||||
synchronized (mLock) {
|
||||
assertNotChildLocked("StagedSession#abandon");
|
||||
assertCallerIsOwnerOrRootLocked();
|
||||
if (isInTerminalState()) {
|
||||
// We keep the session in the database if it's in a finalized state. It will be
|
||||
// removed by PackageInstallerService when the last update time is old enough.
|
||||
// Also, in such cases cleanStageDir() has already been executed so no need to
|
||||
// do it now.
|
||||
return;
|
||||
}
|
||||
mDestroyed = true;
|
||||
boolean isCommitted = mCommitted;
|
||||
List<PackageInstallerSession> childSessions = getChildSessionsLocked();
|
||||
r = () -> {
|
||||
assertNotLocked("abandonStaged");
|
||||
if (isCommitted) {
|
||||
mStagingManager.abortCommittedSession(this);
|
||||
}
|
||||
cleanStageDir(childSessions);
|
||||
destroyInternal();
|
||||
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
|
||||
maybeCleanUpChildSessions();
|
||||
};
|
||||
if (mInPreRebootVerification) {
|
||||
// Pre-reboot verification is ongoing, not safe to clean up the session yet.
|
||||
mPendingAbandonCallback = r;
|
||||
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
r.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified by the staging manager that pre-reboot verification is about to start. The
|
||||
* return value should be checked to decide whether it is OK to start pre-reboot
|
||||
* verification. In the case of a destroyed session, {@code false} is returned and there is
|
||||
* no need to start pre-reboot verification.
|
||||
*/
|
||||
@Override
|
||||
public boolean notifyStartPreRebootVerification() {
|
||||
synchronized (mLock) {
|
||||
if (mInPreRebootVerification) {
|
||||
throw new IllegalStateException("Pre-reboot verification has started");
|
||||
}
|
||||
if (mDestroyed) {
|
||||
return false;
|
||||
}
|
||||
mInPreRebootVerification = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
|
||||
* clean up the session if {@link #abandon()} has been called previously.
|
||||
*/
|
||||
@Override
|
||||
public void notifyEndPreRebootVerification() {
|
||||
synchronized (mLock) {
|
||||
if (!mInPreRebootVerification) {
|
||||
throw new IllegalStateException("Pre-reboot verification not started");
|
||||
}
|
||||
mInPreRebootVerification = false;
|
||||
}
|
||||
dispatchPendingAbandonCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes verification process for non-final committed staged session.
|
||||
*
|
||||
* Useful if a device gets rebooted before verification is complete and we need to restart
|
||||
* the verification.
|
||||
*/
|
||||
@Override
|
||||
public void verifySession() {
|
||||
assertCallerIsOwnerOrRootOrSystemLocked();
|
||||
Preconditions.checkArgument(isCommitted());
|
||||
Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
|
||||
verify();
|
||||
}
|
||||
|
||||
private void dispatchPendingAbandonCallback() {
|
||||
final Runnable callback;
|
||||
synchronized (mLock) {
|
||||
callback = mPendingAbandonCallback;
|
||||
mPendingAbandonCallback = null;
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to the validated base APK for this session, which may point at an
|
||||
@@ -691,12 +1002,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
mPrepared = prepared;
|
||||
mCommitted = committed;
|
||||
mDestroyed = destroyed;
|
||||
mStagedSessionReady = isReady;
|
||||
mStagedSessionFailed = isFailed;
|
||||
mStagedSessionApplied = isApplied;
|
||||
mStagedSessionErrorCode = stagedSessionErrorCode;
|
||||
mStagedSessionErrorMessage =
|
||||
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
|
||||
mStagedSession = params.isStaged ? new StagedSession(isReady, isApplied, isFailed,
|
||||
stagedSessionErrorCode, stagedSessionErrorMessage) : null;
|
||||
|
||||
if (isDataLoaderInstallation()) {
|
||||
if (isApexSession()) {
|
||||
@@ -789,10 +1096,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
info.rollbackDataPolicy = params.rollbackDataPolicy;
|
||||
info.parentSessionId = mParentSessionId;
|
||||
info.childSessionIds = getChildSessionIdsLocked();
|
||||
info.isStagedSessionApplied = mStagedSessionApplied;
|
||||
info.isStagedSessionReady = mStagedSessionReady;
|
||||
info.isStagedSessionFailed = mStagedSessionFailed;
|
||||
info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage);
|
||||
info.isStagedSessionApplied = isStagedSessionApplied();
|
||||
info.isStagedSessionReady = isStagedSessionReady();
|
||||
info.isStagedSessionFailed = isStagedSessionFailed();
|
||||
info.setStagedSessionErrorCode(getStagedSessionErrorCode(),
|
||||
getStagedSessionErrorMessage());
|
||||
info.createdMillis = createdMillis;
|
||||
info.updatedMillis = updatedMillis;
|
||||
}
|
||||
@@ -827,9 +1135,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
|
||||
/** Returns true if a staged session has reached a final state and can be forgotten about */
|
||||
public boolean isStagedAndInTerminalState() {
|
||||
synchronized (mLock) {
|
||||
return params.isStaged && (mStagedSessionApplied || mStagedSessionFailed);
|
||||
}
|
||||
return params.isStaged && mStagedSession.isInTerminalState();
|
||||
}
|
||||
|
||||
private void assertNotLocked(String cookie) {
|
||||
@@ -1724,11 +2030,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
// Session is sealed and committed but could not be verified, we need to destroy it.
|
||||
destroyInternal();
|
||||
if (isStaged()) {
|
||||
setStagedSessionFailed(
|
||||
mStagedSession.setSessionFailed(
|
||||
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
|
||||
// TODO(b/136257624): Remove this once all verification logic has been transferred out
|
||||
// of StagingManager.
|
||||
mStagingManager.notifyVerificationComplete(this);
|
||||
mStagingManager.notifyVerificationComplete(mStagedSession);
|
||||
} else {
|
||||
// Dispatch message to remove session from PackageInstallerService.
|
||||
dispatchSessionFinished(error, msg, null);
|
||||
@@ -1847,7 +2153,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
.write();
|
||||
}
|
||||
if (params.isStaged) {
|
||||
mStagingManager.commitSession(this);
|
||||
mStagingManager.commitSession(mStagedSession);
|
||||
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
|
||||
// though ideally, we just need to send session committed broadcast.
|
||||
dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
|
||||
@@ -1863,21 +2169,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes verification process for non-final committed staged session.
|
||||
*
|
||||
* Useful if a device gets rebooted before verification is complete and we need to restart the
|
||||
* verification.
|
||||
*/
|
||||
void verifyStagedSession() {
|
||||
assertCallerIsOwnerOrRootOrSystemLocked();
|
||||
Preconditions.checkArgument(isCommitted());
|
||||
Preconditions.checkArgument(isStaged());
|
||||
Preconditions.checkArgument(!mStagedSessionApplied && !mStagedSessionFailed);
|
||||
|
||||
verify();
|
||||
}
|
||||
|
||||
private void verify() {
|
||||
try {
|
||||
verifyNonStaged();
|
||||
@@ -1932,36 +2223,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs apks of staged session while skipping the verification process for a committed and
|
||||
* ready session.
|
||||
*/
|
||||
void installStagedSession(IntentSender statusReceiver) {
|
||||
assertCallerIsOwnerOrRootOrSystemLocked();
|
||||
Preconditions.checkArgument(!hasParentSessionId()); // Don't allow installing child sessions
|
||||
Preconditions.checkArgument(isCommitted() && isStagedSessionReady());
|
||||
|
||||
// Since staged sessions are installed during boot, the original reference to status
|
||||
// receiver from the owner has already been lost. We can safely replace it with a
|
||||
// status receiver from the system without effecting the flow.
|
||||
updateRemoteStatusReceiver(statusReceiver);
|
||||
install();
|
||||
}
|
||||
|
||||
private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
|
||||
synchronized (mLock) {
|
||||
mRemoteStatusReceiver = remoteStatusReceiver;
|
||||
if (isMultiPackage()) {
|
||||
final IntentSender childIntentSender =
|
||||
new ChildStatusIntentReceiver(mChildSessions.clone(), remoteStatusReceiver)
|
||||
.getIntentSender();
|
||||
for (int i = mChildSessions.size() - 1; i >= 0; --i) {
|
||||
mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void install() {
|
||||
try {
|
||||
installNonStaged();
|
||||
@@ -2188,7 +2449,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
if (isStaged()) {
|
||||
// TODO(b/136257624): Remove this once all verification logic has been transferred out
|
||||
// of StagingManager.
|
||||
mStagingManager.notifyPreRebootVerification_Apk_Complete(this);
|
||||
mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
|
||||
// TODO(b/136257624): We also need to destroy internals for verified staged session,
|
||||
// otherwise file descriptors are never closed for verified staged session until reboot
|
||||
return;
|
||||
@@ -2370,10 +2631,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean containsApexSession() {
|
||||
return sessionContains((s) -> s.isApexSession());
|
||||
}
|
||||
|
||||
boolean containsApkSession() {
|
||||
return sessionContains((s) -> !s.isApexSession());
|
||||
}
|
||||
@@ -3195,6 +3452,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
|
||||
private void abandonNonStaged() {
|
||||
synchronized (mLock) {
|
||||
assertNotChildLocked("abandonNonStaged");
|
||||
assertCallerIsOwnerOrRootLocked();
|
||||
if (mRelinquished) {
|
||||
if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control");
|
||||
@@ -3206,98 +3464,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
maybeCleanUpChildSessions();
|
||||
}
|
||||
|
||||
private void abandonStaged() {
|
||||
final Runnable r;
|
||||
synchronized (mLock) {
|
||||
assertCallerIsOwnerOrRootLocked();
|
||||
if (isStagedAndInTerminalState()) {
|
||||
// We keep the session in the database if it's in a finalized state. It will be
|
||||
// removed by PackageInstallerService when the last update time is old enough.
|
||||
// Also, in such cases cleanStageDir() has already been executed so no need to
|
||||
// do it now.
|
||||
return;
|
||||
}
|
||||
mDestroyed = true;
|
||||
boolean isCommitted = mCommitted;
|
||||
List<PackageInstallerSession> childSessions = getChildSessionsLocked();
|
||||
r = () -> {
|
||||
assertNotLocked("abandonStaged");
|
||||
if (isCommitted) {
|
||||
mStagingManager.abortCommittedSession(this);
|
||||
}
|
||||
cleanStageDir(childSessions);
|
||||
destroyInternal();
|
||||
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
|
||||
maybeCleanUpChildSessions();
|
||||
};
|
||||
if (mInPreRebootVerification) {
|
||||
// Pre-reboot verification is ongoing. It is not safe to clean up the session yet.
|
||||
mPendingAbandonCallback = r;
|
||||
mCallback.onStagedSessionChanged(this);
|
||||
return;
|
||||
}
|
||||
@GuardedBy("mLock")
|
||||
private void assertNotChildLocked(String cookie) {
|
||||
if (hasParentSessionId()) {
|
||||
throw new IllegalStateException(cookie + " can't be called on a child session, id="
|
||||
+ sessionId + " parentId=" + getParentSessionId());
|
||||
}
|
||||
r.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abandon() {
|
||||
if (hasParentSessionId()) {
|
||||
throw new IllegalStateException(
|
||||
"Session " + sessionId + " is a child of multi-package session "
|
||||
+ getParentSessionId() + " and may not be abandoned directly.");
|
||||
}
|
||||
if (params.isStaged) {
|
||||
abandonStaged();
|
||||
mStagedSession.abandon();
|
||||
} else {
|
||||
abandonNonStaged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified by the staging manager that pre-reboot verification is about to start. The return
|
||||
* value should be checked to decide whether it is OK to start pre-reboot verification. In
|
||||
* the case of a destroyed session, {@code false} is returned and there is no need to start
|
||||
* pre-reboot verification.
|
||||
*/
|
||||
boolean notifyStagedStartPreRebootVerification() {
|
||||
synchronized (mLock) {
|
||||
if (mInPreRebootVerification) {
|
||||
throw new IllegalStateException("Pre-reboot verification has started");
|
||||
}
|
||||
if (mDestroyed) {
|
||||
return false;
|
||||
}
|
||||
mInPreRebootVerification = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchPendingAbandonCallback() {
|
||||
final Runnable callback;
|
||||
synchronized (mLock) {
|
||||
callback = mPendingAbandonCallback;
|
||||
mPendingAbandonCallback = null;
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
|
||||
* clean up the session if {@link #abandon()} has been called previously.
|
||||
*/
|
||||
void notifyStagedEndPreRebootVerification() {
|
||||
synchronized (mLock) {
|
||||
if (!mInPreRebootVerification) {
|
||||
throw new IllegalStateException("Pre-reboot verification not started");
|
||||
}
|
||||
mInPreRebootVerification = false;
|
||||
}
|
||||
dispatchPendingAbandonCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiPackage() {
|
||||
return params.isMultiPackage;
|
||||
@@ -3782,89 +3965,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
void setStagedSessionReady() {
|
||||
synchronized (mLock) {
|
||||
// Do not allow destroyed/failed staged session to change state
|
||||
if (mDestroyed || mStagedSessionFailed) return;
|
||||
mStagedSessionReady = true;
|
||||
mStagedSessionApplied = false;
|
||||
mStagedSessionFailed = false;
|
||||
mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
mStagedSessionErrorMessage = "";
|
||||
}
|
||||
mCallback.onStagedSessionChanged(this);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
|
||||
List<PackageInstallerSession> childSessions;
|
||||
synchronized (mLock) {
|
||||
// Do not allow destroyed/failed staged session to change state
|
||||
if (mDestroyed || mStagedSessionFailed) return;
|
||||
mStagedSessionReady = false;
|
||||
mStagedSessionApplied = false;
|
||||
mStagedSessionFailed = true;
|
||||
mStagedSessionErrorCode = errorCode;
|
||||
mStagedSessionErrorMessage = errorMessage;
|
||||
Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
|
||||
childSessions = getChildSessionsLocked();
|
||||
}
|
||||
cleanStageDir(childSessions);
|
||||
mCallback.onStagedSessionChanged(this);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
void setStagedSessionApplied() {
|
||||
List<PackageInstallerSession> childSessions;
|
||||
synchronized (mLock) {
|
||||
// Do not allow destroyed/failed staged session to change state
|
||||
if (mDestroyed || mStagedSessionFailed) return;
|
||||
mStagedSessionReady = false;
|
||||
mStagedSessionApplied = true;
|
||||
mStagedSessionFailed = false;
|
||||
mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
mStagedSessionErrorMessage = "";
|
||||
Slog.d(TAG, "Marking session " + sessionId + " as applied");
|
||||
childSessions = getChildSessionsLocked();
|
||||
}
|
||||
cleanStageDir(childSessions);
|
||||
mCallback.onStagedSessionChanged(this);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
boolean isStagedSessionReady() {
|
||||
synchronized (mLock) {
|
||||
return mStagedSessionReady;
|
||||
}
|
||||
return params.isStaged && mStagedSession.isSessionReady();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
boolean isStagedSessionApplied() {
|
||||
synchronized (mLock) {
|
||||
return mStagedSessionApplied;
|
||||
}
|
||||
return params.isStaged && mStagedSession.isSessionApplied();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
boolean isStagedSessionFailed() {
|
||||
synchronized (mLock) {
|
||||
return mStagedSessionFailed;
|
||||
}
|
||||
return params.isStaged && mStagedSession.isSessionFailed();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@StagedSessionErrorCode int getStagedSessionErrorCode() {
|
||||
synchronized (mLock) {
|
||||
return mStagedSessionErrorCode;
|
||||
}
|
||||
return params.isStaged ? mStagedSession.getSessionErrorCode()
|
||||
: SessionInfo.STAGED_SESSION_NO_ERROR;
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
String getStagedSessionErrorMessage() {
|
||||
synchronized (mLock) {
|
||||
return mStagedSessionErrorMessage;
|
||||
}
|
||||
return params.isStaged ? mStagedSession.getSessionErrorMessage() : "";
|
||||
}
|
||||
|
||||
private void destroyInternal() {
|
||||
@@ -3961,11 +4085,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
pw.printPair("params.isStaged", params.isStaged);
|
||||
pw.printPair("mParentSessionId", mParentSessionId);
|
||||
pw.printPair("mChildSessionIds", getChildSessionIdsLocked());
|
||||
pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
|
||||
pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
|
||||
pw.printPair("mStagedSessionReady", mStagedSessionReady);
|
||||
pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode);
|
||||
pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage);
|
||||
pw.printPair("mStagedSessionApplied", isStagedSessionApplied());
|
||||
pw.printPair("mStagedSessionFailed", isStagedSessionFailed());
|
||||
pw.printPair("mStagedSessionReady", isStagedSessionReady());
|
||||
pw.printPair("mStagedSessionErrorCode", getStagedSessionErrorCode());
|
||||
pw.printPair("mStagedSessionErrorMessage", getStagedSessionErrorMessage());
|
||||
pw.println();
|
||||
|
||||
pw.decreaseIndent();
|
||||
@@ -4131,12 +4255,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
|
||||
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
|
||||
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
|
||||
writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady);
|
||||
writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed);
|
||||
writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied);
|
||||
out.attributeInt(null, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode);
|
||||
writeBooleanAttribute(out, ATTR_IS_READY, isStagedSessionReady());
|
||||
writeBooleanAttribute(out, ATTR_IS_FAILED, isStagedSessionFailed());
|
||||
writeBooleanAttribute(out, ATTR_IS_APPLIED, isStagedSessionApplied());
|
||||
out.attributeInt(null, ATTR_STAGED_SESSION_ERROR_CODE, getStagedSessionErrorCode());
|
||||
writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE,
|
||||
mStagedSessionErrorMessage);
|
||||
getStagedSessionErrorMessage());
|
||||
// TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
|
||||
// we've read all sessions.
|
||||
out.attributeInt(null, ATTR_PARENT_SESSION_ID, mParentSessionId);
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionInfo;
|
||||
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageParser.PackageParserException;
|
||||
@@ -82,6 +83,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -102,7 +104,7 @@ public class StagingManager {
|
||||
private String mFailureReason;
|
||||
|
||||
@GuardedBy("mStagedSessions")
|
||||
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
|
||||
private final SparseArray<StagedSession> mStagedSessions = new SparseArray<>();
|
||||
|
||||
@GuardedBy("mFailedPackageNames")
|
||||
private final List<String> mFailedPackageNames = new ArrayList<>();
|
||||
@@ -111,6 +113,35 @@ public class StagingManager {
|
||||
@GuardedBy("mSuccessfulStagedSessionIds")
|
||||
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
|
||||
|
||||
interface StagedSession {
|
||||
boolean isMultiPackage();
|
||||
boolean isApexSession();
|
||||
boolean isCommitted();
|
||||
boolean isInTerminalState();
|
||||
boolean isDestroyed();
|
||||
boolean isSessionReady();
|
||||
boolean isSessionApplied();
|
||||
boolean isSessionFailed();
|
||||
List<StagedSession> getChildSessions();
|
||||
String getPackageName();
|
||||
int getParentSessionId();
|
||||
int sessionId();
|
||||
PackageInstaller.SessionParams sessionParams();
|
||||
boolean sessionContains(Predicate<StagedSession> filter);
|
||||
boolean containsApkSession();
|
||||
boolean containsApexSession();
|
||||
void setSessionReady();
|
||||
void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage);
|
||||
void setSessionApplied();
|
||||
void installSession(IntentSender statusReceiver);
|
||||
boolean hasParentSessionId();
|
||||
long getCommittedMillis();
|
||||
void abandon();
|
||||
boolean notifyStartPreRebootVerification();
|
||||
void notifyEndPreRebootVerification();
|
||||
void verifySession();
|
||||
}
|
||||
|
||||
StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
|
||||
mContext = context;
|
||||
mPackageParserSupplier = packageParserSupplier;
|
||||
@@ -213,20 +244,20 @@ public class StagingManager {
|
||||
+ " compatible with the one currently installed on device");
|
||||
}
|
||||
|
||||
private List<PackageInfo> submitSessionToApexService(@NonNull PackageInstallerSession session,
|
||||
private List<PackageInfo> submitSessionToApexService(@NonNull StagedSession session,
|
||||
int rollbackId) throws PackageManagerException {
|
||||
final IntArray childSessionIds = new IntArray();
|
||||
if (session.isMultiPackage()) {
|
||||
for (PackageInstallerSession s : session.getChildSessions()) {
|
||||
for (StagedSession s : session.getChildSessions()) {
|
||||
if (s.isApexSession()) {
|
||||
childSessionIds.add(s.sessionId);
|
||||
childSessionIds.add(s.sessionId());
|
||||
}
|
||||
}
|
||||
}
|
||||
ApexSessionParams apexSessionParams = new ApexSessionParams();
|
||||
apexSessionParams.sessionId = session.sessionId;
|
||||
apexSessionParams.sessionId = session.sessionId();
|
||||
apexSessionParams.childSessionIds = childSessionIds.toArray();
|
||||
if (session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
|
||||
if (session.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
|
||||
apexSessionParams.isRollback = true;
|
||||
apexSessionParams.rollbackId = rollbackId;
|
||||
} else {
|
||||
@@ -269,7 +300,7 @@ public class StagingManager {
|
||||
result.add(packageInfo);
|
||||
apexPackageNames.add(packageInfo.packageName);
|
||||
}
|
||||
Slog.d(TAG, "Session " + session.sessionId + " has following APEX packages: "
|
||||
Slog.d(TAG, "Session " + session.sessionId() + " has following APEX packages: "
|
||||
+ apexPackageNames);
|
||||
return result;
|
||||
}
|
||||
@@ -288,22 +319,23 @@ public class StagingManager {
|
||||
"Could not find rollback id for commit session: " + sessionId);
|
||||
}
|
||||
|
||||
private void checkRequiredVersionCode(final PackageInstallerSession session,
|
||||
private void checkRequiredVersionCode(final StagedSession session,
|
||||
final PackageInfo activePackage) throws PackageManagerException {
|
||||
if (session.params.requiredInstalledVersionCode == PackageManager.VERSION_CODE_HIGHEST) {
|
||||
if (session.sessionParams().requiredInstalledVersionCode
|
||||
== PackageManager.VERSION_CODE_HIGHEST) {
|
||||
return;
|
||||
}
|
||||
final long activeVersion = activePackage.applicationInfo.longVersionCode;
|
||||
if (activeVersion != session.params.requiredInstalledVersionCode) {
|
||||
if (activeVersion != session.sessionParams().requiredInstalledVersionCode) {
|
||||
throw new PackageManagerException(
|
||||
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||
"Installed version of APEX package " + activePackage.packageName
|
||||
+ " does not match required. Active version: " + activeVersion
|
||||
+ " required: " + session.params.requiredInstalledVersionCode);
|
||||
+ " required: " + session.sessionParams().requiredInstalledVersionCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDowngrade(final PackageInstallerSession session,
|
||||
private void checkDowngrade(final StagedSession session,
|
||||
final PackageInfo activePackage, final PackageInfo newPackage)
|
||||
throws PackageManagerException {
|
||||
final long activeVersion = activePackage.applicationInfo.longVersionCode;
|
||||
@@ -311,7 +343,7 @@ public class StagingManager {
|
||||
final boolean isAppDebuggable = (activePackage.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
||||
final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
|
||||
session.params.installFlags, isAppDebuggable);
|
||||
session.sessionParams().installFlags, isAppDebuggable);
|
||||
if (activeVersion > newVersionCode && !allowsDowngrade) {
|
||||
throw new PackageManagerException(
|
||||
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||
@@ -363,10 +395,10 @@ public class StagingManager {
|
||||
/**
|
||||
* Utility function for extracting apex sessions out of multi-package/single session.
|
||||
*/
|
||||
private List<PackageInstallerSession> extractApexSessions(PackageInstallerSession session) {
|
||||
List<PackageInstallerSession> apexSessions = new ArrayList<>();
|
||||
private List<StagedSession> extractApexSessions(StagedSession session) {
|
||||
List<StagedSession> apexSessions = new ArrayList<>();
|
||||
if (session.isMultiPackage()) {
|
||||
for (PackageInstallerSession s : session.getChildSessions()) {
|
||||
for (StagedSession s : session.getChildSessions()) {
|
||||
if (s.containsApexSession()) {
|
||||
apexSessions.add(s);
|
||||
}
|
||||
@@ -383,14 +415,14 @@ public class StagingManager {
|
||||
*
|
||||
* @throws PackageManagerException if any apk-in-apex failed to install
|
||||
*/
|
||||
private void checkInstallationOfApkInApexSuccessful(PackageInstallerSession session)
|
||||
private void checkInstallationOfApkInApexSuccessful(StagedSession session)
|
||||
throws PackageManagerException {
|
||||
final List<PackageInstallerSession> apexSessions = extractApexSessions(session);
|
||||
final List<StagedSession> apexSessions = extractApexSessions(session);
|
||||
if (apexSessions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (PackageInstallerSession apexSession : apexSessions) {
|
||||
for (StagedSession apexSession : apexSessions) {
|
||||
String packageName = apexSession.getPackageName();
|
||||
if (!mApexManager.isApkInApexInstallSuccess(packageName)) {
|
||||
throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||
@@ -405,16 +437,16 @@ public class StagingManager {
|
||||
* directory directly by PackageManager, as such, RollbackManager need to handle their data
|
||||
* separately here.
|
||||
*/
|
||||
private void snapshotAndRestoreForApexSession(PackageInstallerSession session) {
|
||||
private void snapshotAndRestoreForApexSession(StagedSession session) {
|
||||
boolean doSnapshotOrRestore =
|
||||
(session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
|
||||
|| session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
|
||||
(session.sessionParams().installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
|
||||
|| session.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK;
|
||||
if (!doSnapshotOrRestore) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all the apex sessions that needs processing
|
||||
final List<PackageInstallerSession> apexSessions = extractApexSessions(session);
|
||||
final List<StagedSession> apexSessions = extractApexSessions(session);
|
||||
if (apexSessions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -475,7 +507,7 @@ public class StagingManager {
|
||||
* session package names.
|
||||
* Logging needs to wait until the ACTION_BOOT_COMPLETED broadcast is sent.
|
||||
*/
|
||||
private void prepareForLoggingApexdRevert(@NonNull PackageInstallerSession session,
|
||||
private void prepareForLoggingApexdRevert(@NonNull StagedSession session,
|
||||
@NonNull String nativeFailureReason) {
|
||||
synchronized (mFailedPackageNames) {
|
||||
mNativeFailureReason = nativeFailureReason;
|
||||
@@ -485,15 +517,15 @@ public class StagingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeSession(@NonNull PackageInstallerSession session)
|
||||
private void resumeSession(@NonNull StagedSession session)
|
||||
throws PackageManagerException {
|
||||
Slog.d(TAG, "Resuming session " + session.sessionId);
|
||||
Slog.d(TAG, "Resuming session " + session.sessionId());
|
||||
|
||||
final boolean hasApex = session.containsApexSession();
|
||||
ApexSessionInfo apexSessionInfo = null;
|
||||
if (hasApex) {
|
||||
// Check with apexservice whether the apex packages have been activated.
|
||||
apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
|
||||
apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId());
|
||||
|
||||
// Prepare for logging a native crash during boot, if one occurred.
|
||||
if (apexSessionInfo != null && !TextUtils.isEmpty(
|
||||
@@ -506,7 +538,7 @@ public class StagingManager {
|
||||
// pre-reboot verification, perhaps because the device rebooted in the meantime.
|
||||
// Greedily re-trigger the pre-reboot verification. We want to avoid marking it as
|
||||
// failed when not in checkpoint mode, hence it is being processed separately.
|
||||
Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to "
|
||||
Slog.d(TAG, "Found pending staged session " + session.sessionId() + " still to "
|
||||
+ "be verified, resuming pre-reboot verification");
|
||||
mPreRebootVerificationHandler.startPreRebootVerification(session);
|
||||
return;
|
||||
@@ -523,21 +555,21 @@ public class StagingManager {
|
||||
// mode. If not, we fail all sessions.
|
||||
if (supportsCheckpoint() && !needsCheckpoint()) {
|
||||
String revertMsg = "Reverting back to safe state. Marking "
|
||||
+ session.sessionId + " as failed.";
|
||||
+ session.sessionId() + " as failed.";
|
||||
final String reasonForRevert = getReasonForRevert();
|
||||
if (!TextUtils.isEmpty(reasonForRevert)) {
|
||||
revertMsg += " Reason for revert: " + reasonForRevert;
|
||||
}
|
||||
Slog.d(TAG, revertMsg);
|
||||
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
|
||||
session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// Cannot continue staged install without knowing if fs-checkpoint is supported
|
||||
Slog.e(TAG, "Checkpoint support unknown. Aborting staged install for session "
|
||||
+ session.sessionId, e);
|
||||
+ session.sessionId(), e);
|
||||
// TODO: Mark all staged sessions together and reboot only once
|
||||
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
|
||||
session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
|
||||
"Checkpoint support unknown. Aborting staged install.");
|
||||
if (hasApex) {
|
||||
mApexManager.revertActiveSessions();
|
||||
@@ -568,7 +600,7 @@ public class StagingManager {
|
||||
// Apexd did not apply the session for some unknown reason. There is no
|
||||
// guarantee that apexd will install it next time. Safer to proactively mark
|
||||
// it as failed.
|
||||
final String errorMsg = "Staged session " + session.sessionId + "at boot "
|
||||
final String errorMsg = "Staged session " + session.sessionId() + "at boot "
|
||||
+ "didn't activate nor fail. Marking it as failed anyway.";
|
||||
throw new PackageManagerException(
|
||||
SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
|
||||
@@ -580,38 +612,38 @@ public class StagingManager {
|
||||
checkInstallationOfApkInApexSuccessful(session);
|
||||
checkDuplicateApkInApex(session);
|
||||
snapshotAndRestoreForApexSession(session);
|
||||
Slog.i(TAG, "APEX packages in session " + session.sessionId
|
||||
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);
|
||||
Slog.d(TAG, "Installing APK packages in session " + session.sessionId());
|
||||
installApksInSession(session);
|
||||
|
||||
Slog.d(TAG, "Marking session " + session.sessionId + " as applied");
|
||||
session.setStagedSessionApplied();
|
||||
Slog.d(TAG, "Marking session " + session.sessionId() + " as applied");
|
||||
session.setSessionApplied();
|
||||
if (hasApex) {
|
||||
try {
|
||||
if (supportsCheckpoint()) {
|
||||
// Store the session ID, which will be marked as successful by ApexManager
|
||||
// upon boot completion.
|
||||
synchronized (mSuccessfulStagedSessionIds) {
|
||||
mSuccessfulStagedSessionIds.add(session.sessionId);
|
||||
mSuccessfulStagedSessionIds.add(session.sessionId());
|
||||
}
|
||||
} else {
|
||||
// Mark sessions as successful immediately on non-checkpointing devices.
|
||||
mApexManager.markStagedSessionSuccessful(session.sessionId);
|
||||
mApexManager.markStagedSessionSuccessful(session.sessionId());
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Checkpoint support unknown, marking session as successful "
|
||||
+ "immediately.");
|
||||
mApexManager.markStagedSessionSuccessful(session.sessionId);
|
||||
mApexManager.markStagedSessionSuccessful(session.sessionId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onInstallationFailure(PackageInstallerSession session, PackageManagerException e) {
|
||||
session.setStagedSessionFailed(e.error, e.getMessage());
|
||||
abortCheckpoint(session.sessionId, e.getMessage());
|
||||
void onInstallationFailure(StagedSession session, PackageManagerException e) {
|
||||
session.setSessionFailed(e.error, e.getMessage());
|
||||
abortCheckpoint(session.sessionId(), e.getMessage());
|
||||
|
||||
// If checkpoint is not supported, we have to handle failure for one staged session.
|
||||
if (!session.containsApexSession()) {
|
||||
@@ -641,26 +673,26 @@ public class StagingManager {
|
||||
/**
|
||||
* Throws a PackageManagerException if there are duplicate packages in apk and apk-in-apex.
|
||||
*/
|
||||
private void checkDuplicateApkInApex(@NonNull PackageInstallerSession session)
|
||||
private void checkDuplicateApkInApex(@NonNull StagedSession session)
|
||||
throws PackageManagerException {
|
||||
if (!session.isMultiPackage()) {
|
||||
return;
|
||||
}
|
||||
final Set<String> apkNames = new ArraySet<>();
|
||||
for (PackageInstallerSession s : session.getChildSessions()) {
|
||||
for (StagedSession s : session.getChildSessions()) {
|
||||
if (!s.isApexSession()) {
|
||||
apkNames.add(s.getPackageName());
|
||||
}
|
||||
}
|
||||
final List<PackageInstallerSession> apexSessions = extractApexSessions(session);
|
||||
for (PackageInstallerSession apexSession : apexSessions) {
|
||||
final List<StagedSession> apexSessions = extractApexSessions(session);
|
||||
for (StagedSession apexSession : apexSessions) {
|
||||
String packageName = apexSession.getPackageName();
|
||||
for (String apkInApex : mApexManager.getApksInApex(packageName)) {
|
||||
if (!apkNames.add(apkInApex)) {
|
||||
throw new PackageManagerException(
|
||||
SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||
"Package: " + packageName + " in session: "
|
||||
+ apexSession.sessionId + " has duplicate apk-in-apex: "
|
||||
+ apexSession.sessionId() + " has duplicate apk-in-apex: "
|
||||
+ apkInApex, null);
|
||||
|
||||
}
|
||||
@@ -668,14 +700,14 @@ public class StagingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void installApksInSession(PackageInstallerSession session)
|
||||
private void installApksInSession(StagedSession session)
|
||||
throws PackageManagerException {
|
||||
if (!session.containsApkSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
|
||||
session.installStagedSession(receiver.getIntentSender());
|
||||
session.installSession(receiver.getIntentSender());
|
||||
final Intent result = receiver.getResult();
|
||||
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
|
||||
PackageInstaller.STATUS_FAILURE);
|
||||
@@ -683,31 +715,31 @@ public class StagingManager {
|
||||
final String errorMessage = result.getStringExtra(
|
||||
PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||
Slog.e(TAG, "Failure to install APK staged session "
|
||||
+ session.sessionId + " [" + errorMessage + "]");
|
||||
+ session.sessionId() + " [" + errorMessage + "]");
|
||||
throw new PackageManagerException(
|
||||
SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void commitSession(@NonNull PackageInstallerSession session) {
|
||||
void commitSession(@NonNull StagedSession session) {
|
||||
// Store this parent session which will be used to check overlapping later
|
||||
createSession(session);
|
||||
mPreRebootVerificationHandler.startPreRebootVerification(session);
|
||||
}
|
||||
|
||||
private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
|
||||
return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
|
||||
private int getSessionIdForParentOrSelf(StagedSession session) {
|
||||
return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId();
|
||||
}
|
||||
|
||||
private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
|
||||
private StagedSession getParentSessionOrSelf(StagedSession 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;
|
||||
private boolean isRollback(StagedSession session) {
|
||||
final StagedSession root = getParentSessionOrSelf(session);
|
||||
return root.sessionParams().installReason == PackageManager.INSTALL_REASON_ROLLBACK;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -722,7 +754,7 @@ public class StagingManager {
|
||||
* @throws PackageManagerException if session fails the check
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void checkNonOverlappingWithStagedSessions(@NonNull PackageInstallerSession session)
|
||||
void checkNonOverlappingWithStagedSessions(@NonNull StagedSession session)
|
||||
throws PackageManagerException {
|
||||
if (session.isMultiPackage()) {
|
||||
// We cannot say a parent session overlaps until we process its children
|
||||
@@ -732,7 +764,7 @@ public class StagingManager {
|
||||
String packageName = session.getPackageName();
|
||||
if (packageName == null) {
|
||||
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||
"Cannot stage session " + session.sessionId + " with package name null");
|
||||
"Cannot stage session " + session.sessionId() + " with package name null");
|
||||
}
|
||||
|
||||
boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
|
||||
@@ -742,9 +774,9 @@ public class StagingManager {
|
||||
|
||||
synchronized (mStagedSessions) {
|
||||
for (int i = 0; i < mStagedSessions.size(); i++) {
|
||||
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
|
||||
final StagedSession stagedSession = mStagedSessions.valueAt(i);
|
||||
if (stagedSession.hasParentSessionId() || !stagedSession.isCommitted()
|
||||
|| stagedSession.isStagedAndInTerminalState()
|
||||
|| stagedSession.isInTerminalState()
|
||||
|| stagedSession.isDestroyed()) {
|
||||
continue;
|
||||
}
|
||||
@@ -759,8 +791,8 @@ public class StagingManager {
|
||||
// From here on, stagedSession is a parent active staged session
|
||||
|
||||
// Check if session is one of the active sessions
|
||||
if (getSessionIdForParentOrSelf(session) == stagedSession.sessionId) {
|
||||
Slog.w(TAG, "Session " + session.sessionId + " is already staged");
|
||||
if (getSessionIdForParentOrSelf(session) == stagedSession.sessionId()) {
|
||||
Slog.w(TAG, "Session " + session.sessionId() + " is already staged");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -769,24 +801,25 @@ public class StagingManager {
|
||||
if (isRollback) {
|
||||
// If the new session is a rollback, then it gets priority. The existing
|
||||
// session is failed to unblock rollback.
|
||||
final PackageInstallerSession root = stagedSession;
|
||||
final StagedSession root = stagedSession;
|
||||
if (!ensureActiveApexSessionIsAborted(root)) {
|
||||
Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
|
||||
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(
|
||||
root.setSessionFailed(
|
||||
SessionInfo.STAGED_SESSION_CONFLICT,
|
||||
"Session was blocking rollback session: " + session.sessionId);
|
||||
Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
|
||||
+ "blocking rollback session: " + session.sessionId);
|
||||
"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(
|
||||
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
|
||||
"Package: " + session.getPackageName() + " in session: "
|
||||
+ session.sessionId + " has been staged already by session:"
|
||||
+ " " + stagedSession.sessionId, null);
|
||||
+ session.sessionId()
|
||||
+ " has been staged already by session: "
|
||||
+ stagedSession.sessionId(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,24 +836,24 @@ public class StagingManager {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void createSession(@NonNull PackageInstallerSession sessionInfo) {
|
||||
void createSession(@NonNull StagedSession sessionInfo) {
|
||||
synchronized (mStagedSessions) {
|
||||
mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
|
||||
mStagedSessions.append(sessionInfo.sessionId(), sessionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void abortSession(@NonNull PackageInstallerSession session) {
|
||||
void abortSession(@NonNull StagedSession session) {
|
||||
synchronized (mStagedSessions) {
|
||||
mStagedSessions.remove(session.sessionId);
|
||||
mStagedSessions.remove(session.sessionId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Abort committed staged session
|
||||
*/
|
||||
void abortCommittedSession(@NonNull PackageInstallerSession session) {
|
||||
int sessionId = session.sessionId;
|
||||
if (session.isStagedAndInTerminalState()) {
|
||||
void abortCommittedSession(@NonNull StagedSession session) {
|
||||
int sessionId = session.sessionId();
|
||||
if (session.isInTerminalState()) {
|
||||
Slog.w(TAG, "Cannot abort session in final state: " + sessionId);
|
||||
return;
|
||||
}
|
||||
@@ -834,12 +867,12 @@ public class StagingManager {
|
||||
}
|
||||
|
||||
// A session could be marked ready once its pre-reboot verification ends
|
||||
if (session.isStagedSessionReady()) {
|
||||
if (session.isSessionReady()) {
|
||||
if (!ensureActiveApexSessionIsAborted(session)) {
|
||||
// Failed to ensure apex session is aborted, so it can 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.e(TAG, "Failed to abort apex session " + session.sessionId);
|
||||
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,15 +886,15 @@ public class StagingManager {
|
||||
*
|
||||
* @return returns true if it is ensured that there is no active apex session, otherwise false
|
||||
*/
|
||||
private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
|
||||
private boolean ensureActiveApexSessionIsAborted(StagedSession session) {
|
||||
if (!session.containsApexSession()) {
|
||||
return true;
|
||||
}
|
||||
final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
|
||||
final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId());
|
||||
if (apexSession == null || isApexSessionFinalized(apexSession)) {
|
||||
return true;
|
||||
}
|
||||
return mApexManager.abortStagedSession(session.sessionId);
|
||||
return mApexManager.abortStagedSession(session.sessionId());
|
||||
}
|
||||
|
||||
private boolean isApexSessionFinalized(ApexSessionInfo session) {
|
||||
@@ -878,7 +911,7 @@ public class StagingManager {
|
||||
|| apexSessionInfo.isRevertFailed;
|
||||
}
|
||||
|
||||
void restoreSession(@NonNull PackageInstallerSession session, boolean isDeviceUpgrading) {
|
||||
void restoreSession(@NonNull StagedSession session, boolean isDeviceUpgrading) {
|
||||
if (session.hasParentSessionId()) {
|
||||
// Only parent sessions can be restored
|
||||
return;
|
||||
@@ -887,16 +920,16 @@ public class StagingManager {
|
||||
createSession(session);
|
||||
// The preconditions used during pre-reboot verification might have changed when device
|
||||
// is upgrading. Updated staged sessions to activation failed before we resume the session.
|
||||
PackageInstallerSession sessionToResume = session;
|
||||
if (isDeviceUpgrading && !sessionToResume.isStagedAndInTerminalState()) {
|
||||
sessionToResume.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||
StagedSession sessionToResume = session;
|
||||
if (isDeviceUpgrading && !sessionToResume.isInTerminalState()) {
|
||||
sessionToResume.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
|
||||
"Build fingerprint has changed");
|
||||
return;
|
||||
}
|
||||
checkStateAndResume(sessionToResume);
|
||||
}
|
||||
|
||||
private void checkStateAndResume(@NonNull PackageInstallerSession session) {
|
||||
private void checkStateAndResume(@NonNull StagedSession session) {
|
||||
// Do not resume session if boot completed already
|
||||
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
|
||||
return;
|
||||
@@ -907,7 +940,7 @@ public class StagingManager {
|
||||
return;
|
||||
}
|
||||
// Check the state of the session and decide what to do next.
|
||||
if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) {
|
||||
if (session.isSessionFailed() || session.isSessionApplied()) {
|
||||
// Final states, nothing to do.
|
||||
return;
|
||||
}
|
||||
@@ -916,7 +949,7 @@ public class StagingManager {
|
||||
session.abandon();
|
||||
return;
|
||||
}
|
||||
if (!session.isStagedSessionReady()) {
|
||||
if (!session.isSessionReady()) {
|
||||
// The framework got restarted before the pre-reboot verification could complete,
|
||||
// restart the verification.
|
||||
mPreRebootVerificationHandler.startPreRebootVerification(session);
|
||||
@@ -998,8 +1031,8 @@ public class StagingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private PackageInstallerSession getStagedSession(int sessionId) {
|
||||
PackageInstallerSession session;
|
||||
private StagedSession getStagedSession(int sessionId) {
|
||||
StagedSession session;
|
||||
synchronized (mStagedSessions) {
|
||||
session = mStagedSessions.get(sessionId);
|
||||
}
|
||||
@@ -1009,20 +1042,20 @@ public class StagingManager {
|
||||
// TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
|
||||
// verification logic is extracted out of StagingManager into PMS, we can remove
|
||||
// this.
|
||||
void notifyVerificationComplete(PackageInstallerSession session) {
|
||||
void notifyVerificationComplete(StagedSession session) {
|
||||
mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
|
||||
}
|
||||
|
||||
// TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
|
||||
// verification logic is extracted out of StagingManager into PMS, we can remove
|
||||
// this.
|
||||
void notifyPreRebootVerification_Apk_Complete(@NonNull PackageInstallerSession session) {
|
||||
void notifyPreRebootVerification_Apk_Complete(@NonNull StagedSession session) {
|
||||
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
|
||||
}
|
||||
|
||||
private final class PreRebootVerificationHandler extends Handler {
|
||||
// Hold sessions before handler gets ready to do the verification.
|
||||
private List<PackageInstallerSession> mPendingSessions;
|
||||
private List<StagedSession> mPendingSessions;
|
||||
private boolean mIsReady;
|
||||
|
||||
PreRebootVerificationHandler(Looper looper) {
|
||||
@@ -1052,8 +1085,8 @@ public class StagingManager {
|
||||
public void handleMessage(Message msg) {
|
||||
final int sessionId = msg.arg1;
|
||||
final int rollbackId = msg.arg2;
|
||||
final PackageInstallerSession session = (PackageInstallerSession) msg.obj;
|
||||
if (session.isDestroyed() || session.isStagedSessionFailed()) {
|
||||
final StagedSession session = (StagedSession) msg.obj;
|
||||
if (session.isDestroyed() || session.isSessionFailed()) {
|
||||
// No point in running verification on a destroyed/failed session
|
||||
onPreRebootVerificationComplete(session);
|
||||
return;
|
||||
@@ -1086,7 +1119,7 @@ public class StagingManager {
|
||||
mIsReady = true;
|
||||
if (mPendingSessions != null) {
|
||||
for (int i = 0; i < mPendingSessions.size(); i++) {
|
||||
PackageInstallerSession session = mPendingSessions.get(i);
|
||||
StagedSession session = mPendingSessions.get(i);
|
||||
startPreRebootVerification(session);
|
||||
}
|
||||
mPendingSessions = null;
|
||||
@@ -1095,7 +1128,7 @@ public class StagingManager {
|
||||
|
||||
// Method for starting the pre-reboot verification
|
||||
private synchronized void startPreRebootVerification(
|
||||
@NonNull PackageInstallerSession session) {
|
||||
@NonNull StagedSession session) {
|
||||
if (!mIsReady) {
|
||||
if (mPendingSessions == null) {
|
||||
mPendingSessions = new ArrayList<>();
|
||||
@@ -1104,47 +1137,47 @@ public class StagingManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.notifyStagedStartPreRebootVerification()) {
|
||||
int sessionId = session.sessionId;
|
||||
if (session.notifyStartPreRebootVerification()) {
|
||||
int sessionId = session.sessionId();
|
||||
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
|
||||
.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void onPreRebootVerificationFailure(PackageInstallerSession session,
|
||||
private void onPreRebootVerificationFailure(StagedSession session,
|
||||
@SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
|
||||
if (!ensureActiveApexSessionIsAborted(session)) {
|
||||
Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
|
||||
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);
|
||||
session.setSessionFailed(errorCode, errorMessage);
|
||||
onPreRebootVerificationComplete(session);
|
||||
}
|
||||
|
||||
// Things to do when pre-reboot verification completes for a particular sessionId
|
||||
private void onPreRebootVerificationComplete(PackageInstallerSession session) {
|
||||
int sessionId = session.sessionId;
|
||||
private void onPreRebootVerificationComplete(StagedSession session) {
|
||||
int sessionId = session.sessionId();
|
||||
Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
|
||||
session.notifyStagedEndPreRebootVerification();
|
||||
session.notifyEndPreRebootVerification();
|
||||
}
|
||||
|
||||
private void notifyPreRebootVerification_Start_Complete(
|
||||
@NonNull PackageInstallerSession session, int rollbackId) {
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session.sessionId, rollbackId, session)
|
||||
.sendToTarget();
|
||||
@NonNull StagedSession session, int rollbackId) {
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session.sessionId(), rollbackId,
|
||||
session).sendToTarget();
|
||||
}
|
||||
|
||||
private void notifyPreRebootVerification_Apex_Complete(
|
||||
@NonNull PackageInstallerSession session) {
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId, -1, session)
|
||||
@NonNull StagedSession session) {
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId(), -1, session)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
private void notifyPreRebootVerification_Apk_Complete(
|
||||
@NonNull PackageInstallerSession session) {
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId, -1, session)
|
||||
@NonNull StagedSession session) {
|
||||
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@@ -1153,10 +1186,10 @@ public class StagingManager {
|
||||
*
|
||||
* See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
|
||||
*/
|
||||
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
|
||||
private void handlePreRebootVerification_Start(@NonNull StagedSession session) {
|
||||
try {
|
||||
if (session.isMultiPackage()) {
|
||||
for (PackageInstallerSession s : session.getChildSessions()) {
|
||||
for (StagedSession s : session.getChildSessions()) {
|
||||
checkNonOverlappingWithStagedSessions(s);
|
||||
}
|
||||
} else {
|
||||
@@ -1168,7 +1201,8 @@ public class StagingManager {
|
||||
}
|
||||
|
||||
int rollbackId = -1;
|
||||
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
|
||||
if ((session.sessionParams().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.
|
||||
@@ -1177,14 +1211,15 @@ public class StagingManager {
|
||||
try {
|
||||
// NOTE: To stay consistent with the non-staged install flow, we don't fail the
|
||||
// entire install if rollbacks can't be enabled.
|
||||
rollbackId = rm.notifyStagedSession(session.sessionId);
|
||||
rollbackId = rm.notifyStagedSession(session.sessionId());
|
||||
} catch (RuntimeException re) {
|
||||
Slog.e(TAG, "Failed to notifyStagedSession for session: "
|
||||
+ session.sessionId, re);
|
||||
+ session.sessionId(), re);
|
||||
}
|
||||
} else if (session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
|
||||
} else if (session.sessionParams().installReason
|
||||
== PackageManager.INSTALL_REASON_ROLLBACK) {
|
||||
try {
|
||||
rollbackId = retrieveRollbackIdForCommitSession(session.sessionId);
|
||||
rollbackId = retrieveRollbackIdForCommitSession(session.sessionId());
|
||||
} catch (PackageManagerException e) {
|
||||
onPreRebootVerificationFailure(session, e.error, e.getMessage());
|
||||
return;
|
||||
@@ -1203,7 +1238,7 @@ public class StagingManager {
|
||||
* </ul></p>
|
||||
*/
|
||||
private void handlePreRebootVerification_Apex(
|
||||
@NonNull PackageInstallerSession session, int rollbackId) {
|
||||
@NonNull StagedSession session, int rollbackId) {
|
||||
final boolean hasApex = session.containsApexSession();
|
||||
|
||||
// APEX checks. For single-package sessions, check if they contain an APEX. For
|
||||
@@ -1233,12 +1268,12 @@ public class StagingManager {
|
||||
* {@link PackageManagerService} for verification and it notifies back the result via
|
||||
* {@link #notifyPreRebootVerification_Apk_Complete}
|
||||
*/
|
||||
private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
|
||||
private void handlePreRebootVerification_Apk(@NonNull StagedSession session) {
|
||||
if (!session.containsApkSession()) {
|
||||
notifyPreRebootVerification_Apk_Complete(session);
|
||||
return;
|
||||
}
|
||||
session.verifyStagedSession();
|
||||
session.verifySession();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1248,7 +1283,7 @@ public class StagingManager {
|
||||
* <li>marks session as ready</li>
|
||||
* </ul></p>
|
||||
*/
|
||||
private void handlePreRebootVerification_End(@NonNull PackageInstallerSession session) {
|
||||
private void handlePreRebootVerification_End(@NonNull StagedSession session) {
|
||||
// Before marking the session as ready, start checkpoint service if available
|
||||
try {
|
||||
IStorageManager storageManager = PackageHelper.getStorageManager();
|
||||
@@ -1280,15 +1315,15 @@ public class StagingManager {
|
||||
// On the other hand, if the order of the calls was inverted (first call apexd, then
|
||||
// mark session as ready), then if a device gets rebooted right after the call to apexd,
|
||||
// only apex part of the train will be applied, leaving device in an inconsistent state.
|
||||
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
|
||||
session.setStagedSessionReady();
|
||||
if (session.isStagedSessionReady()) {
|
||||
Slog.d(TAG, "Marking session " + session.sessionId() + " as ready");
|
||||
session.setSessionReady();
|
||||
if (session.isSessionReady()) {
|
||||
final boolean hasApex = session.containsApexSession();
|
||||
if (hasApex) {
|
||||
try {
|
||||
mApexManager.markStagedSessionReady(session.sessionId);
|
||||
mApexManager.markStagedSessionReady(session.sessionId());
|
||||
} catch (PackageManagerException e) {
|
||||
session.setStagedSessionFailed(e.error, e.getMessage());
|
||||
session.setSessionFailed(e.error, e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,11 @@ import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -70,8 +73,8 @@ public class StagingManagerTest {
|
||||
public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
|
||||
throws Exception {
|
||||
// Create 2 sessions with overlapping packages
|
||||
PackageInstallerSession session1 = createSession(111, "com.foo", 1);
|
||||
PackageInstallerSession session2 = createSession(222, "com.foo", 2);
|
||||
StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
|
||||
StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);
|
||||
|
||||
mStagingManager.createSession(session1);
|
||||
mStagingManager.createSession(session2);
|
||||
@@ -82,7 +85,7 @@ public class StagingManagerTest {
|
||||
() -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
|
||||
}
|
||||
|
||||
private PackageInstallerSession createSession(int sessionId, String packageName,
|
||||
private StagingManager.StagedSession createSession(int sessionId, String packageName,
|
||||
long committedMillis) {
|
||||
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
@@ -121,8 +124,12 @@ public class StagingManagerTest {
|
||||
/* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
|
||||
/* stagedSessionErrorMessage */ "no error");
|
||||
|
||||
session = spy(session);
|
||||
doReturn(packageName).when(session).getPackageName();
|
||||
return session;
|
||||
StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
|
||||
doReturn(packageName).when(stagedSession).getPackageName();
|
||||
doAnswer(invocation -> {
|
||||
Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
|
||||
return filter.test(stagedSession);
|
||||
}).when(stagedSession).sessionContains(any());
|
||||
return stagedSession;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user