diff options
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));          } |