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/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f790542..9f2fffd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2580,4 +2580,13 @@
             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 ab9af5a..61531ae 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -594,4 +594,6 @@
     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 98edbf8..ab3b7e8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6050,4 +6050,21 @@
     @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 a1747c7..a90b18a 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -246,4 +246,25 @@
      * @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 71b9482..0741348 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -295,7 +295,6 @@
      * Input: Nothing.
      * <p>
      * Output: Nothing.
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_MANAGE_EXTERNAL_SOURCES =
@@ -5148,6 +5147,9 @@
          *
          * <p>1 = permit app installation via the system package installer intent
          * <p>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 6d48862..8f8acc1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2482,7 +2482,7 @@
     <!-- Allows an application to request installing packages. Apps
          targeting APIs greater than 25 must hold this permission in
          order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.
-         <p>Protection level: normal
+         <p>Protection level: signature
     -->
     <permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
         android:label="@string/permlab_requestInstallPackages"