summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Patrick Baumann <patb@google.com> 2021-03-25 20:00:53 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-03-25 20:00:53 +0000
commit47c338bf1ca2960e332b208e20b4737e7a014be9 (patch)
treebf87f03336349792f7c4b6530da672c4a34e4d1d
parenta17f3e5babc982de3b74bc3d0bfb58626b47e5f4 (diff)
parent9458370ec9dd41181c395c3f4b7a42ba358a2629 (diff)
Merge "Exposes way to bypass user action on update" into sc-dev
-rw-r--r--core/api/current.txt6
-rw-r--r--core/java/android/content/pm/PackageInstaller.java105
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java5
-rw-r--r--core/java/android/content/pm/parsing/PackageLite.java14
-rw-r--r--core/res/AndroidManifest.xml13
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java186
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java69
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java4
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));
}