Perform user restriction check before creating install session
If a user is restricted with DISALLOW_INSTALL_APPS restriction,
PackageInstallerSession throws a SecurityException causing PIA to crash.
Perform restriction checks before PIA creates an install session
Test: atest CtsPackageInstallTestCases:UserRestrictionInstallTest
Bug: 280441684
Change-Id: I9e6b9a0e56eea122b17b4e75a5496d158b15eefe
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 3f98867..229b7a7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -20,6 +20,8 @@
import android.Manifest;
import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -31,6 +33,8 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserManager;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -45,16 +49,24 @@
* it.
*/
public class InstallStart extends Activity {
- private static final String LOG_TAG = InstallStart.class.getSimpleName();
+ private static final String TAG = InstallStart.class.getSimpleName();
private static final String DOWNLOADS_AUTHORITY = "downloads";
+
+ private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = 1;
+ private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = 2;
private PackageManager mPackageManager;
+ private UserManager mUserManager;
private boolean mAbortInstall = false;
+ private final boolean mLocalLOGV = false;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackageManager = getPackageManager();
+ mUserManager = getSystemService(UserManager.class);
+
Intent intent = getIntent();
String callingPackage = getCallingPackage();
String callingAttributionTag = null;
@@ -80,8 +92,7 @@
// Uid of the source package, coming from ActivityManager
int callingUid = getLaunchedFromUid();
if (callingUid == Process.INVALID_UID) {
- // Cannot reach ActivityManager. Aborting install.
- Log.e(LOG_TAG, "Could not determine the launching uid.");
+ Log.e(TAG, "Could not determine the launching uid.");
}
// Uid of the source package, with a preference to uid from ApplicationInfo
final int originatingUid = sourceInfo != null ? sourceInfo.uid : callingUid;
@@ -104,12 +115,12 @@
&& originatingUid != Process.INVALID_UID) {
final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
if (targetSdkVersion < 0) {
- Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
+ Log.w(TAG, "Cannot get target sdk version for uid " + originatingUid);
// Invalid originating uid supplied. Abort install.
mAbortInstall = true;
} else if (targetSdkVersion >= Build.VERSION_CODES.O && !isUidRequestingPermission(
originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
- Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ Log.e(TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ Manifest.permission.REQUEST_INSTALL_PACKAGES);
mAbortInstall = true;
}
@@ -119,6 +130,8 @@
mAbortInstall = true;
}
+ checkDevicePolicyRestriction();
+
final String installerPackageNameFromIntent = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageNameFromIntent != null) {
@@ -126,7 +139,7 @@
if (!TextUtils.equals(installerPackageNameFromIntent, callingPkgName)
&& mPackageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES,
callingPkgName) != PackageManager.PERMISSION_GRANTED) {
- Log.e(LOG_TAG, "The given installer package name " + installerPackageNameFromIntent
+ Log.e(TAG, "The given installer package name " + installerPackageNameFromIntent
+ " is invalid. Remove it.");
EventLog.writeEvent(0x534e4554, "236687884", getLaunchedFromUid(),
"Invalid EXTRA_INSTALLER_PACKAGE_NAME");
@@ -270,4 +283,97 @@
int installerUid = packageInstaller.getSessionInfo(sessionId).getInstallerUid();
return (originatingUid == Process.ROOT_UID) || (originatingUid == installerUid);
}
+
+ private void checkDevicePolicyRestriction() {
+ // Check for install apps user restriction first.
+ final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
+ if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+ if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
+ mAbortInstall = true;
+ showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
+ return;
+ } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ if (mLocalLOGV) {
+ Log.i(TAG, "install not allowed by admin; showing "
+ + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+ }
+ mAbortInstall = true;
+ startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
+ return;
+ }
+
+ final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
+ final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
+ final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
+ & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
+ if (systemRestriction != 0) {
+ if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
+ mAbortInstall = true;
+ showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
+ } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ mAbortInstall = true;
+ startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ mAbortInstall = true;
+ startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
+ }
+ }
+
+ /**
+ * Replace any dialog shown by the dialog with the one for the given {@link #createDialog(int)}.
+ *
+ * @param id The dialog type to add
+ */
+ private void showDialogInner(int id) {
+ if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + id + ")");
+ DialogFragment currentDialog =
+ (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
+ if (currentDialog != null) {
+ currentDialog.dismissAllowingStateLoss();
+ }
+
+ DialogFragment newDialog = createDialog(id);
+ if (newDialog != null) {
+ getFragmentManager().beginTransaction()
+ .add(newDialog, "dialog").commitAllowingStateLoss();
+ }
+ }
+
+ /**
+ * Create a new dialog.
+ *
+ * @param id The id of the dialog (determines dialog type)
+ *
+ * @return The dialog
+ */
+ private DialogFragment createDialog(int id) {
+ if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
+ switch (id) {
+ case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
+ return PackageUtil.SimpleErrorDialog.newInstance(
+ R.string.install_apps_user_restriction_dlg_text);
+ case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
+ return PackageUtil.SimpleErrorDialog.newInstance(
+ R.string.unknown_apps_user_restriction_dlg_text);
+ }
+ return null;
+ }
+
+ private void startAdminSupportDetailsActivity(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction);
+
+ // If the given restriction is set by an admin, display information about the
+ // admin enforcing the restriction for the affected user.
+ final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
+ final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
+ if (showAdminSupportDetailsIntent != null) {
+ if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
+ startActivity(showAdminSupportDetailsIntent);
+ } else {
+ if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction);
+ }
+ }
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 7e294ee..d154156 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -26,7 +26,6 @@
import android.app.AppOpsManager;
import android.app.Dialog;
import android.app.DialogFragment;
-import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
@@ -56,7 +55,6 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
import java.io.File;
import java.util.ArrayList;
@@ -123,13 +121,11 @@
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
- private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
- private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
- private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
- private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
- private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
- private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 7;
- private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 8;
+ private static final int DLG_PACKAGE_ERROR = DLG_BASE + 1;
+ private static final int DLG_OUT_OF_SPACE = DLG_BASE + 2;
+ private static final int DLG_INSTALL_ERROR = DLG_BASE + 3;
+ private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 4;
+ private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 5;
// If unknown sources are temporary allowed
private boolean mAllowUnknownSources;
@@ -218,19 +214,13 @@
if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
switch (id) {
case DLG_PACKAGE_ERROR:
- return SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
+ return PackageUtil.SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
case DLG_OUT_OF_SPACE:
return OutOfSpaceDialog.newInstance(
mPm.getApplicationLabel(mPkgInfo.applicationInfo));
case DLG_INSTALL_ERROR:
return InstallErrorDialog.newInstance(
mPm.getApplicationLabel(mPkgInfo.applicationInfo));
- case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
- return SimpleErrorDialog.newInstance(
- R.string.install_apps_user_restriction_dlg_text);
- case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
- return SimpleErrorDialog.newInstance(
- R.string.unknown_apps_user_restriction_dlg_text);
case DLG_EXTERNAL_SOURCE_BLOCKED:
return ExternalSourcesBlockedDialog.newInstance(mOriginatingPackage);
case DLG_ANONYMOUS_SOURCE:
@@ -524,69 +514,17 @@
}
/**
- * Check if it is allowed to install the package and initiate install if allowed. If not allowed
- * show the appropriate dialog.
+ * Check if it is allowed to install the package and initiate install if allowed.
*/
private void checkIfAllowedAndInitiateInstall() {
- // Check for install apps user restriction first.
- final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
- if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
- if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
- showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
- return;
- } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- if (mLocalLOGV) {
- Log.i(TAG, "install not allowed by admin; showing "
- + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
- }
- startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
- finish();
- return;
- }
-
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
if (mLocalLOGV) Log.i(TAG, "install allowed");
initiateInstall();
} else {
- // Check for unknown sources restrictions.
- final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
- final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
- final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
- & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
- if (systemRestriction != 0) {
- if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
- showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
- } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- startAdminSupportDetailsActivity(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
- } else {
- handleUnknownSources();
- }
+ handleUnknownSources();
}
}
- private void startAdminSupportDetailsActivity(String restriction) {
- if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction);
-
- // If the given restriction is set by an admin, display information about the
- // admin enforcing the restriction for the affected user.
- final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
- final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
- if (showAdminSupportDetailsIntent != null) {
- if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
- startActivity(showAdminSupportDetailsIntent);
- } else {
- if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction);
- }
-
- finish();
- }
-
private void handleUnknownSources() {
if (mOriginatingPackage == null) {
Log.i(TAG, "No source found for package " + mPkgInfo.packageName);
@@ -762,38 +700,6 @@
}
/**
- * A simple error dialog showing a message
- */
- public static class SimpleErrorDialog extends DialogFragment {
- private static final String MESSAGE_KEY =
- SimpleErrorDialog.class.getName() + "MESSAGE_KEY";
-
- static SimpleErrorDialog newInstance(@StringRes int message) {
- SimpleErrorDialog dialog = new SimpleErrorDialog();
-
- Bundle args = new Bundle();
- args.putInt(MESSAGE_KEY, message);
- dialog.setArguments(args);
-
- return dialog;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new AlertDialog.Builder(getActivity())
- .setMessage(getArguments().getInt(MESSAGE_KEY))
- .setPositiveButton(R.string.ok, (dialog, which) -> getActivity().finish())
- .create();
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- getActivity().setResult(Activity.RESULT_CANCELED);
- getActivity().finish();
- }
- }
-
- /**
* Dialog to show when the source of apk can not be identified
*/
public static class AnonymousSourceDialog extends DialogFragment {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index 0270591..ff0e5fb 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -18,12 +18,17 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
@@ -32,6 +37,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import java.io.Closeable;
import java.io.File;
@@ -207,4 +213,36 @@
}
}
}
+
+ /**
+ * A simple error dialog showing a message
+ */
+ public static class SimpleErrorDialog extends DialogFragment {
+ private static final String MESSAGE_KEY =
+ SimpleErrorDialog.class.getName() + "MESSAGE_KEY";
+
+ static SimpleErrorDialog newInstance(@StringRes int message) {
+ SimpleErrorDialog dialog = new SimpleErrorDialog();
+
+ Bundle args = new Bundle();
+ args.putInt(MESSAGE_KEY, message);
+ dialog.setArguments(args);
+
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(getArguments().getInt(MESSAGE_KEY))
+ .setPositiveButton(R.string.ok, (dialog, which) -> getActivity().finish())
+ .create();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ }
+ }
}