diff options
| author | 2021-03-25 20:00:53 +0000 | |
|---|---|---|
| committer | 2021-03-25 20:00:53 +0000 | |
| commit | 47c338bf1ca2960e332b208e20b4737e7a014be9 (patch) | |
| tree | bf87f03336349792f7c4b6530da672c4a34e4d1d | |
| parent | a17f3e5babc982de3b74bc3d0bfb58626b47e5f4 (diff) | |
| parent | 9458370ec9dd41181c395c3f4b7a42ba358a2629 (diff) | |
Merge "Exposes way to bypass user action on update" into sc-dev
10 files changed, 312 insertions, 95 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 945ef061bdd2..ae5ac4301948 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -168,6 +168,7 @@ package android { field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR"; field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT"; field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; + field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"; field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC"; field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT"; @@ -12356,6 +12357,7 @@ package android.content.pm { method public int getParentSessionId(); method public float getProgress(); method @Nullable public android.net.Uri getReferrerUri(); + method public int getRequireUserAction(); method public int getSessionId(); method public long getSize(); method public int getStagedSessionErrorCode(); @@ -12380,6 +12382,9 @@ package android.content.pm { field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 + field public static final int USER_ACTION_NOT_REQUIRED = 2; // 0x2 + field public static final int USER_ACTION_REQUIRED = 1; // 0x1 + field public static final int USER_ACTION_UNSPECIFIED = 0; // 0x0 } public static class PackageInstaller.SessionParams implements android.os.Parcelable { @@ -12397,6 +12402,7 @@ package android.content.pm { method public void setOriginatingUid(int); method public void setOriginatingUri(@Nullable android.net.Uri); method public void setReferrerUri(@Nullable android.net.Uri); + method public void setRequireUserAction(boolean); method public void setSize(long); method public void setWhitelistedRestrictedPermissions(@Nullable java.util.Set<java.lang.String>); method public void writeToParcel(android.os.Parcel, int); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 5afec0644920..caedd70bc06c 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1565,6 +1565,8 @@ public class PackageInstaller { public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE; /** {@hide} */ public boolean forceQueryableOverride; + /** {@hide} */ + public Boolean requireUserAction; /** * Construct parameters for a new package install session. @@ -1607,6 +1609,12 @@ public class PackageInstaller { dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel); } rollbackDataPolicy = source.readInt(); + int requireUserActionInt = source.readInt(); + requireUserAction = requireUserActionInt == 0 + ? Boolean.FALSE + : requireUserActionInt == 1 + ? Boolean.TRUE : null; + } /** {@hide} */ @@ -1635,6 +1643,7 @@ public class PackageInstaller { ret.requiredInstalledVersionCode = requiredInstalledVersionCode; ret.dataLoaderParams = dataLoaderParams; ret.rollbackDataPolicy = rollbackDataPolicy; + ret.requireUserAction = requireUserAction; return ret; } @@ -2029,6 +2038,41 @@ public class PackageInstaller { } /** + * Optionally indicate whether user action should be required when the session is + * committed. + * <p> + * Defaults to {@code true} for installers using the + * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES android.permission + * #REQUEST_INSTALL_PACKAGES} permission, and {@code false} otherwise. When {@code true}, + * installers will receive a {@link #STATUS_PENDING_USER_ACTION} callback once the + * session is committed, indicating that the user is required for the install to proceed. + * <p> + * For installers using the {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES + * android.permission.REQUEST_INSTALL_PACKAGES} permission, user action will not be + * required when the following conditions are met: + * + * <ul> + * <li>{@code requireUserAction} is set to {@code false}.</li> + * <li>The being installed targets {@link android.os.Build.VERSION_CODES#Q API 29} or + * higher.</li> + * <li>The installer is the {@link InstallSourceInfo#getInstallingPackageName() + * installer of record} of an existing version of the app (i.e.: this install session + * is an app update or the installer is updating itself).</li> + * <li>The installer declares the + * {@link android.Manifest.permission#UPDATE_PACKAGES_WITHOUT_USER_ACTION android + * .permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION} permission.</li> + * </ul> + * <p> + * Note: The target API level requirement will advance in future Android versions. + * Session owners should always be prepared to handle {@link #STATUS_PENDING_USER_ACTION} + * + * @param requireUserAction whether user action should be required. + */ + public void setRequireUserAction(boolean requireUserAction) { + this.requireUserAction = requireUserAction; + } + + /** * Sets the install scenario for this session, which describes the expected user journey. */ public void setInstallScenario(@InstallScenario int installScenario) { @@ -2058,6 +2102,7 @@ public class PackageInstaller { pw.printPair("isMultiPackage", isMultiPackage); pw.printPair("isStaged", isStaged); pw.printPair("forceQueryable", forceQueryableOverride); + pw.printPair("requireUserAction", requireUserAction); pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode); pw.printPair("dataLoaderParams", dataLoaderParams); pw.printPair("rollbackDataPolicy", rollbackDataPolicy); @@ -2099,6 +2144,10 @@ public class PackageInstaller { dest.writeParcelable(null, flags); } dest.writeInt(rollbackDataPolicy); + dest.writeInt(requireUserAction == Boolean.TRUE + ? 1 + : requireUserAction == Boolean.FALSE + ? 0 : 2); } public static final Parcelable.Creator<SessionParams> @@ -2165,6 +2214,31 @@ public class PackageInstaller { */ public static final int STAGED_SESSION_CONFLICT = 4; + /** @hide */ + @IntDef(prefix = {"USER_ACTION"}, value = { + USER_ACTION_UNSPECIFIED, + USER_ACTION_REQUIRED, + USER_ACTION_NOT_REQUIRED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UserActionRequirement {} + + /** + * The installer did not calling {@link SessionParams#setRequireUserAction(boolean)} to + * specify whether user action should be required for the install. + */ + public static final int USER_ACTION_UNSPECIFIED = 0; + /** + * The installer called {@link SessionParams#setRequireUserAction(boolean)} with + * {@code true} to require user action for the install to complete. + */ + public static final int USER_ACTION_REQUIRED = 1; + /** + * The installer called {@link SessionParams#setRequireUserAction(boolean)} with + * {@code false} to request that user action not be required for this install. + */ + public static final int USER_ACTION_NOT_REQUIRED = 2; + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int sessionId; @@ -2257,6 +2331,9 @@ public class PackageInstaller { public int rollbackDataPolicy; /** {@hide} */ + public Boolean requireUserAction; + + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public SessionInfo() { } @@ -2305,6 +2382,11 @@ public class PackageInstaller { isCommitted = source.readBoolean(); rollbackDataPolicy = source.readInt(); createdMillis = source.readLong(); + int requireUserActionInt = source.readInt(); + requireUserAction = requireUserActionInt == 0 + ? Boolean.FALSE + : requireUserActionInt == 1 + ? Boolean.TRUE : null; } /** @@ -2804,6 +2886,25 @@ public class PackageInstaller { return updatedMillis; } + /** + * Whether user action was required by the installer. + * + * <p> + * Note: a return value of {@code USER_ACTION_NOT_REQUIRED} does not guarantee that the + * install will not result in user action. + * + * @return {@link #USER_ACTION_NOT_REQUIRED}, {@link #USER_ACTION_REQUIRED} or + * {@link #USER_ACTION_UNSPECIFIED} + */ + @UserActionRequirement + public int getRequireUserAction() { + return requireUserAction == null + ? USER_ACTION_UNSPECIFIED + : requireUserAction == Boolean.TRUE + ? USER_ACTION_REQUIRED + : USER_ACTION_NOT_REQUIRED; + } + @Override public int describeContents() { return 0; @@ -2849,6 +2950,10 @@ public class PackageInstaller { dest.writeBoolean(isCommitted); dest.writeInt(rollbackDataPolicy); dest.writeLong(createdMillis); + dest.writeInt(requireUserAction == Boolean.TRUE + ? 1 + : requireUserAction == Boolean.FALSE + ? 0 : 2); } public static final Parcelable.Creator<SessionInfo> diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index a3c2cbc56652..5887047728a6 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -106,7 +106,7 @@ public class ApkLiteParseUtils { final String packagePath = packageFile.getAbsolutePath(); return input.success( new PackageLite(packagePath, baseApk.getPath(), baseApk, null, - null, null, null, null, null)); + null, null, null, null, null, baseApk.getTargetSdkVersion())); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -242,7 +242,8 @@ public class ApkLiteParseUtils { splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath(); return input.success( new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits, - usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes)); + usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes, + baseApk.getTargetSdkVersion())); } /** diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java index 803d643c985c..9172555bfcd3 100644 --- a/core/java/android/content/pm/parsing/PackageLite.java +++ b/core/java/android/content/pm/parsing/PackageLite.java @@ -55,6 +55,7 @@ public class PackageLite { /** Major and minor version number of this package */ private final int mVersionCodeMajor; private final int mVersionCode; + private final int mTargetSdk; /** Revision code of base APK */ private final int mBaseRevisionCode; /** Revision codes of any split APKs, ordered by parsed splitName */ @@ -99,7 +100,8 @@ public class PackageLite { public PackageLite(String path, String baseApkPath, ApkLite baseApk, String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames, - String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes) { + String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes, + int targetSdk) { // The following paths may be different from the path in ApkLite because we // move or rename the APK files. Use parameters to indicate the correct paths. mPath = path; @@ -125,6 +127,7 @@ public class PackageLite { mConfigForSplit = configForSplit; mSplitApkPaths = splitApkPaths; mSplitRevisionCodes = splitRevisionCodes; + mTargetSdk = targetSdk; } /** @@ -230,6 +233,11 @@ public class PackageLite { return mVersionCode; } + @DataClass.Generated.Member + public int getTargetSdk() { + return mTargetSdk; + } + /** * Revision code of base APK */ @@ -349,10 +357,10 @@ public class PackageLite { } @DataClass.Generated( - time = 1610596639255L, + time = 1615914120261L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 90755b96cba3..2ad9c91214f1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5687,6 +5687,19 @@ <permission android:name="android.permission.SET_CLIP_SOURCE" android:protectionLevel="signature|recents" /> + <!-- Allows an application to request installs that update existing packages do so without + user action via + {@link android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(boolean)}. + This permission only grants the ability to make the request and is not a guarantee that the + request will be honored. In order to execute the install, the caller must also have the + "android.permission.REQUEST_INSTALL_PACKAGES" or "android.permission.INSTALL_PACKAGES" + permissions. + <p>Protection level: normal + --> + <permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" + android:protectionLevel="normal" /> + <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index a5e28f164bc2..0a484e21b018 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -88,7 +88,6 @@ import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.permission.PermissionManagerServiceInternal; import libcore.io.IoUtils; @@ -145,7 +144,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final PackageManagerService mPm; private final ApexManager mApexManager; private final StagingManager mStagingManager; - private final PermissionManagerServiceInternal mPermissionManager; private AppOpsManager mAppOps; @@ -226,7 +224,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements Supplier<PackageParser2> apexParserSupplier) { mContext = context; mPm = pm; - mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class); mInstallThread = new HandlerThread(TAG); mInstallThread.start(); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 67638bccf7fa..2e6c57c5c01b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -46,6 +46,7 @@ import static com.android.internal.util.XmlUtils.writeUriAttribute; import static com.android.server.pm.PackageInstallerService.prepareStageDir; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -71,6 +72,7 @@ import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.IPackageInstallerSessionFileSystemConnector; import android.content.pm.IPackageLoadingProgressCallback; +import android.content.pm.InstallSourceInfo; import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; import android.content.pm.PackageInfo; @@ -92,6 +94,7 @@ import android.content.pm.parsing.result.ParseTypeImpl; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.FileBridge; import android.os.FileUtils; @@ -896,6 +899,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mInstallSource.installerPackageName, mInstallerUid); } + private static final int USER_ACTION_NOT_NEEDED = 0; + private static final int USER_ACTION_REQUIRED = 1; + private static final int USER_ACTION_PENDING_APK_PARSING = 2; + + @IntDef({USER_ACTION_NOT_NEEDED, USER_ACTION_REQUIRED, USER_ACTION_PENDING_APK_PARSING}) + @interface + UserActionRequirement {} + /** * Checks if the permissions still need to be confirmed. * @@ -904,15 +915,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * * @return {@code true} iff we need to ask to confirm the permissions? */ - private boolean needToAskForPermissions() { + @UserActionRequirement + private int computeUserActionRequirement() { final String packageName; synchronized (mLock) { if (mPermissionsManuallyAccepted) { - return false; + return USER_ACTION_NOT_NEEDED; } packageName = mPackageName; } + final boolean forcePermissionPrompt = + (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0 + || params.requireUserAction == Boolean.TRUE; + if (forcePermissionPrompt) { + return USER_ACTION_REQUIRED; + } // It is safe to access mInstallerUid and mInstallSource without lock // because they are immutable after sealing. final boolean isInstallPermissionGranted = @@ -924,19 +942,47 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isUpdatePermissionGranted = (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES, mInstallerUid) == PackageManager.PERMISSION_GRANTED); + final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission( + android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid) + == PackageManager.PERMISSION_GRANTED); final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId); + final boolean isUpdate = targetPackageUid != -1; + final InstallSourceInfo installSourceInfo = isUpdate + ? mPm.getInstallSourceInfo(packageName) + : null; + final String installerPackageName = installSourceInfo != null + ? installSourceInfo.getInstallingPackageName() + : null; + final boolean isInstallerOfRecord = isUpdate + && Objects.equals(installerPackageName, getInstallerPackageName()); + final boolean isSelfUpdate = targetPackageUid == mInstallerUid; final boolean isPermissionGranted = isInstallPermissionGranted - || (isUpdatePermissionGranted && targetPackageUid != -1) - || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid); + || (isUpdatePermissionGranted && isUpdate) + || (isSelfUpdatePermissionGranted && isSelfUpdate); final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); - final boolean forcePermissionPrompt = - (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0; // Device owners and affiliated profile owners are allowed to silently install packages, so // the permission check is waived if the installer is the device owner. - return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot - || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner()); + final boolean noUserActionNecessary = isPermissionGranted || isInstallerRoot + || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner(); + + if (noUserActionNecessary) { + return USER_ACTION_NOT_NEEDED; + } + + if (mPm.isInstallDisabledForPackage(installerPackageName, mInstallerUid, userId)) { + // show the installer to account for device poslicy or unknown sources use cases + return USER_ACTION_REQUIRED; + } + + if (params.requireUserAction == Boolean.FALSE + && isUpdateWithoutUserActionPermissionGranted + && (isInstallerOfRecord || isSelfUpdate)) { + return USER_ACTION_PENDING_APK_PARSING; + } + + return USER_ACTION_REQUIRED; } public PackageInstallerSession(PackageInstallerService.InternalCallback callback, @@ -1109,6 +1155,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { getStagedSessionErrorMessage()); info.createdMillis = createdMillis; info.updatedMillis = updatedMillis; + info.requireUserAction = params.requireUserAction; } return info; } @@ -2194,7 +2241,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void verifyNonStaged() throws PackageManagerException { final PackageManagerService.VerificationParams verifyingSession = - makeVerificationParams(); + prepareForVerification(); if (verifyingSession == null) { return; } @@ -2211,7 +2258,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final PackageInstallerSession session = childSessions.get(i); try { final PackageManagerService.VerificationParams verifyingChildSession = - session.makeVerificationParams(); + session.prepareForVerification(); if (verifyingChildSession != null) { verifyingChildSessions.add(verifyingChildSession); } @@ -2298,51 +2345,78 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * in case permissions need to be requested before verification can proceed. */ @Nullable - private PackageManagerService.VerificationParams makeVerificationParams() + private PackageManagerService.VerificationParams prepareForVerification() throws PackageManagerException { assertNotLocked("makeSessionActive"); + @UserActionRequirement + int userActionRequirement = USER_ACTION_NOT_NEEDED; // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc - if (!params.isMultiPackage && needToAskForPermissions()) { - // User needs to confirm installation; - // give installer an intent they can use to involve - // user. - final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL); - intent.setPackage(mPm.getPackageInstallerPackageName()); - intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); - - final IntentSender statusReceiver; - synchronized (mLock) { - statusReceiver = mRemoteStatusReceiver; - } - sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent); - - // Commit was keeping session marked as active until now; release - // that extra refcount so session appears idle. - closeInternal(false); - return null; + if (!params.isMultiPackage) { + userActionRequirement = computeUserActionRequirement(); + if (userActionRequirement == USER_ACTION_REQUIRED) { + sendPendingUserActionIntent(); + return null; + } // else, we'll wait until we parse to determine if we need to } synchronized (mLock) { + if (mRelinquished) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session relinquished"); + } + if (mDestroyed) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session destroyed"); + } + if (!mSealed) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session not sealed"); + } + PackageLite result = parseApkLite(); + if (result != null) { + mPackageLite = result; + mInternalProgress = 0.5f; + computeProgressLocked(true); + + extractNativeLibraries( + mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs()); + + if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING + && (result.getTargetSdk() < Build.VERSION_CODES.Q)) { + sendPendingUserActionIntent(); + return null; + } + } return makeVerificationParamsLocked(); } } - @GuardedBy("mLock") - private PackageManagerService.VerificationParams makeVerificationParamsLocked() - throws PackageManagerException { - if (mRelinquished) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Session relinquished"); - } - if (mDestroyed) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Session destroyed"); - } - if (!mSealed) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Session not sealed"); + private void sendPendingUserActionIntent() { + // User needs to confirm installation; + // give installer an intent they can use to involve + // user. + final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL); + intent.setPackage(mPm.getPackageInstallerPackageName()); + intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); + + final IntentSender statusReceiver; + synchronized (mLock) { + statusReceiver = mRemoteStatusReceiver; } + sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent); + + // Commit was keeping session marked as active until now; release + // that extra refcount so session appears idle. + closeInternal(false); + } + + /** + * Prepares staged directory with any inherited APKs and returns the parsed package. + */ + @Nullable + private PackageLite parseApkLite() throws PackageManagerException { + // TODO(b/136257624): Some logic in this if block probably belongs in // makeInstallParams(). @@ -2351,8 +2425,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Objects.requireNonNull(mSigningDetails); Objects.requireNonNull(mResolvedBaseFile); - // Inherit any packages and native libraries from existing install that - // haven't been overridden. + // If we haven't already parsed, inherit any packages and native libraries from existing + // install that haven't been overridden. if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { try { final List<File> fromFiles = mResolvedInheritedFiles; @@ -2404,16 +2478,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // 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); - - extractNativeLibraries(mPackageLite, stageDir, params.abiOverride, - mayInheritNativeLibs()); + return getOrParsePackageLiteLocked(stageDir, /* flags */ 0); } + return null; + } + @GuardedBy("mLock") + @Nullable + /** + * Returns a {@link com.android.server.pm.PackageManagerService.VerificationParams} + */ + private PackageManagerService.VerificationParams makeVerificationParamsLocked() { final IPackageInstallObserver2 localObserver; if (!hasParentSessionId()) { // Avoid attaching this observer to child session since they won't use it. @@ -2713,9 +2788,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * <p> * Note that upgrade compatibility is still performed by * {@link PackageManagerService}. + * @return a {@link PackageLite} representation of the validated APK(s). */ @GuardedBy("mLock") - private void validateApkInstallLocked() throws PackageManagerException { + private PackageLite validateApkInstallLocked() throws PackageManagerException { ApkLite baseApk = null; PackageLite packageLite = null; mPackageLite = null; @@ -2737,6 +2813,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package"); } + // Default to require only if existing base apk has fs-verity. mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled() && params.mode == SessionParams.MODE_INHERIT_EXISTING @@ -3023,6 +3100,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mIncrementalFileStorages.disallowReadLogs(); } } + return packageLite; } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 764fa0218023..ed4be19fb8dc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8481,7 +8481,8 @@ public class PackageManagerService extends IPackageManager.Stub int flags, int userId) { if (!mUserManager.exists(userId)) return null; Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0"); - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { + final int callingUid = Binder.getCallingUid(); + if (getInstantAppPackageName(callingUid) != null) { return null; } @@ -8492,8 +8493,7 @@ public class PackageManagerService extends IPackageManager.Stub == PERMISSION_GRANTED || mContext.checkCallingOrSelfPermission(DELETE_PACKAGES) == PERMISSION_GRANTED - || canRequestPackageInstallsInternal(packageName, - PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId, + || canRequestPackageInstallsInternal(packageName, callingUid, userId, false /* throwIfPermNotDeclared*/) || mContext.checkCallingOrSelfPermission(REQUEST_DELETE_PACKAGES) == PERMISSION_GRANTED @@ -27514,51 +27514,60 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean canRequestPackageInstalls(String packageName, int userId) { - return canRequestPackageInstallsInternal(packageName, 0, userId, + return canRequestPackageInstallsInternal(packageName, Binder.getCallingUid(), userId, true /* throwIfPermNotDeclared*/); } - private boolean canRequestPackageInstallsInternal(String packageName, int flags, int userId, - boolean throwIfPermNotDeclared) { - int callingUid = Binder.getCallingUid(); - int uid = getPackageUid(packageName, 0, userId); + private boolean canRequestPackageInstallsInternal(String packageName, int callingUid, + int userId, boolean throwIfPermNotDeclared) { + int uid = getPackageUidInternal(packageName, 0, userId, callingUid); if (callingUid != uid && callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID) { throw new SecurityException( "Caller uid " + callingUid + " does not own package " + packageName); } - if (isInstantApp(packageName, userId)) { + if (isInstantAppInternal(packageName, userId, callingUid)) { return false; } + final AndroidPackage pkg; synchronized (mLock) { - final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { - return false; - } - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) { + pkg = mPackages.get(packageName); + } + if (pkg == null) { + return false; + } + if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) { + return false; + } + if (!pkg.getRequestedPermissions().contains( + android.Manifest.permission.REQUEST_INSTALL_PACKAGES)) { + final String message = "Need to declare " + + android.Manifest.permission.REQUEST_INSTALL_PACKAGES + + " to call this api"; + if (throwIfPermNotDeclared) { + throw new SecurityException(message); + } else { + Slog.e(TAG, message); return false; } - if (!pkg.getRequestedPermissions().contains( - android.Manifest.permission.REQUEST_INSTALL_PACKAGES)) { - final String message = "Need to declare " - + android.Manifest.permission.REQUEST_INSTALL_PACKAGES - + " to call this api"; - if (throwIfPermNotDeclared) { - throw new SecurityException(message); - } else { - Slog.e(TAG, message); - return false; - } - } } + + return !isInstallDisabledForPackage(packageName, uid, userId); + } + + /** + * Returns true if the system or user is explicitly preventing an otherwise valid installer to + * complete an install. This includes checks like unknown sources and user restrictions. + */ + public boolean isInstallDisabledForPackage(String packageName, int uid, int userId) { if (mUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId) - || mUserManager.hasUserRestriction( - UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) { - return false; + || mUserManager.hasUserRestriction( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) { + return true; } if (mExternalSourcesPolicy != null) { int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid); - return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED; + return isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED; } return false; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 0ddb6cd50d4a..853b6a7c946a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -561,7 +561,7 @@ class PackageManagerShellCommand extends ShellCommand { } final ApkLite apkLite = apkLiteResult.getResult(); final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite, null, - null, null, null, null, null); + null, null, null, null, null, apkLite.getTargetSdkVersion()); sessionSize += PackageHelper.calculateInstalledSize(pkgLite, params.sessionParams.abiOverride, fd.getFileDescriptor()); } catch (IOException e) { diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java index a1b2f38af473..f9b8f26f1c86 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java @@ -55,8 +55,8 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Files; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Collection; import java.util.Map; import java.util.zip.ZipEntry; @@ -413,7 +413,7 @@ public class DexMetadataHelperTest { } final ApkLite baseApk = result.getResult(); final PackageLite pkgLite = new PackageLite(null, baseApk.getPath(), baseApk, null, - null, null, null, null, null); + null, null, null, null, null, baseApk.getTargetSdkVersion()); Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite)); } |