diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index bafe987cb546f..f1aa073fccf99 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -699,28 +699,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
- * Notified by the staging manager that pre-reboot verification is about to start. The
- * return value should be checked to decide whether it is OK to start pre-reboot
- * verification. In the case of a destroyed session, {@code false} is returned and there is
- * no need to start pre-reboot verification.
+ * Called when pre-reboot verification is about to start. This shouldn't be called
+ * on a destroyed session.
*/
- @Override
- public boolean notifyStartPreRebootVerification() {
+ private void notifyStartPreRebootVerification() {
synchronized (mLock) {
+ Preconditions.checkState(!mDestroyed);
if (mInPreRebootVerification) {
throw new IllegalStateException("Pre-reboot verification has started");
}
- if (mDestroyed) {
- return false;
- }
mInPreRebootVerification = true;
- return true;
}
}
/**
- * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
- * clean up the session if {@link #abandon()} has been called previously.
+ * Notified by the staging manager or PIS that pre-reboot verification has ended.
+ * Now it is safe to clean up the session if {@link #abandon()} has been called previously.
*/
@Override
public void notifyEndPreRebootVerification() {
@@ -744,6 +738,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootOrSystemLocked();
Preconditions.checkArgument(isCommitted());
Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
+ notifyStartPreRebootVerification();
verify();
}
@@ -2038,9 +2033,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (isStaged()) {
mStagedSession.setSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyVerificationComplete(mStagedSession);
+ mStagedSession.notifyEndPreRebootVerification();
} else {
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, msg, null);
@@ -2160,20 +2153,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
.write();
}
if (params.isStaged) {
- mStagingManager.commitSession(mStagedSession);
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
// though ideally, we just need to send session committed broadcast.
dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
- return;
- }
- if (isApexSession()) {
- destroyInternal();
- dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
- "APEX packages can only be installed using staged sessions.", null);
- return;
+ mStagedSession.verifySession();
+ } else {
+ verify();
}
- verify();
}
private void verify() {
@@ -2338,16 +2325,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Session not sealed");
}
- // TODO(b/136257624): Some logic in this if block probably belongs in
- // makeInstallParams().
- if (!isMultiPackage() && !isApexSession()) {
+ if (!isMultiPackage()) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
- if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ if (!isApexSession() && params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
final List fromFiles = mResolvedInheritedFiles;
final File toDir = stageDir;
@@ -2394,18 +2379,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Failed to inherit existing install", e);
}
}
- // For mode inherit existing, it would link/copy existing files to stage dir in the
- // above block. Therefore, we need to parse the complete package in stage dir here.
- // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
- // verification.
- mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
- // TODO: surface more granular state from dexopt
- mInternalProgress = 0.5f;
- computeProgressLocked(true);
+ if (!isApexSession()) {
+ // For mode inherit existing, it would link/copy existing files to stage dir in the
+ // above block. Therefore, we need to parse the complete package in stage dir here.
+ // Besides, PackageLite may be null for staged sessions that don't complete
+ // pre-reboot verification.
+ mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
- extractNativeLibraries(mPackageLite, stageDir, params.abiOverride,
- mayInheritNativeLibs());
+ // TODO: surface more granular state from dexopt
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
+
+ extractNativeLibraries(mPackageLite, stageDir, params.abiOverride,
+ mayInheritNativeLibs());
+ } else {
+ mPackageLite = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
+ }
}
final IPackageInstallObserver2 localObserver;
@@ -2452,16 +2442,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void onVerificationComplete() {
- // Staged sessions will be installed later during boot
+ // APK verification is done. Continue the installation depending on whether it is a
+ // staged session or not. For a staged session, we will hand it over to the staging
+ // manager to complete the installation.
if (isStaged()) {
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
- // TODO(b/136257624): We also need to destroy internals for verified staged session,
- // otherwise file descriptors are never closed for verified staged session until reboot
+ mStagingManager.commitSession(mStagedSession);
return;
}
+ // APEX sessions should be handled above
+ Preconditions.checkState(!isApexSession());
+
install();
}
@@ -2694,6 +2685,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
+
+ mSigningDetails = apk.getSigningDetails();
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ff042c257f6ae..040647be97a76 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17423,13 +17423,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
public void handleStartCopy() {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- // Apex packages get verified in StagingManager currently.
- // TODO(b/136257624): Move apex verification logic out of StagingManager
- mRet = INSTALL_SUCCEEDED;
- return;
- }
-
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
@@ -17441,7 +17434,10 @@ public class PackageManagerService extends IPackageManager.Stub
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!origin.existing) {
- sendApkVerificationRequest(pkgLite);
+ if ((installFlags & PackageManager.INSTALL_APEX) == 0) {
+ // TODO(b/182426975): treat APEX as APK when APK verification is concerned
+ sendApkVerificationRequest(pkgLite);
+ }
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
sendEnableRollbackRequest();
}
@@ -25894,6 +25890,11 @@ public class PackageManagerService extends IPackageManager.Stub
private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
long requiredInstalledVersionCode, int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ return verifyReplacingVersionCodeForApex(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
String packageName = pkgLite.packageName;
synchronized (mLock) {
// Package which currently owns the data that the new package will own if installed.
@@ -25940,6 +25941,40 @@ public class PackageManagerService extends IPackageManager.Stub
return PackageManager.INSTALL_SUCCEEDED;
}
+ private int verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ String packageName = pkgLite.packageName;
+
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (activePackage == null) {
+ Slog.w(TAG, "Attempting to install new APEX package " + packageName);
+ return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ final long activeVersion = activePackage.getLongVersionCode();
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activeVersion != requiredInstalledVersionCode) {
+ Slog.w(TAG, "Installed version of APEX package " + packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + requiredInstalledVersionCode);
+ return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final long newVersionCode = pkgLite.getLongVersionCode();
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+ && newVersionCode < activeVersion) {
+ Slog.w(TAG, "Downgrade of APEX package " + packageName
+ + " is not allowed. Active version: " + activeVersion
+ + " attempted: " + newVersionCode);
+ return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+ }
+
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
/**
* Check and throw if the given before/after packages would be considered a
* downgrade.
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8f87b19216491..86217351cb9d7 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -28,7 +28,6 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -80,6 +79,7 @@ import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -112,6 +112,8 @@ public class StagingManager {
@GuardedBy("mSuccessfulStagedSessionIds")
private final List mSuccessfulStagedSessionIds = new ArrayList<>();
+ private final CompletableFuture mBootCompleted = new CompletableFuture<>();
+
interface StagedSession {
boolean isMultiPackage();
boolean isApexSession();
@@ -136,7 +138,6 @@ public class StagingManager {
boolean hasParentSessionId();
long getCommittedMillis();
void abandon();
- boolean notifyStartPreRebootVerification();
void notifyEndPreRebootVerification();
void verifySession();
}
@@ -294,15 +295,6 @@ public class StagingManager {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
}
- final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (activePackage == null) {
- Slog.w(TAG, "Attempting to install new APEX package " + packageInfo.packageName);
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "It is forbidden to install new APEX packages.");
- }
- checkRequiredVersionCode(session, activePackage);
- checkDowngrade(session, activePackage, packageInfo);
result.add(packageInfo);
apexPackageNames.add(packageInfo.packageName);
}
@@ -325,40 +317,6 @@ public class StagingManager {
"Could not find rollback id for commit session: " + sessionId);
}
- private void checkRequiredVersionCode(final StagedSession session,
- final PackageInfo activePackage) throws PackageManagerException {
- if (session.sessionParams().requiredInstalledVersionCode
- == PackageManager.VERSION_CODE_HIGHEST) {
- return;
- }
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- if (activeVersion != session.sessionParams().requiredInstalledVersionCode) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Installed version of APEX package " + activePackage.packageName
- + " does not match required. Active version: " + activeVersion
- + " required: " + session.sessionParams().requiredInstalledVersionCode);
- }
- }
-
- private void checkDowngrade(final StagedSession session,
- final PackageInfo activePackage, final PackageInfo newPackage)
- throws PackageManagerException {
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- final long newVersionCode = newPackage.applicationInfo.longVersionCode;
- final boolean isAppDebuggable = (activePackage.applicationInfo.flags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
- session.sessionParams().installFlags, isAppDebuggable);
- if (activeVersion > newVersionCode && !allowsDowngrade) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Downgrade of APEX package " + newPackage.packageName
- + " is not allowed. Active version: " + activeVersion
- + " attempted: " + newVersionCode);
- }
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
boolean needsCheckpoint) {
@@ -863,7 +821,8 @@ public class StagingManager {
} else if (!session.isSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ Slog.i(TAG, "Restart verification for session=" + session.sessionId());
+ mBootCompleted.thenRun(() -> session.verifySession());
StagedSession session2 = sessions.set(j - 1, session);
sessions.set(i, session2);
j--;
@@ -1043,7 +1002,7 @@ public class StagingManager {
@VisibleForTesting
void onBootCompletedBroadcastReceived() {
- mPreRebootVerificationHandler.readyToStart();
+ mBootCompleted.complete(null);
BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
}
@@ -1084,24 +1043,7 @@ public class StagingManager {
return session;
}
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyVerificationComplete(StagedSession session) {
- mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
- }
-
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyPreRebootVerification_Apk_Complete(@NonNull StagedSession session) {
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
- }
-
private final class PreRebootVerificationHandler extends Handler {
- // Hold sessions before handler gets ready to do the verification.
- private List mPendingSessions;
- private boolean mIsReady;
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -1115,7 +1057,6 @@ public class StagingManager {
*
* - MSG_PRE_REBOOT_VERIFICATION_START
* - MSG_PRE_REBOOT_VERIFICATION_APEX
- * - MSG_PRE_REBOOT_VERIFICATION_APK
* - MSG_PRE_REBOOT_VERIFICATION_END
*
*
@@ -1123,8 +1064,7 @@ public class StagingManager {
*/
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
- private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
- private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+ private static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
@Override
public void handleMessage(Message msg) {
@@ -1144,9 +1084,6 @@ public class StagingManager {
case MSG_PRE_REBOOT_VERIFICATION_APEX:
handlePreRebootVerification_Apex(session, rollbackId);
break;
- case MSG_PRE_REBOOT_VERIFICATION_APK:
- handlePreRebootVerification_Apk(session);
- break;
case MSG_PRE_REBOOT_VERIFICATION_END:
handlePreRebootVerification_End(session);
break;
@@ -1159,35 +1096,15 @@ public class StagingManager {
}
}
- // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
- private synchronized void readyToStart() {
- mIsReady = true;
- if (mPendingSessions != null) {
- for (int i = 0; i < mPendingSessions.size(); i++) {
- StagedSession session = mPendingSessions.get(i);
- startPreRebootVerification(session);
- }
- mPendingSessions = null;
- }
- }
-
// Method for starting the pre-reboot verification
private synchronized void startPreRebootVerification(
@NonNull StagedSession session) {
- if (!mIsReady) {
- if (mPendingSessions == null) {
- mPendingSessions = new ArrayList<>();
- }
- mPendingSessions.add(session);
- return;
- }
-
- if (session.notifyStartPreRebootVerification()) {
+ mBootCompleted.thenRun(() -> {
int sessionId = session.sessionId();
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
.sendToTarget();
- }
+ });
}
private void onPreRebootVerificationFailure(StagedSession session,
@@ -1216,12 +1133,6 @@ public class StagingManager {
private void notifyPreRebootVerification_Apex_Complete(
@NonNull StagedSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId(), -1, session)
- .sendToTarget();
- }
-
- private void notifyPreRebootVerification_Apk_Complete(
- @NonNull StagedSession session) {
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
.sendToTarget();
}
@@ -1308,19 +1219,6 @@ public class StagingManager {
notifyPreRebootVerification_Apex_Complete(session);
}
- /**
- * Pre-reboot verification state for apk files. Session is sent to
- * {@link PackageManagerService} for verification and it notifies back the result via
- * {@link #notifyPreRebootVerification_Apk_Complete}
- */
- private void handlePreRebootVerification_Apk(@NonNull StagedSession session) {
- if (!session.containsApkSession()) {
- notifyPreRebootVerification_Apk_Complete(session);
- return;
- }
- session.verifySession();
- }
-
/**
* Pre-reboot verification state for wrapping up:
*
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 195cc010c9a7e..65632df648e49 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -227,8 +227,8 @@ public class StagingManagerTest {
assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
mStagingManager.onBootCompletedBroadcastReceived();
- assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
- assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+ assertThat(nonReadyApkSession.hasVerificationStarted()).isTrue();
+ assertThat(nonReadyApexSession.hasVerificationStarted()).isTrue();
}
@Test
@@ -519,7 +519,7 @@ public class StagingManagerTest {
private int mParentSessionId = -1;
private String mPackageName;
private boolean mIsAbandonded = false;
- private boolean mPreRebootVerificationStarted = false;
+ private boolean mVerificationStarted = false;
private final List mChildSessions = new ArrayList<>();
private FakeStagedSession(int sessionId) {
@@ -550,8 +550,8 @@ public class StagingManagerTest {
return mIsAbandonded;
}
- private boolean hasPreRebootVerificationStarted() {
- return mPreRebootVerificationStarted;
+ private boolean hasVerificationStarted() {
+ return mVerificationStarted;
}
private FakeStagedSession addChildSession(FakeStagedSession session) {
@@ -705,13 +705,6 @@ public class StagingManagerTest {
mIsAbandonded = true;
}
- @Override
- public boolean notifyStartPreRebootVerification() {
- mPreRebootVerificationStarted = true;
- // TODO(ioffe): change to true when tests for pre-reboot verification are added.
- return false;
- }
-
@Override
public void notifyEndPreRebootVerification() {
throw new UnsupportedOperationException();
@@ -719,7 +712,7 @@ public class StagingManagerTest {
@Override
public void verifySession() {
- throw new UnsupportedOperationException();
+ mVerificationStarted = true;
}
}
}