Loading core/java/android/content/pm/PackageInstaller.java +14 −0 Original line number Diff line number Diff line Loading @@ -1211,6 +1211,9 @@ public class PackageInstaller { * Adds a session ID to the set of sessions that will be committed atomically * when this session is committed. * * <p>If the parent is staged or has rollback enabled, all children must have * the same properties. * * @param sessionId the session ID to add to this multi-package session. */ public void addChildSessionId(int sessionId) { Loading Loading @@ -1480,6 +1483,9 @@ public class PackageInstaller { /** * Request that rollbacks be enabled or disabled for the given upgrade. * * <p>If the parent session is staged or has rollback enabled, all children sessions * must have the same properties. * * @param enable set to {@code true} to enable, {@code false} to disable * @hide */ Loading Loading @@ -1607,6 +1613,9 @@ public class PackageInstaller { * multi-package. In that case, if any of the children sessions fail to install at reboot, * all the other children sessions are aborted as well. * * <p>If the parent session is staged or has rollback enabled, all children sessions * must have the same properties. * * {@hide} */ @SystemApi @TestApi Loading @@ -1626,6 +1635,11 @@ public class PackageInstaller { installFlags |= PackageManager.INSTALL_APEX; } /** @hide */ public boolean getEnableRollback() { return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0; } /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); Loading core/java/android/content/pm/PackageManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1397,6 +1397,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119; /** * Installation failed return code: one of the child sessions does not match the parent session * in respect to staged or rollback enabled parameters. * * @hide */ public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120; /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, Loading services/core/java/com/android/server/pm/PackageInstallerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -396,6 +396,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } finally { IoUtils.closeQuietly(fis); } // After all of the sessions were loaded, they are ready to be sealed and validated for (int i = 0; i < mSessions.size(); ++i) { PackageInstallerSession session = mSessions.valueAt(i); session.sealAndValidateIfNecessary(); } } @GuardedBy("mSessions") Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +113 −30 Original line number Diff line number Diff line Loading @@ -231,6 +231,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private boolean mSealed = false; @GuardedBy("mLock") private boolean mShouldBeSealed = false; @GuardedBy("mLock") private boolean mCommitted = false; @GuardedBy("mLock") private boolean mRelinquished = false; Loading Loading @@ -430,6 +432,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { this.updatedMillis = createdMillis; this.stageDir = stageDir; this.stageCid = stageCid; this.mShouldBeSealed = sealed; if (childSessionIds != null) { for (int childSessionId : childSessionIds) { mChildSessionIds.put(childSessionId, 0); Loading @@ -450,16 +453,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionErrorCode = stagedSessionErrorCode; mStagedSessionErrorMessage = stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; if (sealed) { synchronized (mLock) { try { sealAndValidateLocked(); } catch (PackageManagerException | IOException e) { destroyInternal(); throw new IllegalArgumentException(e); } } } } public SessionInfo generateInfo() { Loading Loading @@ -932,6 +925,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull IntentSender statusReceiver, boolean forTransfer) { Preconditions.checkNotNull(statusReceiver); List<PackageInstallerSession> childSessions = getChildSessions(); final boolean wasSealed; synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); Loading Loading @@ -963,7 +958,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { wasSealed = mSealed; if (!mSealed) { try { sealAndValidateLocked(); sealAndValidateLocked(childSessions); } catch (IOException e) { throw new IllegalArgumentException(e); } catch (PackageManagerException e) { Loading Loading @@ -994,21 +989,91 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } /** Return a list of child sessions or null if the session is not multipackage * * <p> This method is handy to prevent potential deadlocks (b/123391593) */ private @Nullable List<PackageInstallerSession> getChildSessions() { List<PackageInstallerSession> childSessions = null; if (isMultiPackage()) { final int[] childSessionIds = getChildSessionIds(); childSessions = new ArrayList<>(childSessionIds.length); for (int childSessionId : childSessionIds) { childSessions.add(mSessionProvider.getSession(childSessionId)); } } return childSessions; } /** * Assert multipackage install has consistent sessions. * * @throws PackageManagerException if child sessions don't match parent session * in respect to staged and enable rollback parameters. */ @GuardedBy("mLock") private void assertMultiPackageConsistencyLocked( @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException { for (PackageInstallerSession childSession : childSessions) { // It might be that the parent session is loaded before all of it's child sessions are, // e.g. when reading sessions from XML. Those sessions will be null here, and their // conformance with the multipackage params will be checked when they're loaded. if (childSession == null) { continue; } assertConsistencyWithLocked(childSession); } } /** * Assert consistency with the given session. * * @throws PackageManagerException if other sessions doesn't match this session * in respect to staged and enable rollback parameters. */ @GuardedBy("mLock") private void assertConsistencyWithLocked(PackageInstallerSession other) throws PackageManagerException { // Session groups must be consistent wrt to isStaged parameter. Non-staging session // cannot be grouped with staging sessions. if (this.params.isStaged != other.params.isStaged) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, "Multipackage Inconsistency: session " + other.sessionId + " and session " + sessionId + " have inconsistent staged settings"); } if (this.params.getEnableRollback() != other.params.getEnableRollback()) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, "Multipackage Inconsistency: session " + other.sessionId + " and session " + sessionId + " have inconsistent rollback settings"); } } /** * Seal the session to prevent further modification and validate the contents of it. * * <p>The session will be sealed after calling this method even if it failed. * * @param childSessions the child sessions of a multipackage that will be checked for * consistency. Can be null if session is not multipackage. * @throws PackageManagerException if the session was sealed but something went wrong. If the * session was sealed this is the only possible exception. */ @GuardedBy("mLock") private void sealAndValidateLocked() throws PackageManagerException, IOException { private void sealAndValidateLocked(List<PackageInstallerSession> childSessions) throws PackageManagerException, IOException { assertNoWriteFileTransfersOpenLocked(); assertPreparedAndNotDestroyedLocked("sealing of session"); mSealed = true; if (childSessions != null) { assertMultiPackageConsistencyLocked(childSessions); } if (params.isStaged) { final PackageInstallerSession activeSession = mStagingManager.getActiveSession(); final boolean anotherSessionAlreadyInProgress = Loading Loading @@ -1048,6 +1113,38 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } /** * If session should be sealed, then it's sealed to prevent further modification * and then it's validated. * * If the session was sealed but something went wrong then it's destroyed. * * <p> This is meant to be called after all of the sessions are loaded and added to * PackageInstallerService */ void sealAndValidateIfNecessary() { synchronized (mLock) { if (!mShouldBeSealed) { return; } } List<PackageInstallerSession> childSessions = getChildSessions(); synchronized (mLock) { try { sealAndValidateLocked(childSessions); } catch (IOException e) { throw new IllegalStateException(e); } catch (PackageManagerException e) { Slog.e(TAG, "Package not valid", e); // Session is sealed but could not be verified, we need to destroy it. destroyInternal(); // Dispatch message to remove session from PackageInstallerService dispatchSessionFinished( e.error, ExceptionUtils.getCompleteMessage(e), null); } } } /** Update the timestamp of when the staged session last changed state */ public void markUpdated() { synchronized (mLock) { Loading Loading @@ -1076,12 +1173,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new SecurityException("Can only transfer sessions that use public options"); } List<PackageInstallerSession> childSessions = getChildSessions(); synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("transfer"); try { sealAndValidateLocked(); sealAndValidateLocked(childSessions); } catch (IOException e) { throw new IllegalStateException(e); } catch (PackageManagerException e) { Loading Loading @@ -1132,14 +1231,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // outside of the lock, because reading the child // sessions with the lock held could lead to deadlock // (b/123391593). List<PackageInstallerSession> childSessions = null; if (isMultiPackage()) { final int[] childSessionIds = getChildSessionIds(); childSessions = new ArrayList<>(childSessionIds.length); for (int childSessionId : childSessionIds) { childSessions.add(mSessionProvider.getSession(childSessionId)); } } List<PackageInstallerSession> childSessions = getChildSessions(); try { synchronized (mLock) { Loading Loading @@ -1965,15 +2057,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + " does not exist"), false, true).rethrowAsRuntimeException(); } // Session groups must be consistent wrt to isStaged parameter. Non-staging session // cannot be grouped with staging sessions. if (this.params.isStaged ^ childSession.params.isStaged) { throw new RemoteException("Unable to add child.", new PackageManagerException("Child session " + childSessionId + " and parent session " + this.sessionId + " do not have consistent" + " staging session settings."), false, true); } synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("addChildSessionId"); Loading Loading
core/java/android/content/pm/PackageInstaller.java +14 −0 Original line number Diff line number Diff line Loading @@ -1211,6 +1211,9 @@ public class PackageInstaller { * Adds a session ID to the set of sessions that will be committed atomically * when this session is committed. * * <p>If the parent is staged or has rollback enabled, all children must have * the same properties. * * @param sessionId the session ID to add to this multi-package session. */ public void addChildSessionId(int sessionId) { Loading Loading @@ -1480,6 +1483,9 @@ public class PackageInstaller { /** * Request that rollbacks be enabled or disabled for the given upgrade. * * <p>If the parent session is staged or has rollback enabled, all children sessions * must have the same properties. * * @param enable set to {@code true} to enable, {@code false} to disable * @hide */ Loading Loading @@ -1607,6 +1613,9 @@ public class PackageInstaller { * multi-package. In that case, if any of the children sessions fail to install at reboot, * all the other children sessions are aborted as well. * * <p>If the parent session is staged or has rollback enabled, all children sessions * must have the same properties. * * {@hide} */ @SystemApi @TestApi Loading @@ -1626,6 +1635,11 @@ public class PackageInstaller { installFlags |= PackageManager.INSTALL_APEX; } /** @hide */ public boolean getEnableRollback() { return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0; } /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); Loading
core/java/android/content/pm/PackageManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1397,6 +1397,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119; /** * Installation failed return code: one of the child sessions does not match the parent session * in respect to staged or rollback enabled parameters. * * @hide */ public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120; /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -396,6 +396,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } finally { IoUtils.closeQuietly(fis); } // After all of the sessions were loaded, they are ready to be sealed and validated for (int i = 0; i < mSessions.size(); ++i) { PackageInstallerSession session = mSessions.valueAt(i); session.sealAndValidateIfNecessary(); } } @GuardedBy("mSessions") Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +113 −30 Original line number Diff line number Diff line Loading @@ -231,6 +231,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private boolean mSealed = false; @GuardedBy("mLock") private boolean mShouldBeSealed = false; @GuardedBy("mLock") private boolean mCommitted = false; @GuardedBy("mLock") private boolean mRelinquished = false; Loading Loading @@ -430,6 +432,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { this.updatedMillis = createdMillis; this.stageDir = stageDir; this.stageCid = stageCid; this.mShouldBeSealed = sealed; if (childSessionIds != null) { for (int childSessionId : childSessionIds) { mChildSessionIds.put(childSessionId, 0); Loading @@ -450,16 +453,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionErrorCode = stagedSessionErrorCode; mStagedSessionErrorMessage = stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; if (sealed) { synchronized (mLock) { try { sealAndValidateLocked(); } catch (PackageManagerException | IOException e) { destroyInternal(); throw new IllegalArgumentException(e); } } } } public SessionInfo generateInfo() { Loading Loading @@ -932,6 +925,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull IntentSender statusReceiver, boolean forTransfer) { Preconditions.checkNotNull(statusReceiver); List<PackageInstallerSession> childSessions = getChildSessions(); final boolean wasSealed; synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); Loading Loading @@ -963,7 +958,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { wasSealed = mSealed; if (!mSealed) { try { sealAndValidateLocked(); sealAndValidateLocked(childSessions); } catch (IOException e) { throw new IllegalArgumentException(e); } catch (PackageManagerException e) { Loading Loading @@ -994,21 +989,91 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } /** Return a list of child sessions or null if the session is not multipackage * * <p> This method is handy to prevent potential deadlocks (b/123391593) */ private @Nullable List<PackageInstallerSession> getChildSessions() { List<PackageInstallerSession> childSessions = null; if (isMultiPackage()) { final int[] childSessionIds = getChildSessionIds(); childSessions = new ArrayList<>(childSessionIds.length); for (int childSessionId : childSessionIds) { childSessions.add(mSessionProvider.getSession(childSessionId)); } } return childSessions; } /** * Assert multipackage install has consistent sessions. * * @throws PackageManagerException if child sessions don't match parent session * in respect to staged and enable rollback parameters. */ @GuardedBy("mLock") private void assertMultiPackageConsistencyLocked( @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException { for (PackageInstallerSession childSession : childSessions) { // It might be that the parent session is loaded before all of it's child sessions are, // e.g. when reading sessions from XML. Those sessions will be null here, and their // conformance with the multipackage params will be checked when they're loaded. if (childSession == null) { continue; } assertConsistencyWithLocked(childSession); } } /** * Assert consistency with the given session. * * @throws PackageManagerException if other sessions doesn't match this session * in respect to staged and enable rollback parameters. */ @GuardedBy("mLock") private void assertConsistencyWithLocked(PackageInstallerSession other) throws PackageManagerException { // Session groups must be consistent wrt to isStaged parameter. Non-staging session // cannot be grouped with staging sessions. if (this.params.isStaged != other.params.isStaged) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, "Multipackage Inconsistency: session " + other.sessionId + " and session " + sessionId + " have inconsistent staged settings"); } if (this.params.getEnableRollback() != other.params.getEnableRollback()) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY, "Multipackage Inconsistency: session " + other.sessionId + " and session " + sessionId + " have inconsistent rollback settings"); } } /** * Seal the session to prevent further modification and validate the contents of it. * * <p>The session will be sealed after calling this method even if it failed. * * @param childSessions the child sessions of a multipackage that will be checked for * consistency. Can be null if session is not multipackage. * @throws PackageManagerException if the session was sealed but something went wrong. If the * session was sealed this is the only possible exception. */ @GuardedBy("mLock") private void sealAndValidateLocked() throws PackageManagerException, IOException { private void sealAndValidateLocked(List<PackageInstallerSession> childSessions) throws PackageManagerException, IOException { assertNoWriteFileTransfersOpenLocked(); assertPreparedAndNotDestroyedLocked("sealing of session"); mSealed = true; if (childSessions != null) { assertMultiPackageConsistencyLocked(childSessions); } if (params.isStaged) { final PackageInstallerSession activeSession = mStagingManager.getActiveSession(); final boolean anotherSessionAlreadyInProgress = Loading Loading @@ -1048,6 +1113,38 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } /** * If session should be sealed, then it's sealed to prevent further modification * and then it's validated. * * If the session was sealed but something went wrong then it's destroyed. * * <p> This is meant to be called after all of the sessions are loaded and added to * PackageInstallerService */ void sealAndValidateIfNecessary() { synchronized (mLock) { if (!mShouldBeSealed) { return; } } List<PackageInstallerSession> childSessions = getChildSessions(); synchronized (mLock) { try { sealAndValidateLocked(childSessions); } catch (IOException e) { throw new IllegalStateException(e); } catch (PackageManagerException e) { Slog.e(TAG, "Package not valid", e); // Session is sealed but could not be verified, we need to destroy it. destroyInternal(); // Dispatch message to remove session from PackageInstallerService dispatchSessionFinished( e.error, ExceptionUtils.getCompleteMessage(e), null); } } } /** Update the timestamp of when the staged session last changed state */ public void markUpdated() { synchronized (mLock) { Loading Loading @@ -1076,12 +1173,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new SecurityException("Can only transfer sessions that use public options"); } List<PackageInstallerSession> childSessions = getChildSessions(); synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("transfer"); try { sealAndValidateLocked(); sealAndValidateLocked(childSessions); } catch (IOException e) { throw new IllegalStateException(e); } catch (PackageManagerException e) { Loading Loading @@ -1132,14 +1231,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // outside of the lock, because reading the child // sessions with the lock held could lead to deadlock // (b/123391593). List<PackageInstallerSession> childSessions = null; if (isMultiPackage()) { final int[] childSessionIds = getChildSessionIds(); childSessions = new ArrayList<>(childSessionIds.length); for (int childSessionId : childSessionIds) { childSessions.add(mSessionProvider.getSession(childSessionId)); } } List<PackageInstallerSession> childSessions = getChildSessions(); try { synchronized (mLock) { Loading Loading @@ -1965,15 +2057,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + " does not exist"), false, true).rethrowAsRuntimeException(); } // Session groups must be consistent wrt to isStaged parameter. Non-staging session // cannot be grouped with staging sessions. if (this.params.isStaged ^ childSession.params.isStaged) { throw new RemoteException("Unable to add child.", new PackageManagerException("Child session " + childSessionId + " and parent session " + this.sessionId + " do not have consistent" + " staging session settings."), false, true); } synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("addChildSessionId"); Loading