Merge "Making install session seal+verification non blocking."
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 41c323c..a09657b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -132,6 +132,7 @@
private static final int MSG_COMMIT = 1;
private static final int MSG_ON_PACKAGE_INSTALLED = 2;
+ private static final int MSG_SEAL = 3;
/** XML constants used for persisting a session */
static final String TAG_SESSION = "session";
@@ -332,6 +333,9 @@
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_SEAL:
+ handleSeal((IntentSender) msg.obj);
+ break;
case MSG_COMMIT:
handleCommit();
break;
@@ -848,7 +852,21 @@
"Session " + sessionId + " is a child of multi-package session "
+ mParentSessionId + " and may not be committed directly.");
}
- if (!markAsCommitted(statusReceiver, forTransfer)) {
+
+ assertCanBeCommitted(forTransfer);
+
+ if (isMultiPackage()) {
+ for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
+ final int childSessionId = mChildSessionIds.keyAt(i);
+ mSessionProvider.getSession(childSessionId).assertCanBeCommitted(forTransfer);
+ }
+ }
+
+ mHandler.obtainMessage(MSG_SEAL, statusReceiver).sendToTarget();
+ }
+
+ private void handleSeal(@NonNull IntentSender statusReceiver) {
+ if (!markAsCommitted(statusReceiver)) {
return;
}
if (isMultiPackage()) {
@@ -856,24 +874,16 @@
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
- RuntimeException commitException = null;
boolean commitFailed = false;
for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
final int childSessionId = mChildSessionIds.keyAt(i);
- try {
- // commit all children, regardless if any of them fail; we'll throw/return
- // as appropriate once all children have been processed
- if (!mSessionProvider.getSession(childSessionId)
- .markAsCommitted(childIntentSender, forTransfer)) {
- commitFailed = true;
- }
- } catch (RuntimeException e) {
- commitException = e;
+ // commit all children, regardless if any of them fail; we'll throw/return
+ // as appropriate once all children have been processed
+ if (!mSessionProvider.getSession(childSessionId)
+ .markAsCommitted(childIntentSender)) {
+ commitFailed = true;
}
}
- if (commitException != null) {
- throw commitException;
- }
if (commitFailed) {
return;
}
@@ -940,27 +950,14 @@
}
}
-
/**
- * Do everything but actually commit the session. If this was not already called, the session
- * will be sealed and marked as committed. The caller of this method is responsible for
- * subsequently submitting this session for processing.
- *
- * This method may be called multiple times to update the status receiver validate caller
- * permissions.
+ * Sanity checks to make sure it's ok to commit the session.
*/
- private boolean markAsCommitted(
- @NonNull IntentSender statusReceiver, boolean forTransfer) {
- Preconditions.checkNotNull(statusReceiver);
-
- List<PackageInstallerSession> childSessions = getChildSessions();
-
- final boolean wasSealed;
+ private void assertCanBeCommitted(boolean forTransfer) {
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotDestroyedLocked("commit");
-
- mRemoteStatusReceiver = statusReceiver;
+ assertNoWriteFileTransfersOpenLocked();
if (forTransfer) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
@@ -973,6 +970,25 @@
throw new IllegalArgumentException("Session has been transferred");
}
}
+ }
+ }
+
+ /**
+ * Do everything but actually commit the session. If this was not already called, the session
+ * will be sealed and marked as committed. The caller of this method is responsible for
+ * subsequently submitting this session for processing.
+ *
+ * This method may be called multiple times to update the status receiver validate caller
+ * permissions.
+ */
+ private boolean markAsCommitted(@NonNull IntentSender statusReceiver) {
+ Preconditions.checkNotNull(statusReceiver);
+
+ List<PackageInstallerSession> childSessions = getChildSessions();
+
+ final boolean wasSealed;
+ synchronized (mLock) {
+ mRemoteStatusReceiver = statusReceiver;
// After validations and updating the observer, we can skip re-sealing, etc. because we
// have already marked ourselves as committed.
@@ -1095,26 +1111,18 @@
assertMultiPackageConsistencyLocked(childSessions);
}
- // Read transfers from the original owner stay open, but as the session's data
- // cannot be modified anymore, there is no leak of information. For staged sessions,
- // further validation is performed by the staging manager.
+ // Read transfers from the original owner stay open, but as the session's data cannot
+ // be modified anymore, there is no leak of information. For staged sessions, further
+ // validation is performed by the staging manager.
if (!params.isMultiPackage) {
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
- try {
- if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
- validateApexInstallLocked();
- } else {
- validateApkInstallLocked(pkgInfo);
- }
- } catch (PackageManagerException e) {
- throw e;
- } catch (Throwable e) {
- // Convert all exceptions into package manager exceptions as only those are
- // handled in the code above.
- throw new PackageManagerException(e);
+ if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+ validateApexInstallLocked();
+ } else {
+ validateApkInstallLocked(pkgInfo);
}
}
@@ -1122,15 +1130,23 @@
mStagingManager.checkNonOverlappingWithStagedSessions(this);
}
} catch (PackageManagerException 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);
- throw e;
+ throw onSessionVerificationFailure(e);
+ } catch (Throwable e) {
+ // Convert all exceptions into package manager exceptions as only those are handled
+ // in the code above.
+ throw onSessionVerificationFailure(new PackageManagerException(e));
}
}
+ private PackageManagerException onSessionVerificationFailure(PackageManagerException 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);
+
+ return e;
+ }
+
/**
* If session should be sealed, then it's sealed to prevent further modification
* and then it's validated.