Merge changes from topic "bug162286562"

* changes:
  Fix #removeChildSessionId (4/n)
  Fix #removeChildSessionId (3/n)
  Fix #addChildSessionId (2/n)
  Fix #addChildSessionId (1/n)
  Ensure mutation to multiple objects is done atomically
This commit is contained in:
JW Wang
2020-07-31 02:02:06 +00:00
committed by Android (Google) Code Review

View File

@@ -157,6 +157,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@@ -262,6 +263,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private final Object mLock = new Object();
/**
* Used to detect and reject concurrent access to this session object to ensure mutation
* to multiple objects like {@link #addChildSessionId} are done atomically.
*/
private final AtomicBoolean mTransactionLock = new AtomicBoolean(false);
/** Timestamp of the last time this session changed state */
@GuardedBy("mLock")
private long updatedMillis;
@@ -3162,39 +3169,85 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
private void acquireTransactionLock() {
if (!mTransactionLock.compareAndSet(false, true)) {
throw new UnsupportedOperationException("Concurrent access not supported");
}
}
private void releaseTransactionLock() {
mTransactionLock.compareAndSet(true, false);
}
@Override
public void addChildSessionId(int childSessionId) {
final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
if (childSession == null || !childSession.canBeAddedAsChild(sessionId)) {
throw new IllegalStateException("Unable to add child session " + childSessionId
+ " as it does not exist or is in an invalid state.");
if (!params.isMultiPackage) {
throw new IllegalStateException("Single-session " + sessionId + " can't have child.");
}
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addChildSessionId");
final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
if (indexOfSession >= 0) {
return;
final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
if (childSession == null) {
throw new IllegalStateException("Unable to add child session " + childSessionId
+ " as it does not exist.");
}
if (childSession.params.isMultiPackage) {
throw new IllegalStateException("Multi-session " + childSessionId
+ " can't be a child.");
}
try {
acquireTransactionLock();
childSession.acquireTransactionLock();
if (!childSession.canBeAddedAsChild(sessionId)) {
throw new IllegalStateException("Unable to add child session " + childSessionId
+ " as it is in an invalid state.");
}
childSession.setParentSessionId(this.sessionId);
addChildSessionIdLocked(childSessionId);
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addChildSessionId");
final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
if (indexOfSession >= 0) {
return;
}
childSession.setParentSessionId(this.sessionId);
addChildSessionIdLocked(childSessionId);
}
} finally {
releaseTransactionLock();
childSession.releaseTransactionLock();
}
}
@Override
public void removeChildSessionId(int sessionId) {
final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
synchronized (mLock) {
final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
try {
acquireTransactionLock();
if (session != null) {
session.setParentSessionId(SessionInfo.INVALID_ID);
session.acquireTransactionLock();
}
if (indexOfSession < 0) {
// not added in the first place; no-op
return;
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("removeChildSessionId");
final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
if (indexOfSession < 0) {
// not added in the first place; no-op
return;
}
if (session != null) {
session.setParentSessionId(SessionInfo.INVALID_ID);
}
mChildSessionIds.removeAt(indexOfSession);
}
} finally {
releaseTransactionLock();
if (session != null) {
session.releaseTransactionLock();
}
mChildSessionIds.removeAt(indexOfSession);
}
}