Loading services/core/java/com/android/server/pm/PackageInstallerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -1270,7 +1270,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void onStagedSessionChanged(PackageInstallerSession session) { session.markUpdated(); writeSessionsAsync(); if (mOkToSendBroadcasts) { if (mOkToSendBroadcasts && !session.isDestroyed()) { // we don't scrub the data here as this is sent only to the installer several // privileged system packages mPm.sendSessionUpdatedBroadcast( Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +28 −5 Original line number Diff line number Diff line Loading @@ -1076,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}. */ Loading Loading @@ -2561,7 +2574,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + mParentSessionId + " and may not be abandoned directly."); } 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(); } if (isStagedAndInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be Loading @@ -2571,11 +2590,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } if (mCommitted && params.isStaged) { synchronized (mLock) { 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(); } Loading Loading @@ -2935,6 +2955,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionReady() { synchronized (mLock) { if (mDestroyed) return; // Do not allow destroyed staged session to change state mStagedSessionReady = true; mStagedSessionApplied = false; mStagedSessionFailed = false; Loading @@ -2948,6 +2969,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) { synchronized (mLock) { if (mDestroyed) return; // Do not allow destroyed staged session to change state mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; Loading @@ -2962,6 +2984,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionApplied() { synchronized (mLock) { if (mDestroyed) return; // Do not allow destroyed staged session to change state mStagedSessionReady = false; mStagedSessionApplied = true; mStagedSessionFailed = false; Loading services/core/java/com/android/server/pm/StagingManager.java +138 −35 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.text.TextUtils; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.apk.ApkSignatureVerifier; Loading Loading @@ -205,7 +206,7 @@ public class StagingManager { final IntArray childSessionIds = new IntArray(); if (session.isMultiPackage()) { for (int id : session.getChildSessionIds()) { if (isApexSession(mStagedSessions.get(id))) { if (isApexSession(getStagedSession(id))) { childSessionIds.add(id); } } Loading Loading @@ -800,6 +801,8 @@ public class StagingManager { + session.sessionId + " [" + errorMessage + "]"); session.setStagedSessionFailed( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); mPreRebootVerificationHandler.onPreRebootVerificationComplete( session.sessionId); return; } mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( Loading Loading @@ -883,7 +886,8 @@ public class StagingManager { synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()) { if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState() || stagedSession.isDestroyed()) { continue; } if (stagedSession.isMultiPackage()) { Loading Loading @@ -946,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()) { Slog.w(TAG, "Cannot abort applied session : " + session.sessionId); return; Slog.w(TAG, "Cannot abort applied session : " + sessionId); 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 (hasApex) { ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId); // If pre-reboot verification is running, then return false. StagingManager will call // abandon again when pre-reboot verification ends. 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)) { Slog.w(TAG, "Cannot abort session " + session.sessionId + " because it is not active or APEXD is not reachable"); return; } try { + " because it is not active."); } else { 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) { /* checking if the session is in a final state, i.e., not active anymore */ return session.isUnknown || session.isActivationFailed || session.isSuccess Loading Loading @@ -1045,6 +1090,11 @@ public class StagingManager { // Final states, nothing to do. return; } if (session.isDestroyed()) { // Device rebooted before abandoned session was cleaned up. session.abandon(); return; } if (!session.isStagedSessionReady()) { // The framework got restarted before the pre-reboot verification could complete, // restart the verification. Loading Loading @@ -1127,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 { // Hold session ids before handler gets ready to do the verification. private IntArray mPendingSessionIds; private boolean mIsReady; @GuardedBy("mVerificationRunning") private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray(); PreRebootVerificationHandler(Looper looper) { super(looper); Loading Loading @@ -1158,13 +1218,15 @@ public class StagingManager { @Override public void handleMessage(Message msg) { final int sessionId = msg.arg1; final PackageInstallerSession session; synchronized (mStagedSessions) { session = mStagedSessions.get(sessionId); } // Maybe session was aborted before pre-reboot verification was complete final PackageInstallerSession session = getStagedSession(sessionId); 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; } switch (msg.what) { Loading Loading @@ -1203,9 +1265,40 @@ public class StagingManager { mPendingSessionIds.add(sessionId); 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(); } // 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) { obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget(); } Loading @@ -1224,8 +1317,6 @@ public class StagingManager { * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification */ 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 rollback is enabled for this session, we call through to the RollbackManager // with the list of sessions it must enable rollback for. Note that Loading Loading @@ -1272,6 +1363,7 @@ public class StagingManager { } } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); onPreRebootVerificationComplete(session.sessionId); return; } Loading Loading @@ -1304,6 +1396,7 @@ public class StagingManager { // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); onPreRebootVerificationComplete(session.sessionId); } } Loading @@ -1326,9 +1419,18 @@ public class StagingManager { Slog.e(TAG, "Failed to get hold of StorageManager", e); session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, "Failed to get hold of StorageManager"); onPreRebootVerificationComplete(session.sessionId); 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 // looks counter-intuitive, this is the easiest way to ensure that session won't end up // in the inconsistent state: Loading @@ -1340,15 +1442,16 @@ public class StagingManager { // 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()) { final boolean hasApex = sessionContainsApex(session); if (!hasApex) { // Session doesn't contain apex, nothing to do. return; } if (hasApex) { try { mApexManager.markStagedSessionReady(session.sessionId); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); return; } } } } } Loading Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -1270,7 +1270,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void onStagedSessionChanged(PackageInstallerSession session) { session.markUpdated(); writeSessionsAsync(); if (mOkToSendBroadcasts) { if (mOkToSendBroadcasts && !session.isDestroyed()) { // we don't scrub the data here as this is sent only to the installer several // privileged system packages mPm.sendSessionUpdatedBroadcast( Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +28 −5 Original line number Diff line number Diff line Loading @@ -1076,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}. */ Loading Loading @@ -2561,7 +2574,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + mParentSessionId + " and may not be abandoned directly."); } 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(); } if (isStagedAndInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be Loading @@ -2571,11 +2590,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } if (mCommitted && params.isStaged) { synchronized (mLock) { 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(); } Loading Loading @@ -2935,6 +2955,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionReady() { synchronized (mLock) { if (mDestroyed) return; // Do not allow destroyed staged session to change state mStagedSessionReady = true; mStagedSessionApplied = false; mStagedSessionFailed = false; Loading @@ -2948,6 +2969,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) { synchronized (mLock) { if (mDestroyed) return; // Do not allow destroyed staged session to change state mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; Loading @@ -2962,6 +2984,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionApplied() { synchronized (mLock) { if (mDestroyed) return; // Do not allow destroyed staged session to change state mStagedSessionReady = false; mStagedSessionApplied = true; mStagedSessionFailed = false; Loading
services/core/java/com/android/server/pm/StagingManager.java +138 −35 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.text.TextUtils; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.apk.ApkSignatureVerifier; Loading Loading @@ -205,7 +206,7 @@ public class StagingManager { final IntArray childSessionIds = new IntArray(); if (session.isMultiPackage()) { for (int id : session.getChildSessionIds()) { if (isApexSession(mStagedSessions.get(id))) { if (isApexSession(getStagedSession(id))) { childSessionIds.add(id); } } Loading Loading @@ -800,6 +801,8 @@ public class StagingManager { + session.sessionId + " [" + errorMessage + "]"); session.setStagedSessionFailed( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); mPreRebootVerificationHandler.onPreRebootVerificationComplete( session.sessionId); return; } mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( Loading Loading @@ -883,7 +886,8 @@ public class StagingManager { synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()) { if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState() || stagedSession.isDestroyed()) { continue; } if (stagedSession.isMultiPackage()) { Loading Loading @@ -946,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()) { Slog.w(TAG, "Cannot abort applied session : " + session.sessionId); return; Slog.w(TAG, "Cannot abort applied session : " + sessionId); 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 (hasApex) { ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId); // If pre-reboot verification is running, then return false. StagingManager will call // abandon again when pre-reboot verification ends. 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)) { Slog.w(TAG, "Cannot abort session " + session.sessionId + " because it is not active or APEXD is not reachable"); return; } try { + " because it is not active."); } else { 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) { /* checking if the session is in a final state, i.e., not active anymore */ return session.isUnknown || session.isActivationFailed || session.isSuccess Loading Loading @@ -1045,6 +1090,11 @@ public class StagingManager { // Final states, nothing to do. return; } if (session.isDestroyed()) { // Device rebooted before abandoned session was cleaned up. session.abandon(); return; } if (!session.isStagedSessionReady()) { // The framework got restarted before the pre-reboot verification could complete, // restart the verification. Loading Loading @@ -1127,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 { // Hold session ids before handler gets ready to do the verification. private IntArray mPendingSessionIds; private boolean mIsReady; @GuardedBy("mVerificationRunning") private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray(); PreRebootVerificationHandler(Looper looper) { super(looper); Loading Loading @@ -1158,13 +1218,15 @@ public class StagingManager { @Override public void handleMessage(Message msg) { final int sessionId = msg.arg1; final PackageInstallerSession session; synchronized (mStagedSessions) { session = mStagedSessions.get(sessionId); } // Maybe session was aborted before pre-reboot verification was complete final PackageInstallerSession session = getStagedSession(sessionId); 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; } switch (msg.what) { Loading Loading @@ -1203,9 +1265,40 @@ public class StagingManager { mPendingSessionIds.add(sessionId); 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(); } // 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) { obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget(); } Loading @@ -1224,8 +1317,6 @@ public class StagingManager { * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification */ 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 rollback is enabled for this session, we call through to the RollbackManager // with the list of sessions it must enable rollback for. Note that Loading Loading @@ -1272,6 +1363,7 @@ public class StagingManager { } } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); onPreRebootVerificationComplete(session.sessionId); return; } Loading Loading @@ -1304,6 +1396,7 @@ public class StagingManager { // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); onPreRebootVerificationComplete(session.sessionId); } } Loading @@ -1326,9 +1419,18 @@ public class StagingManager { Slog.e(TAG, "Failed to get hold of StorageManager", e); session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, "Failed to get hold of StorageManager"); onPreRebootVerificationComplete(session.sessionId); 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 // looks counter-intuitive, this is the easiest way to ensure that session won't end up // in the inconsistent state: Loading @@ -1340,15 +1442,16 @@ public class StagingManager { // 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()) { final boolean hasApex = sessionContainsApex(session); if (!hasApex) { // Session doesn't contain apex, nothing to do. return; } if (hasApex) { try { mApexManager.markStagedSessionReady(session.sessionId); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); return; } } } } } Loading