From: Suprabh Shukla Date: Tue, 24 Jan 2017 02:09:03 +0000 (-0800) Subject: Adding an api for apps to check whether they can install apps X-Git-Tag: android-x86-8.1-r1~4727^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=aef2513c7157a28236d097a81fe74d7ba6b710c9;p=android-x86%2Fframeworks-base.git Adding an api for apps to check whether they can install apps Some apps may want to check whether they are trusted to install apps on the device, so they can prompt the user to go to settings and mark them as trusted before they do an intensive operation like downloading an apk. Test: cts-tradefed run cts -m CtsExternalSourcesTestCases Bug: 31002700 Change-Id: Icd9d04daa157e6733decba245ec251ce4acd4122 --- diff --git a/api/current.txt b/api/current.txt index bca65ee5e0bf..f70392328c7d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10051,6 +10051,7 @@ package android.content.pm { method public abstract boolean addPermission(android.content.pm.PermissionInfo); method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo); method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public abstract boolean canRequestPackageInstalls(); method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); @@ -33313,6 +33314,7 @@ package android.provider { field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; + field public static final java.lang.String ACTION_MANAGE_EXTERNAL_SOURCES = "android.settings.action.MANAGE_EXTERNAL_SOURCES"; field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION"; field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS"; field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; @@ -33458,7 +33460,7 @@ package android.provider { field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods"; field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy"; field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility"; - field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; + field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; field public static final java.lang.String LOCATION_MODE = "location_mode"; field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2 field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3 @@ -39203,6 +39205,7 @@ package android.test.mock { method public boolean addPermission(android.content.pm.PermissionInfo); method public boolean addPermissionAsync(android.content.pm.PermissionInfo); method public void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public boolean canRequestPackageInstalls(); method public java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public int checkPermission(java.lang.String, java.lang.String); method public int checkSignatures(java.lang.String, java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index 53552fc8cd6e..f0d972f0c0fb 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10501,6 +10501,7 @@ package android.content.pm { method public abstract boolean addPermission(android.content.pm.PermissionInfo); method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo); method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public abstract boolean canRequestPackageInstalls(); method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); @@ -36238,6 +36239,7 @@ package android.provider { field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; + field public static final java.lang.String ACTION_MANAGE_EXTERNAL_SOURCES = "android.settings.action.MANAGE_EXTERNAL_SOURCES"; field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION"; field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS"; field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; @@ -36394,7 +36396,7 @@ package android.provider { field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods"; field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy"; field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility"; - field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; + field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; field public static final java.lang.String LOCATION_MODE = "location_mode"; field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2 field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3 @@ -42492,6 +42494,7 @@ package android.test.mock { method public boolean addPermission(android.content.pm.PermissionInfo); method public boolean addPermissionAsync(android.content.pm.PermissionInfo); method public void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public boolean canRequestPackageInstalls(); method public java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public int checkPermission(java.lang.String, java.lang.String); method public int checkSignatures(java.lang.String, java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index d18af4c5b297..a6619b06b045 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -10079,6 +10079,7 @@ package android.content.pm { method public abstract boolean addPermission(android.content.pm.PermissionInfo); method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo); method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public abstract boolean canRequestPackageInstalls(); method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); @@ -33430,6 +33431,7 @@ package android.provider { field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; + field public static final java.lang.String ACTION_MANAGE_EXTERNAL_SOURCES = "android.settings.action.MANAGE_EXTERNAL_SOURCES"; field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION"; field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS"; field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; @@ -33577,7 +33579,7 @@ package android.provider { field public static final java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy"; field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility"; - field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; + field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; field public static final java.lang.String LOCATION_MODE = "location_mode"; field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2 field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3 @@ -39325,6 +39327,7 @@ package android.test.mock { method public boolean addPermission(android.content.pm.PermissionInfo); method public boolean addPermissionAsync(android.content.pm.PermissionInfo); method public void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public boolean canRequestPackageInstalls(); method public java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public int checkPermission(java.lang.String, java.lang.String); method public int checkSignatures(java.lang.String, java.lang.String); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index f790542c6847..9f2fffddde5e 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2580,4 +2580,13 @@ public class ApplicationPackageManager extends PackageManager { return false; } } + + @Override + public boolean canRequestPackageInstalls() { + try { + return mPM.canRequestPackageInstalls(mContext.getPackageName(), mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ab9af5abdb67..61531ae89b99 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -594,4 +594,6 @@ interface IPackageManager { int getInstallReason(String packageName, int userId); ParceledListSlice getSharedLibraries(int flags, int userId); + + boolean canRequestPackageInstalls(String packageName, int userId); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 98edbf817519..ab3b7e8defc7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -6050,4 +6050,21 @@ public abstract class PackageManager { @TestApi public abstract @InstallReason int getInstallReason(String packageName, @NonNull UserHandle user); + + /** + * Checks whether the calling package is allowed to request package installs through package + * installer. Apps are encouraged to call this api before launching the package installer via + * intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the + * user can explicitly choose what external sources they trust to install apps on the device. + * If this api returns false, the install request will be blocked by the package installer and + * a dialog will be shown to the user with an option to launch settings to change their + * preference. An application must target Android O or higher and declare permission + * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this api. + * + * @return true if the calling package is trusted by the user to request install packages on + * the device, false otherwise. + * @see {@link android.content.Intent#ACTION_INSTALL_PACKAGE} + * @see {@link android.provider.Settings#ACTION_MANAGE_EXTERNAL_SOURCES} + */ + public abstract boolean canRequestPackageInstalls(); } diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index a1747c7d22b9..a90b18a877ff 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -246,4 +246,25 @@ public abstract class PackageManagerInternal { * @return The SetupWizard package name. */ public abstract String getSetupWizardPackageName(); + + public interface ExternalSourcesPolicy { + + int USER_TRUSTED = 0; // User has trusted the package to install apps + int USER_BLOCKED = 1; // User has blocked the package to install apps + int USER_DEFAULT = 2; // Default code to use when user response is unavailable + + /** + * Checks the user preference for whether a package is trusted to request installs through + * package installer + * + * @param packageName The package to check for + * @param uid the uid in which the package is running + * @return {@link USER_TRUSTED} if the user has trusted the package, {@link USER_BLOCKED} + * if user has blocked requests from the package, {@link USER_DEFAULT} if the user response + * is not yet available + */ + int getPackageTrustedToInstallApps(String packageName, int uid); + } + + public abstract void setExternalSourcesPolicy(ExternalSourcesPolicy policy); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 71b9482657b6..0741348b8ba4 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -295,7 +295,6 @@ public final class Settings { * Input: Nothing. *

* Output: Nothing. - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_EXTERNAL_SOURCES = @@ -5148,6 +5147,9 @@ public final class Settings { * *

1 = permit app installation via the system package installer intent *

0 = do not allow use of the package installer + * @deprecated Starting from {@link android.os.Build.VERSION_CODES#O}, apps should use + * {@link PackageManager#canRequestPackageInstalls()} + * @see PackageManager#canRequestPackageInstalls() */ public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6d48862ea132..8f8acc1689ff 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2482,7 +2482,7 @@ > mSystemPermissions; @@ -22830,6 +22832,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public String getSetupWizardPackageName() { return mSetupWizardPackage; } + + public void setExternalSourcesPolicy(ExternalSourcesPolicy policy) { + if (policy != null) { + mExternalSourcesPolicy = policy; + } + } } @Override @@ -22919,4 +22927,38 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } return PackageManager.INSTALL_REASON_UNKNOWN; } + + @Override + public boolean canRequestPackageInstalls(String packageName, int userId) { + int callingUid = Binder.getCallingUid(); + int uid = getPackageUid(packageName, 0, userId); + if (callingUid != uid && callingUid != Process.ROOT_UID + && callingUid != Process.SYSTEM_UID) { + throw new SecurityException( + "Caller uid " + callingUid + " does not own package " + packageName); + } + ApplicationInfo info = getApplicationInfo(packageName, 0, userId); + if (info == null) { + return false; + } + if (info.targetSdkVersion < Build.VERSION_CODES.O) { + throw new UnsupportedOperationException( + "Operation only supported on apps targeting Android O or higher"); + } + String appOpPermission = Manifest.permission.REQUEST_INSTALL_PACKAGES; + String[] packagesDeclaringPermission = getAppOpPermissionPackages(appOpPermission); + if (!ArrayUtils.contains(packagesDeclaringPermission, packageName)) { + throw new SecurityException("Need to declare " + appOpPermission + " to call this api"); + } + if (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)) { + return false; + } + if (mExternalSourcesPolicy != null) { + int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid); + if (isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT) { + return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED; + } + } + return checkUidPermission(appOpPermission, uid) == PERMISSION_GRANTED; + } } diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 0c34f204a8cb..fe4e3302b836 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -220,6 +220,11 @@ public class MockPackageManager extends PackageManager { } @Override + public boolean canRequestPackageInstalls() { + throw new UnsupportedOperationException(); + } + + @Override public boolean isPermissionRevokedByPolicy(String permName, String pkgName) { throw new UnsupportedOperationException(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index d3ec9e271ba4..d325ee99aa2d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -891,4 +891,9 @@ public class BridgePackageManager extends PackageManager { public int getInstallReason(String packageName, UserHandle user) { return INSTALL_REASON_UNKNOWN; } + + @Override + public boolean canRequestPackageInstalls() { + return false; + } }