diff options
| author | 2020-10-06 09:15:39 +0000 | |
|---|---|---|
| committer | 2020-10-06 09:15:39 +0000 | |
| commit | b522c02e47dc3b0e5d446609e5d90fb12157b268 (patch) | |
| tree | 8eff8816093d48aee8e5ead321124d62541ebfe0 | |
| parent | dcd5a9cbb2d64ab29db398f36be312f08a51ba7e (diff) | |
| parent | 038fd8d035689910230fa4bf32618e726befa13c (diff) | |
Merge changes from topic "app-staging"
* changes:
Reverify certain conditions during staged install
Ensure staged sessions are not installed in app-staging directory
Stop creating extra apk session during post-reboot install phase
3 files changed, 200 insertions, 105 deletions
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 2aafe9a6f9a1..7efe0810946a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -28,6 +28,8 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; +import static android.content.pm.PackageManager.INSTALL_STAGED; +import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageParser.APEX_FILE_EXTENSION; import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.system.OsConstants.O_CREAT; @@ -1458,7 +1460,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mChildSessionsRemaining.removeAt(sessionIndex); if (mChildSessionsRemaining.size() == 0) { destroyInternal(); - dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, + dispatchSessionFinished(INSTALL_SUCCEEDED, "Session installed", null); } } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) { @@ -1533,7 +1535,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); - assertPreparedAndNotDestroyedLocked("commit"); + assertPreparedAndNotDestroyedLocked("commit of session " + sessionId); assertNoWriteFileTransfersOpenLocked(); final boolean isSecureFrpEnabled = @@ -1663,7 +1665,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throws PackageManagerException { try { assertNoWriteFileTransfersOpenLocked(); - assertPreparedAndNotDestroyedLocked("sealing of session"); + assertPreparedAndNotDestroyedLocked("sealing of session " + sessionId); mSealed = true; } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled @@ -1701,6 +1703,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private void onSessionInstallationFailure(int error, String detailedMessage) { + Slog.e(TAG, "Install of session " + sessionId + " failed: " + detailedMessage); + destroyInternal(); + dispatchSessionFinished(error, detailedMessage, null); + } + private void onStorageHealthStatusChanged(int status) { final String packageName = getPackageName(); if (TextUtils.isEmpty(packageName)) { @@ -1757,15 +1765,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { sealLocked(); - if (isApexSession()) { - // APEX installations rely on certain fields to be populated after reboot. - // E.g. mPackageName. - validateApexInstallLocked(); - } else { - // Populate mPackageName for this APK session which is required by the staging - // manager to check duplicate apk-in-apex. - PackageInstallerSession parent = allSessions.get(mParentSessionId); - if (parent != null && parent.isStagedSessionReady()) { + // Session that are staged, ready and not multi package will be installed during + // this boot. As such, we need populate all the fields for successful installation. + if (isMultiPackage()) { + return; + } + final PackageInstallerSession root = hasParentSessionId() + ? allSessions.get(getParentSessionId()) + : this; + if (root != null && root.isStagedSessionReady()) { + if (isApexSession()) { + validateApexInstallLocked(); + } else { validateApkInstallLocked(); } } @@ -1829,7 +1840,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagingManager.commitSession(this); // 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(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); + dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null); return; } @@ -1911,12 +1922,53 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + /** + * Installs apks of staged session while skipping the verification process for a committed and + * ready session. + */ + void installStagedSession(IntentSender statusReceiver) { + assertCallerIsOwnerOrRootOrSystemLocked(); + Preconditions.checkArgument(!hasParentSessionId()); // Don't allow installing child sessions + Preconditions.checkArgument(isCommitted() && isStagedSessionReady()); + + // Since staged sessions are installed during boot, the original reference to status + // receiver from the owner has already been lost. We can safely replace it with a + // status receiver from the system without effecting the flow. + updateRemoteStatusReceiver(statusReceiver); + install(); + } + + private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) { + synchronized (mLock) { + mRemoteStatusReceiver = remoteStatusReceiver; + if (isMultiPackage()) { + final IntentSender childIntentSender = + new ChildStatusIntentReceiver(mChildSessions.clone(), remoteStatusReceiver) + .getIntentSender(); + for (int i = mChildSessions.size() - 1; i >= 0; --i) { + mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender; + } + } + } + } + + private void install() { + try { + installNonStaged(); + } catch (PackageManagerException e) { + final String completeMsg = ExceptionUtils.getCompleteMessage(e); + onSessionInstallationFailure(e.error, completeMsg); + } + } + private void installNonStaged() throws PackageManagerException { - final PackageManagerService.InstallParams installingSession = - makeInstallParams(); + Preconditions.checkArgument(containsApkSession()); + + final PackageManagerService.InstallParams installingSession = makeInstallParams(); if (installingSession == null) { - return; + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session should contain at least one apk session for installation"); } if (isMultiPackage()) { final List<PackageInstallerSession> childSessions; @@ -2084,7 +2136,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { - if (returnCode == PackageManager.INSTALL_SUCCEEDED) { + if (returnCode == INSTALL_SUCCEEDED) { onVerificationComplete(); } else { onSessionVerificationFailure(returnCode, msg); @@ -2126,20 +2178,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - try { - installNonStaged(); - } catch (PackageManagerException e) { - final String completeMsg = ExceptionUtils.getCompleteMessage(e); - Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); - destroyInternal(); - dispatchSessionFinished(e.error, completeMsg, null); - } + install(); } /** * Stages this session for install and returns a * {@link PackageManagerService.InstallParams} representing this new staged state. */ + @Nullable private PackageManagerService.InstallParams makeInstallParams() throws PackageManagerException { synchronized (mLock) { @@ -2153,8 +2199,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - // We've reached point of no return; call into PMS to install the stage. - // Regardless of success or failure we always destroy session. + // Do not try to install apex session. Parent session will have at least one apk session. + if (!isMultiPackage() && isApexSession()) { + sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, + "Apex package should have been installed by apexd", null); + return null; + } + final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) { @@ -2164,8 +2215,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { - destroyInternal(); - dispatchSessionFinished(returnCode, msg, extras); + if (isStaged()) { + sendUpdateToRemoteStatusReceiver(returnCode, msg, extras); + } else { + // We've reached point of no return; call into PMS to install the stage. + // Regardless of success or failure we always destroy session. + destroyInternal(); + dispatchSessionFinished(returnCode, msg, extras); + } } }; @@ -2176,6 +2233,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { user = new UserHandle(userId); } + if (params.isStaged) { + params.installFlags |= INSTALL_STAGED; + } + synchronized (mLock) { return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user, mSigningDetails, mInstallerUid); @@ -2952,7 +3013,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { handle = NativeLibraryHelper.Handle.create(packageDir); final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, abiOverride, isIncrementalInstallation()); - if (res != PackageManager.INSTALL_SUCCEEDED) { + if (res != INSTALL_SUCCEEDED) { throw new PackageManagerException(res, "Failed to extract native libraries, res=" + res); } @@ -3586,16 +3647,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { - final IntentSender statusReceiver; - final String packageName; + sendUpdateToRemoteStatusReceiver(returnCode, msg, extras); + synchronized (mLock) { mFinalStatus = returnCode; mFinalMessage = msg; + } + final boolean success = (returnCode == INSTALL_SUCCEEDED); + + // Send broadcast to default launcher only if it's a new install + // TODO(b/144270665): Secure the usage of this broadcast. + final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { + mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); + } + + mCallback.onSessionFinished(this, success); + if (isDataLoaderInstallation()) { + logDataLoaderInstallationSession(returnCode); + } + } + + private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) { + final IntentSender statusReceiver; + final String packageName; + synchronized (mLock) { statusReceiver = mRemoteStatusReceiver; packageName = mPackageName; } - if (statusReceiver != null) { // Execute observer.onPackageInstalled on different thread as we don't want callers // inside the system server have to worry about catching the callbacks while they are @@ -3606,23 +3686,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { args.arg3 = extras; args.arg4 = statusReceiver; args.argi1 = returnCode; - mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget(); } - - final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); - - // Send broadcast to default launcher only if it's a new install - // TODO(b/144270665): Secure the usage of this broadcast. - final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { - mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); - } - - mCallback.onSessionFinished(this, success); - if (isDataLoaderInstallation()) { - logDataLoaderInstallationSession(returnCode); - } } /** {@hide} */ @@ -3834,7 +3899,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId, boolean showNotification, int userId, String basePackageName, int returnCode, String msg, Bundle extras) { - if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) { + if (INSTALL_SUCCEEDED == returnCode && showNotification) { boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING); Notification notification = PackageInstallerService.buildSuccessNotification(context, context.getResources() diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index cc1ef86bfb1a..8f1576516e07 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -66,6 +66,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTEN import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; +import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; @@ -14990,6 +14991,7 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable MultiPackageInstallParams mParentInstallParams; final boolean forceQueryableOverride; final int mDataLoaderType; + final long requiredInstalledVersionCode; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, InstallSource installSource, String volumeUuid, @@ -15010,6 +15012,7 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = PackageManager.INSTALL_REASON_UNKNOWN; this.forceQueryableOverride = false; this.mDataLoaderType = DataLoaderType.NONE; + this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; } InstallParams(File stagedDir, IPackageInstallObserver2 observer, @@ -15032,6 +15035,7 @@ public class PackageManagerService extends IPackageManager.Stub forceQueryableOverride = sessionParams.forceQueryableOverride; mDataLoaderType = (sessionParams.dataLoaderParams != null) ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; + requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; } @Override @@ -15178,6 +15182,18 @@ public class PackageManagerService extends IPackageManager.Stub public void handleStartCopy() { PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, origin.resolvedPath, installFlags, packageAbiOverride); + + // For staged session, there is a delay between its verification and install. Device + // state can change within this delay and hence we need to re-verify certain conditions. + boolean isStaged = (installFlags & INSTALL_STAGED) != 0; + if (isStaged) { + mRet = verifyReplacingVersionCode( + pkgLite, requiredInstalledVersionCode, installFlags); + if (mRet != INSTALL_SUCCEEDED) { + return; + } + } + mRet = overrideInstallLocation(pkgLite); } @@ -15324,11 +15340,14 @@ public class PackageManagerService extends IPackageManager.Stub PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, origin.resolvedPath, installFlags, packageAbiOverride); - mRet = verifyReplacingVersionCode(pkgLite); + mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags); + if (mRet != INSTALL_SUCCEEDED) { + return; + } // Perform package verification and enable rollback (unless we are simply moving the // package). - if (mRet == INSTALL_SUCCEEDED && !origin.existing) { + if (!origin.existing) { sendApkVerificationRequest(pkgLite); if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { sendEnableRollbackRequest(); @@ -15336,54 +15355,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private int verifyReplacingVersionCode(PackageInfoLite pkgLite) { - String packageName = pkgLite.packageName; - synchronized (mLock) { - // Package which currently owns the data that the new package will own if installed. - // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg - // will be null whereas dataOwnerPkg will contain information about the package - // which was uninstalled while keeping its data. - AndroidPackage dataOwnerPkg = mPackages.get(packageName); - if (dataOwnerPkg == null) { - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps != null) { - dataOwnerPkg = ps.pkg; - } - } - - if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { - if (dataOwnerPkg == null) { - Slog.w(TAG, "Required installed version code was " - + requiredInstalledVersionCode - + " but package is not installed"); - return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; - } - - if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { - Slog.w(TAG, "Required installed version code was " - + requiredInstalledVersionCode - + " but actual installed version is " - + dataOwnerPkg.getLongVersionCode()); - return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; - } - } - - if (dataOwnerPkg != null) { - if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, - dataOwnerPkg.isDebuggable())) { - try { - checkDowngrade(dataOwnerPkg, pkgLite); - } catch (PackageManagerException e) { - Slog.w(TAG, "Downgrade detected: " + e.getMessage()); - return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; - } - } - } - } - return PackageManager.INSTALL_SUCCEEDED; - } - - void sendApkVerificationRequest(PackageInfoLite pkgLite) { final int verificationId = mPendingVerificationToken++; @@ -15977,7 +15948,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - final File targetDir = codeFile.getParentFile(); + final File targetDir = resolveTargetDir(); final File beforeCodeFile = codeFile; final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName()); @@ -16020,6 +15991,17 @@ public class PackageManagerService extends IPackageManager.Stub return true; } + // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged + // flow, we won't need this method anymore. + private File resolveTargetDir() { + boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0; + if (isStagedInstall) { + return Environment.getDataAppDirectory(null); + } else { + return codeFile.getParentFile(); + } + } + int doPostInstall(int status, int uid) { if (status != PackageManager.INSTALL_SUCCEEDED) { cleanUp(); @@ -24197,6 +24179,54 @@ public class PackageManagerService extends IPackageManager.Stub } } + private int verifyReplacingVersionCode(PackageInfoLite pkgLite, + long requiredInstalledVersionCode, int installFlags) { + String packageName = pkgLite.packageName; + synchronized (mLock) { + // Package which currently owns the data that the new package will own if installed. + // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg + // will be null whereas dataOwnerPkg will contain information about the package + // which was uninstalled while keeping its data. + AndroidPackage dataOwnerPkg = mPackages.get(packageName); + if (dataOwnerPkg == null) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + dataOwnerPkg = ps.pkg; + } + } + + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { + if (dataOwnerPkg == null) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but package is not installed"); + return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; + } + + if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but actual installed version is " + + dataOwnerPkg.getLongVersionCode()); + return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; + } + } + + if (dataOwnerPkg != null) { + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, + dataOwnerPkg.isDebuggable())) { + try { + checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + Slog.w(TAG, "Downgrade detected: " + e.getMessage()); + 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 35c26d6cd54e..529f16af883d 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -794,18 +794,18 @@ public class StagingManager { private void installApksInSession(PackageInstallerSession session) throws PackageManagerException { - final PackageInstallerSession apksToInstall = extractApksInSession(session); - if (apksToInstall == null) { + if (!session.containsApkSession()) { return; } - if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { + if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { // If rollback is available for this session, notify the rollback // manager of the apk session so it can properly enable rollback. final RollbackManagerInternal rm = LocalServices.getService(RollbackManagerInternal.class); try { - rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId); + // TODO(b/136257624): extra apk session id in rollback is now redundant. + rm.notifyStagedApkSession(session.sessionId, session.sessionId); } catch (RuntimeException re) { Slog.e(TAG, "Failed to notifyStagedApkSession for session: " + session.sessionId, re); @@ -813,7 +813,7 @@ public class StagingManager { } final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync(); - apksToInstall.commit(receiver.getIntentSender(), false); + session.installStagedSession(receiver.getIntentSender()); final Intent result = receiver.getResult(); final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); |