Merge "Write staged sessions to /data/staging."
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 8904ee6..a236300 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -382,6 +382,11 @@
}
/** {@hide} */
+ public static File getDataStagingDirectory(String volumeUuid) {
+ return new File(getDataDirectory(volumeUuid), "staging");
+ }
+
+ /** {@hide} */
public static File getDataUserCeDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "user");
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 66eae5e..7ca39df 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -209,8 +209,7 @@
synchronized (mSessions) {
readSessionsLocked();
- reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isInstant*/);
- reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isInstant*/);
+ reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL);
final ArraySet<File> unclaimedIcons = newArraySet(
mSessionsDir.listFiles());
@@ -230,8 +229,8 @@
}
@GuardedBy("mSessions")
- private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
- final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
+ private void reconcileStagesLocked(String volumeUuid) {
+ final File stagingDir = getTmpSessionDir(volumeUuid);
final ArraySet<File> unclaimedStages = newArraySet(
stagingDir.listFiles(sStageFilter));
@@ -252,7 +251,7 @@
public void onPrivateVolumeMounted(String volumeUuid) {
synchronized (mSessions) {
- reconcileStagesLocked(volumeUuid, false /*isInstant*/);
+ reconcileStagesLocked(volumeUuid);
}
}
@@ -269,9 +268,9 @@
try {
final int sessionId = allocateSessionIdLocked();
mLegacySessions.put(sessionId, true);
- final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
- prepareStageDir(stageDir);
- return stageDir;
+ final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
+ prepareStageDir(sessionStageDir);
+ return sessionStageDir;
} catch (IllegalStateException e) {
throw new IOException(e);
}
@@ -526,9 +525,7 @@
String stageCid = null;
if (!params.isMultiPackage) {
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- final boolean isInstant =
- (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
+ stageDir = buildSessionDir(sessionId, params);
} else {
stageCid = buildExternalStageCid(sessionId);
}
@@ -634,13 +631,21 @@
throw new IllegalStateException("Failed to allocate session ID");
}
- private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
+ private File getTmpSessionDir(String volumeUuid) {
return Environment.getDataAppDirectory(volumeUuid);
}
- private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
- final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
- return new File(stagingDir, "vmdl" + sessionId + ".tmp");
+ private File buildTmpSessionDir(int sessionId, String volumeUuid) {
+ final File sessionStagingDir = getTmpSessionDir(volumeUuid);
+ return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
+ }
+
+ private File buildSessionDir(int sessionId, SessionParams params) {
+ if (params.isStaged) {
+ final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
+ return new File(sessionStagingDir, "session_" + sessionId);
+ }
+ return buildTmpSessionDir(sessionId, params.volumeUuid);
}
static void prepareStageDir(File stageDir) throws IOException {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d290f3f..795f655 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -978,18 +978,18 @@
// Read transfers from the original owner stay open, but as the session's data
// cannot be modified anymore, there is no leak of information.
- if (!params.isMultiPackage) {
+ // For staged sessions, the validation is performed by StagingManager.
+ if (!params.isMultiPackage && !params.isStaged) {
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
resolveStageDirLocked();
- // Verify that stage looks sane with respect to existing application.
- // This currently only ensures packageName, versionCode, and certificate
- // consistency.
try {
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+ // TODO(b/118865310): Remove this when APEX validation is done via
+ // StagingManager.
validateApexInstallLocked(pkgInfo);
} else {
// Verify that stage looks sane with respect to existing application.
@@ -1062,16 +1062,17 @@
@GuardedBy("mLock")
private void commitLocked()
throws PackageManagerException {
+ if (params.isStaged) {
+ mStagingManager.commitSession(this);
+ destroyInternal();
+ dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+ return;
+ }
final PackageManagerService.ActiveInstallSession committingSession =
makeSessionActiveLocked();
if (committingSession == null) {
return;
}
- if (isStaged()) {
- mStagingManager.commitSession(this);
- dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
- return;
- }
if (isMultiPackage()) {
final int[] childSessionIds = getChildSessionIds();
List<PackageManagerService.ActiveInstallSession> childSessions =
@@ -1984,7 +1985,11 @@
bridge.forceClose();
}
}
- if (stageDir != null) {
+ // For staged sessions, we don't delete the directory where the packages have been copied,
+ // since these packages are supposed to be read on reboot. StagingManager is in charge of
+ // deleting these dirs when the staged session has reached a final state.
+ // TODO(b/118865310): Implement packageDir deletion in StagingManager.
+ if (stageDir != null && !params.isStaged) {
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ac9c6ef..2e9d26a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2293,6 +2293,9 @@
break;
case "--apex":
sessionParams.installFlags |= PackageManager.INSTALL_APEX;
+ // TODO(b/118865310): APEX packages should always imply
+ // sessionParams.isStaged(). Enforce this when the staged
+ // install workflow is complete.
break;
case "--multi-package":
sessionParams.setMultiPackage();
@@ -2588,7 +2591,7 @@
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
- if (!session.isMultiPackage()) {
+ if (!session.isMultiPackage() && !session.isStaged()) {
// Sanity check that all .dm files match an apk.
// (The installer does not support standalone .dm files and will not process them.)
try {