Add device support for permission APIs in Activity

Add API overloads for requestPermissions and
shouldShowRequestPermissionRationale to support
cross device permission requests.

Bug: 283978094
Test: presubmit
Change-Id: I9fe58722aec5a733d2c154f855e9e2746aee3788
This commit is contained in:
mrulhania
2023-06-01 14:51:42 -07:00
parent e227645866
commit 3b0a4e44db
5 changed files with 170 additions and 25 deletions

View File

@@ -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<java.lang.Void,java.lang.Throwable>);
method public final void requestPermissions(@NonNull String[], int);
method public final void requestPermissions(@NonNull String[], int, int);
method public final void requestShowKeyboardShortcuts();
method @Deprecated public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
@@ -4546,6 +4548,7 @@ package android.app {
method public void setVrModeEnabled(boolean, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean shouldDockBigOverlays();
method public boolean shouldShowRequestPermissionRationale(@NonNull String);
method public boolean shouldShowRequestPermissionRationale(@NonNull String, int);
method public boolean shouldUpRecreateTask(android.content.Intent);
method public boolean showAssist(android.os.Bundle);
method @Deprecated public final void showDialog(int);

View File

@@ -3924,6 +3924,7 @@ package android.content.pm {
field public static final int DELETE_FAILED_OWNER_BLOCKED = -4; // 0xfffffffc
field public static final int DELETE_KEEP_DATA = 1; // 0x1
field public static final int DELETE_SUCCEEDED = 1; // 0x1
field public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";

View File

@@ -5445,14 +5445,14 @@ public class Activity extends ContextThemeWrapper
* the signature of the app declaring the permissions.
* </p>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
@@ -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}.
* </p>
* <p>
* 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
* <code>true</code> because in this case the activity would not receive
* result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
* result callbacks including {@link #onRequestPermissionsResult}.
* </p>
* <p>
* The <a href="https://github.com/android/platform-samples/tree/main/samples/privacy/permissions">
@@ -5483,18 +5482,89 @@ public class Activity extends ContextThemeWrapper
* request permissions at run time.
* </p>
*
* @param permissions The requested permissions. Must me non-null and not empty.
* @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(int, String[], int[])}.
* Should be >= 0.
* reported to {@link #onRequestPermissionsResult}.
* Should be >= 0.
*
* @throws IllegalArgumentException if requestCode is negative.
*
* @see #onRequestPermissionsResult(int, String[], int[])
* @see #checkSelfPermission(String)
* @see #shouldShowRequestPermissionRationale(String)
* @see #onRequestPermissionsResult
* @see #checkSelfPermission
* @see #shouldShowRequestPermissionRationale
*/
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
requestPermissions(permissions, requestCode, getDeviceId());
}
/**
* Requests permissions to be granted to this application. These permissions
* must be requested in your manifest, they should not be granted to your app,
* and they should have protection level {@link
* android.content.pm.PermissionInfo#PROTECTION_DANGEROUS dangerous}, regardless
* whether they are declared by the platform or a third-party app.
* <p>
* 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.
* </p>
* <p>
* Call {@link #shouldShowRequestPermissionRationale} before calling this API to
* check if the system recommends to show a rationale UI before asking for a permission.
* </p>
* <p>
* 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.
* </p>
* <p>
* Note that requesting a permission does not guarantee it will be granted and
* your app should be able to run without having this permission.
* </p>
* <p>
* 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}.
* </p>
* <p>
* When checking whether you have a permission you should use {@link
* #checkSelfPermission(String)}.
* </p>
* <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive
* result callbacks including {@link #onRequestPermissionsResult}.
* </p>
* <p>
* The <a href="https://github.com/android/platform-samples/tree/main/samples/privacy/permissions">
* permissions samples</a> repo demonstrates how to use this method to
* request permissions at run time.
* </p>
*
* @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}
* <p>
* <strong>Note:</strong> 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.
* </p>
*
* @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}.
* <p>
* <strong>Note:</strong> 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.
* </p>
*
* @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,

View File

@@ -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 {

View File

@@ -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.
* <p>
* <strong>Type:</strong> int
* </p>
*
* @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.
* <p>
@@ -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);