Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +81 −103 Original line number Diff line number Diff line Loading @@ -469,8 +469,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Nullable final StagedSession mStagedSession; @VisibleForTesting public class StagedSession implements StagingManager.StagedSession { /** * 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. Loading @@ -479,6 +477,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Nullable private Runnable mPendingAbandonCallback; @VisibleForTesting public class StagedSession implements StagingManager.StagedSession { @Override public List<StagingManager.StagedSession> getChildSessions() { if (!params.isMultiPackage) { Loading Loading @@ -575,9 +575,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public boolean isInTerminalState() { synchronized (mLock) { return mSessionApplied || mSessionFailed; } return PackageInstallerSession.this.isInTerminalState(); } @Override Loading Loading @@ -612,48 +610,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void abandon() { final Runnable r; synchronized (mLock) { assertNotChild("StagedSession#abandon"); assertCallerIsOwnerOrRootOrSystem(); 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; r = () -> { assertNotLocked("abandonStaged"); if (mCommitted.get()) { mStagingManager.abortCommittedSession(this); } destroy(); dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "Session was abandoned because the parent session is abandoned"); }; if (mStageDirInUse) { // Pre-reboot verification is ongoing, not safe to clean up the session yet. mPendingAbandonCallback = r; mCallback.onSessionChanged(PackageInstallerSession.this); return; } } r.run(); } /** * Called when pre-reboot verification has ended. * Now it is safe to clean up the session if {@link #abandon()} has been called previously. */ private void notifyEndPreRebootVerification() { synchronized (mLock) { Preconditions.checkState(mStageDirInUse); mStageDirInUse = false; } dispatchPendingAbandonCallback(); PackageInstallerSession.this.abandon(); } /** Loading @@ -669,17 +626,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Preconditions.checkArgument(!isInTerminalState()); verify(); } private void dispatchPendingAbandonCallback() { final Runnable callback; synchronized (mLock) { callback = mPendingAbandonCallback; mPendingAbandonCallback = null; } if (callback != null) { callback.run(); } } } /** Loading Loading @@ -1138,9 +1084,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private boolean isInTerminalState() { synchronized (mLock) { return mSessionApplied || mSessionFailed; } } /** Returns true if a staged session has reached a final state and can be forgotten about */ public boolean isStagedAndInTerminalState() { return params.isStaged && mStagedSession.isInTerminalState(); return params.isStaged && isInTerminalState(); } private void assertNotLocked(String cookie) { Loading Loading @@ -1968,9 +1920,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void onSessionVerificationFailure(int error, String msg) { Slog.e(TAG, "Failed to verify session " + sessionId); if (isStaged()) { mStagedSession.notifyEndPreRebootVerification(); } // Dispatch message to remove session from PackageInstallerService. dispatchSessionFinished(error, msg, null); maybeFinishChildSessions(error, msg); Loading Loading @@ -2279,6 +2228,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } @GuardedBy("mLock") private void markStageDirInUseLocked() throws PackageManagerException { if (mDestroyed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } // Set this flag to prevent abandon() from deleting staging files when verification or // installation is about to start. mStageDirInUse = true; } private void parseApkAndExtractNativeLibraries() throws PackageManagerException { synchronized (mLock) { if (mStageDirInUse) { Loading Loading @@ -2324,19 +2284,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void verifyNonStaged() throws PackageManagerException { synchronized (mLock) { if (mDestroyed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } // Set this flag to prevent abandon() from deleting staging files while verification is // in progress. For staged sessions, we will reset this flag when verification is done // so abandon() can take effect. For non-staged sessions, the staging files will be // deleted when install is completed (no matter success or not). No need to reset // the flag. mStageDirInUse = true; markStageDirInUseLocked(); } mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> { mHandler.post(() -> { if (dispatchPendingAbandonCallback()) { // No need to continue if abandoned return; } if (error == INSTALL_SUCCEEDED) { onVerificationComplete(); } else { Loading Loading @@ -2426,7 +2381,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @WorkerThread private void onVerificationComplete() { if (isStaged()) { mStagedSession.notifyEndPreRebootVerification(); mStagingManager.commitSession(mStagedSession); sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null); return; Loading @@ -2444,14 +2398,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private InstallParams makeInstallParams(CompletableFuture<Void> future) throws PackageManagerException { synchronized (mLock) { if (mDestroyed) { throw new PackageManagerException( INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } if (!mSealed) { throw new PackageManagerException( INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); } markStageDirInUseLocked(); } if (isMultiPackage()) { Loading Loading @@ -3523,36 +3474,63 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private void abandonNonStaged() { private void assertNotChild(String cookie) { if (hasParentSessionId()) { throw new IllegalStateException(cookie + " can't be called on a child session, id=" + sessionId + " parentId=" + getParentSessionId()); } } /** * Called when verification has completed. Now it is safe to clean up the session * if {@link #abandon()} has been called previously. * * @return True if this session has been abandoned. */ private boolean dispatchPendingAbandonCallback() { final Runnable callback; synchronized (mLock) { Preconditions.checkState(mStageDirInUse); mStageDirInUse = false; callback = mPendingAbandonCallback; mPendingAbandonCallback = null; } if (callback != null) { callback.run(); return true; } return false; } @Override public void abandon() { final Runnable r; synchronized (mLock) { assertNotChild("abandonNonStaged"); assertNotChild("abandon"); assertCallerIsOwnerOrRootOrSystem(); if (mStageDirInUse) { if (LOGD) Slog.d(TAG, "Ignoring abandon for staging files are in use"); if (isInTerminalState()) { // Finalized sessions have been properly cleaned up. No need to abandon them. return; } mDestroyed = true; r = () -> { assertNotLocked("abandonStaged"); if (isStaged() && mCommitted.get()) { mStagingManager.abortCommittedSession(mStagedSession); } destroy(); dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "Session was abandoned because the parent session is abandoned"); }; if (mStageDirInUse) { // Verification is ongoing, not safe to clean up the session yet. mPendingAbandonCallback = r; mCallback.onSessionChanged(this); return; } private void assertNotChild(String cookie) { if (hasParentSessionId()) { throw new IllegalStateException(cookie + " can't be called on a child session, id=" + sessionId + " parentId=" + getParentSessionId()); } } @Override public void abandon() { if (params.isStaged) { mStagedSession.abandon(); } else { abandonNonStaged(); } r.run(); } @Override Loading Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +81 −103 Original line number Diff line number Diff line Loading @@ -469,8 +469,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Nullable final StagedSession mStagedSession; @VisibleForTesting public class StagedSession implements StagingManager.StagedSession { /** * 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. Loading @@ -479,6 +477,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Nullable private Runnable mPendingAbandonCallback; @VisibleForTesting public class StagedSession implements StagingManager.StagedSession { @Override public List<StagingManager.StagedSession> getChildSessions() { if (!params.isMultiPackage) { Loading Loading @@ -575,9 +575,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public boolean isInTerminalState() { synchronized (mLock) { return mSessionApplied || mSessionFailed; } return PackageInstallerSession.this.isInTerminalState(); } @Override Loading Loading @@ -612,48 +610,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void abandon() { final Runnable r; synchronized (mLock) { assertNotChild("StagedSession#abandon"); assertCallerIsOwnerOrRootOrSystem(); 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; r = () -> { assertNotLocked("abandonStaged"); if (mCommitted.get()) { mStagingManager.abortCommittedSession(this); } destroy(); dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "Session was abandoned because the parent session is abandoned"); }; if (mStageDirInUse) { // Pre-reboot verification is ongoing, not safe to clean up the session yet. mPendingAbandonCallback = r; mCallback.onSessionChanged(PackageInstallerSession.this); return; } } r.run(); } /** * Called when pre-reboot verification has ended. * Now it is safe to clean up the session if {@link #abandon()} has been called previously. */ private void notifyEndPreRebootVerification() { synchronized (mLock) { Preconditions.checkState(mStageDirInUse); mStageDirInUse = false; } dispatchPendingAbandonCallback(); PackageInstallerSession.this.abandon(); } /** Loading @@ -669,17 +626,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Preconditions.checkArgument(!isInTerminalState()); verify(); } private void dispatchPendingAbandonCallback() { final Runnable callback; synchronized (mLock) { callback = mPendingAbandonCallback; mPendingAbandonCallback = null; } if (callback != null) { callback.run(); } } } /** Loading Loading @@ -1138,9 +1084,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private boolean isInTerminalState() { synchronized (mLock) { return mSessionApplied || mSessionFailed; } } /** Returns true if a staged session has reached a final state and can be forgotten about */ public boolean isStagedAndInTerminalState() { return params.isStaged && mStagedSession.isInTerminalState(); return params.isStaged && isInTerminalState(); } private void assertNotLocked(String cookie) { Loading Loading @@ -1968,9 +1920,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void onSessionVerificationFailure(int error, String msg) { Slog.e(TAG, "Failed to verify session " + sessionId); if (isStaged()) { mStagedSession.notifyEndPreRebootVerification(); } // Dispatch message to remove session from PackageInstallerService. dispatchSessionFinished(error, msg, null); maybeFinishChildSessions(error, msg); Loading Loading @@ -2279,6 +2228,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } @GuardedBy("mLock") private void markStageDirInUseLocked() throws PackageManagerException { if (mDestroyed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } // Set this flag to prevent abandon() from deleting staging files when verification or // installation is about to start. mStageDirInUse = true; } private void parseApkAndExtractNativeLibraries() throws PackageManagerException { synchronized (mLock) { if (mStageDirInUse) { Loading Loading @@ -2324,19 +2284,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void verifyNonStaged() throws PackageManagerException { synchronized (mLock) { if (mDestroyed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } // Set this flag to prevent abandon() from deleting staging files while verification is // in progress. For staged sessions, we will reset this flag when verification is done // so abandon() can take effect. For non-staged sessions, the staging files will be // deleted when install is completed (no matter success or not). No need to reset // the flag. mStageDirInUse = true; markStageDirInUseLocked(); } mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> { mHandler.post(() -> { if (dispatchPendingAbandonCallback()) { // No need to continue if abandoned return; } if (error == INSTALL_SUCCEEDED) { onVerificationComplete(); } else { Loading Loading @@ -2426,7 +2381,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @WorkerThread private void onVerificationComplete() { if (isStaged()) { mStagedSession.notifyEndPreRebootVerification(); mStagingManager.commitSession(mStagedSession); sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null); return; Loading @@ -2444,14 +2398,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private InstallParams makeInstallParams(CompletableFuture<Void> future) throws PackageManagerException { synchronized (mLock) { if (mDestroyed) { throw new PackageManagerException( INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } if (!mSealed) { throw new PackageManagerException( INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); } markStageDirInUseLocked(); } if (isMultiPackage()) { Loading Loading @@ -3523,36 +3474,63 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } private void abandonNonStaged() { private void assertNotChild(String cookie) { if (hasParentSessionId()) { throw new IllegalStateException(cookie + " can't be called on a child session, id=" + sessionId + " parentId=" + getParentSessionId()); } } /** * Called when verification has completed. Now it is safe to clean up the session * if {@link #abandon()} has been called previously. * * @return True if this session has been abandoned. */ private boolean dispatchPendingAbandonCallback() { final Runnable callback; synchronized (mLock) { Preconditions.checkState(mStageDirInUse); mStageDirInUse = false; callback = mPendingAbandonCallback; mPendingAbandonCallback = null; } if (callback != null) { callback.run(); return true; } return false; } @Override public void abandon() { final Runnable r; synchronized (mLock) { assertNotChild("abandonNonStaged"); assertNotChild("abandon"); assertCallerIsOwnerOrRootOrSystem(); if (mStageDirInUse) { if (LOGD) Slog.d(TAG, "Ignoring abandon for staging files are in use"); if (isInTerminalState()) { // Finalized sessions have been properly cleaned up. No need to abandon them. return; } mDestroyed = true; r = () -> { assertNotLocked("abandonStaged"); if (isStaged() && mCommitted.get()) { mStagingManager.abortCommittedSession(mStagedSession); } destroy(); dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "Session was abandoned because the parent session is abandoned"); }; if (mStageDirInUse) { // Verification is ongoing, not safe to clean up the session yet. mPendingAbandonCallback = r; mCallback.onSessionChanged(this); return; } private void assertNotChild(String cookie) { if (hasParentSessionId()) { throw new IllegalStateException(cookie + " can't be called on a child session, id=" + sessionId + " parentId=" + getParentSessionId()); } } @Override public void abandon() { if (params.isStaged) { mStagedSession.abandon(); } else { abandonNonStaged(); } r.run(); } @Override Loading