diff --git a/core/api/current.txt b/core/api/current.txt
index 6aac750872583..cad9a312e6dff 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4458,6 +4458,7 @@ package android.app {
method public void onProvideAssistData(android.os.Bundle);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]);
+ method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int);
method @CallSuper protected void onRestart();
method protected void onRestoreInstanceState(@NonNull android.os.Bundle);
method public void onRestoreInstanceState(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle);
@@ -4499,6 +4500,7 @@ package android.app {
method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent);
method public void requestFullscreenMode(int, @Nullable android.os.OutcomeReceiver
- * Call {@link #shouldShowRequestPermissionRationale(String)} before calling this API to + * Call {@link #shouldShowRequestPermissionRationale} before calling this API to * check if the system recommends to show a rationale UI before asking for a permission. *
** If your app does not have the requested permissions the user will be presented * with UI for accepting them. After the user has accepted or rejected the * requested permissions you will receive a callback on {@link - * #onRequestPermissionsResult(int, String[], int[])} reporting whether the + * #onRequestPermissionsResult} reporting whether the * permissions were granted or not. *
*@@ -5464,8 +5464,7 @@ public class Activity extends ContextThemeWrapper * to grant and which to reject. Hence, you should be prepared that your activity * may be paused and resumed. Further, granting some permissions may require * a restart of you application. In such a case, the system will recreate the - * activity stack before delivering the result to {@link - * #onRequestPermissionsResult(int, String[], int[])}. + * activity stack before delivering the result to {@link #onRequestPermissionsResult}. *
*
* When checking whether you have a permission you should use {@link
@@ -5475,7 +5474,7 @@ public class Activity extends ContextThemeWrapper
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* true because in this case the activity would not receive
- * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
+ * result callbacks including {@link #onRequestPermissionsResult}.
*
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL} + * are granted at install time if requested in the manifest. Signature permissions + * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at + * install time if requested in the manifest and the signature of your app matches + * the signature of the app declaring the permissions. + *
+ *+ * Call {@link #shouldShowRequestPermissionRationale} before calling this API to + * check if the system recommends to show a rationale UI before asking for a permission. + *
+ *+ * If your app does not have the requested permissions the user will be presented + * with UI for accepting them. After the user has accepted or rejected the + * requested permissions you will receive a callback on {@link #onRequestPermissionsResult} + * reporting whether the permissions were granted or not. + *
+ *+ * Note that requesting a permission does not guarantee it will be granted and + * your app should be able to run without having this permission. + *
+ *+ * This method may start an activity allowing the user to choose which permissions + * to grant and which to reject. Hence, you should be prepared that your activity + * may be paused and resumed. Further, granting some permissions may require + * a restart of you application. In such a case, the system will recreate the + * activity stack before delivering the result to {@link #onRequestPermissionsResult}. + *
+ *+ * When checking whether you have a permission you should use {@link + * #checkSelfPermission(String)}. + *
+ *
+ * You cannot request a permission if your activity sets {@link
+ * android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
+ * true because in this case the activity would not receive
+ * result callbacks including {@link #onRequestPermissionsResult}.
+ *
+ * The + * permissions samples repo demonstrates how to use this method to + * request permissions at run time. + *
+ * + * @param permissions The requested permissions. Must be non-null and not empty. + * @param requestCode Application specific request code to match with a result + * reported to {@link #onRequestPermissionsResult}. + * Should be >= 0. + * @param deviceId The app is requesting permissions for this device. The primary/physical + * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link + * android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices} + * are assigned unique device Ids. + * + * @throws IllegalArgumentException if requestCode is negative. + * + * @see #onRequestPermissionsResult + * @see #checkSelfPermission + * @see #shouldShowRequestPermissionRationale + * @see Context#DEVICE_ID_DEFAULT + */ + public final void requestPermissions(@NonNull String[] permissions, int requestCode, + int deviceId) { if (requestCode < 0) { throw new IllegalArgumentException("requestCode should be >= 0"); } @@ -5502,7 +5572,7 @@ public class Activity extends ContextThemeWrapper if (mHasCurrentPermissionsRequest) { Log.w(TAG, "Can request only one set of permissions at a time"); // Dispatch the callback with empty arrays which means a cancellation. - onRequestPermissionsResult(requestCode, new String[0], new int[0]); + onRequestPermissionsResult(requestCode, new String[0], new int[0], deviceId); return; } @@ -5516,47 +5586,95 @@ public class Activity extends ContextThemeWrapper } } - final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); + PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager() + : createDeviceContext(deviceId).getPackageManager(); + final Intent intent = packageManager.buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); mHasCurrentPermissionsRequest = true; } /** * Callback for the result from requesting permissions. This method - * is invoked for every call on {@link #requestPermissions(String[], int)}. + * is invoked for every call on {@link #requestPermissions} ** Note: It is possible that the permissions request interaction * with the user is interrupted. In this case you will receive empty permissions * and results arrays which should be treated as a cancellation. *
* - * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}. + * @param requestCode The request code passed in {@link #requestPermissions}. * @param permissions The requested permissions. Never null. - * @param grantResults The grant results for the corresponding permissions - * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED} - * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. + * @param grantResults The grant results for the corresponding permissions which is either + * {@link android.content.pm.PackageManager#PERMISSION_GRANTED} or + * {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. * - * @see #requestPermissions(String[], int) + * @see #requestPermissions */ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { /* callback - no nothing */ } + /** + * Callback for the result from requesting permissions. This method + * is invoked for every call on {@link #requestPermissions}. + *+ * Note: It is possible that the permissions request interaction + * with the user is interrupted. In this case you will receive empty permissions + * and results arrays which should be treated as a cancellation. + *
+ * + * @param requestCode The request code passed in {@link #requestPermissions}. + * @param permissions The requested permissions. Never null. + * @param grantResults The grant results for the corresponding permissions which is either + * {@link android.content.pm.PackageManager#PERMISSION_GRANTED} or + * {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. + * @param deviceId The deviceId for which permissions were requested. The primary/physical + * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link + * android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices} + * are assigned unique device Ids. + * + * @see #requestPermissions + */ + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults, int deviceId) { + onRequestPermissionsResult(requestCode, permissions, grantResults); + } + /** * Gets whether you should show UI with rationale before requesting a permission. * * @param permission A permission your app wants to request. * @return Whether you should show permission rationale UI. * - * @see #checkSelfPermission(String) - * @see #requestPermissions(String[], int) - * @see #onRequestPermissionsResult(int, String[], int[]) + * @see #checkSelfPermission + * @see #requestPermissions + * @see #onRequestPermissionsResult */ public boolean shouldShowRequestPermissionRationale(@NonNull String permission) { return getPackageManager().shouldShowRequestPermissionRationale(permission); } + /** + * Gets whether you should show UI with rationale before requesting a permission. + * + * @param permission A permission your app wants to request. + * @param deviceId The app is requesting permissions for this device. The primary/physical + * device is assigned {@link Context#DEVICE_ID_DEFAULT}, and {@link + * android.companion.virtual.VirtualDeviceManager.VirtualDevice virtual devices} + * are assigned unique device Ids. + * @return Whether you should show permission rationale UI. + * + * @see #checkSelfPermission + * @see #requestPermissions + * @see #onRequestPermissionsResult + */ + public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) { + final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager() + : createDeviceContext(deviceId).getPackageManager(); + return packageManager.shouldShowRequestPermissionRationale(permission); + } + /** * Same as calling {@link #startActivityForResult(Intent, int, Bundle)} * with no options. @@ -9107,12 +9225,15 @@ public class Activity extends ContextThemeWrapper private void dispatchRequestPermissionsResult(int requestCode, Intent data) { mHasCurrentPermissionsRequest = false; - // If the package installer crashed we may have not data - best effort. + // If the package installer crashed we may have no data - best effort. String[] permissions = (data != null) ? data.getStringArrayExtra( PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0]; final int[] grantResults = (data != null) ? data.getIntArrayExtra( PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0]; - onRequestPermissionsResult(requestCode, permissions, grantResults); + final int deviceId = (data != null) ? data.getIntExtra( + PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, Context.DEVICE_ID_DEFAULT + ) : Context.DEVICE_ID_DEFAULT; + onRequestPermissionsResult(requestCode, permissions, grantResults, deviceId); } private void dispatchRequestPermissionsResultToFragment(int requestCode, Intent data, diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0ba56b97d510c..f4a29ed3a92e9 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -941,6 +941,13 @@ public class ApplicationPackageManager extends PackageManager { return getPermissionManager().shouldShowRequestPermissionRationale(permName); } + @Override + public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) { + Intent intent = super.buildRequestPermissionsIntent(permissions); + intent.putExtra(EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, mContext.getDeviceId()); + return intent; + } + @Override public CharSequence getBackgroundPermissionOptionLabel() { try { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index dca4544f9a143..292f51bff83a9 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4611,6 +4611,19 @@ public abstract class PackageManager { public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; + /** + * The deviceId for which the permissions are requested, {@link Context#DEVICE_ID_DEFAULT} + * is the default device ID. + *+ * Type: int + *
+ * + * @hide + */ + @SystemApi + public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = + "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; + /** * The results from the permissions request. *@@ -6592,7 +6605,7 @@ public abstract class PackageManager { @UnsupportedAppUsage public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) { if (ArrayUtils.isEmpty(permissions)) { - throw new IllegalArgumentException("permission cannot be null or empty"); + throw new IllegalArgumentException("permission cannot be null or empty"); } Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS); intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);