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; } } }