diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | api/system-current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/app/Activity.java | 25 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 10 | ||||
| -rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 3 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageManager.java | 13 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 40 | ||||
| -rw-r--r-- | test-runner/src/android/test/mock/MockPackageManager.java | 6 |
8 files changed, 99 insertions, 0 deletions
diff --git a/api/current.txt b/api/current.txt index aa0f946d654d..3be2c1d1b91e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3513,6 +3513,7 @@ package android.app { method public deprecated void setTitleColor(int); method public void setVisible(boolean); method public final void setVolumeControlStream(int); + method public boolean shouldShowRequestPermissionRationale(java.lang.String); method public boolean shouldUpRecreateTask(android.content.Intent); method public final deprecated void showDialog(int); method public final deprecated boolean showDialog(int, android.os.Bundle); diff --git a/api/system-current.txt b/api/system-current.txt index d0c274a48e34..da693d86e297 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3598,6 +3598,7 @@ package android.app { method public deprecated void setTitleColor(int); method public void setVisible(boolean); method public final void setVolumeControlStream(int); + method public boolean shouldShowRequestPermissionRationale(java.lang.String); method public boolean shouldUpRecreateTask(android.content.Intent); method public final deprecated void showDialog(int); method public final deprecated boolean showDialog(int, android.os.Bundle); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 49f509974fcd..a8eaaae16ad2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3746,6 +3746,7 @@ public class Activity extends ContextThemeWrapper * * @see #onRequestPermissionsResult(int, String[], int[]) * @see #checkSelfPermission(String) + * @see #canShowRequestPermissionRationale(String) */ public final void requestPermissions(@NonNull String[] permissions, int requestCode) { Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); @@ -3770,6 +3771,30 @@ public class Activity extends ContextThemeWrapper } /** + * Gets whether you should show UI with rationale for requesting a permission. + * You should do this only if you do not have the permission and the context in + * which the permission is requested does not clearly communicate to the user + * what would be the benefit from granting this permission. + * <p> + * For example, if you write a camera app, requesting the camera permission + * would be expected by the user and no rationale for why it is requested is + * needed. If however, the app needs location for tagging photos then a non-tech + * savvy user may wonder how location is related to taking photos. In this case + * you may choose to show UI with rationale of requesting this permission. + * </p> + * + * @param permission A permission your app wants to request. + * @return Whether you can show permission rationale UI. + * + * @see #checkSelfPermission(String) + * @see #requestPermissions(String[], int) + * @see #onRequestPermissionsResult(int, String[], int[]) + */ + public boolean shouldShowRequestPermissionRationale(String permission) { + return getPackageManager().shouldShowRequestPermissionRationale(permission); + } + + /** * Same as calling {@link #startActivityForResult(Intent, int, Bundle)} * with no options. * diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 04f643074592..41e3db834f48 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -486,6 +486,16 @@ final class ApplicationPackageManager extends PackageManager { } @Override + public boolean shouldShowRequestPermissionRationale(String permission) { + try { + return mPM.shouldShowRequestPermissionRationale(permission, + mContext.getPackageName(), mContext.getUserId()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override public int checkSignatures(String pkg1, String pkg2) { try { return mPM.checkSignatures(pkg1, pkg2); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ddff7821cdcb..00b8c71fde32 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -105,6 +105,9 @@ interface IPackageManager { void updatePermissionFlags(String permissionName, String packageName, int flagMask, int flagValues, int userId); + boolean shouldShowRequestPermissionRationale(String permissionName, + String packageName, int userId); + boolean isProtectedBroadcast(String actionName); int checkSignatures(String pkg1, String pkg2); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2ca030635ef5..45245e4af95d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2539,6 +2539,19 @@ public abstract class PackageManager { @NonNull UserHandle user); /** + * Gets whether you should show UI with rationale for requesting a permission. + * You should do this only if you do not have the permission and the context in + * which the permission is requested does not clearly communicate to the user + * what would be the benefit from grating this permission. + * + * @param permission A permission your app wants to request. + * @return Whether you can show permission rationale UI. + * + * @hide + */ + public abstract boolean shouldShowRequestPermissionRationale(String permission); + + /** * Returns an {@link android.content.Intent} suitable for passing to * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)} * which prompts the user to grant permissions to this application. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9b619c79749d..47e39631cfca 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3360,6 +3360,46 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public boolean shouldShowRequestPermissionRationale(String permissionName, + String packageName, int userId) { + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "canShowRequestPermissionRationale for user " + userId); + } + + final int uid = getPackageUid(packageName, userId); + if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) { + return false; + } + + if (checkPermission(permissionName, packageName, userId) + == PackageManager.PERMISSION_GRANTED) { + return false; + } + + final int flags; + + final long identity = Binder.clearCallingIdentity(); + try { + flags = getPermissionFlags(permissionName, + packageName, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + + final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED + | PackageManager.FLAG_PERMISSION_POLICY_FIXED + | PackageManager.FLAG_PERMISSION_USER_FIXED; + + if ((flags & fixedFlags) != 0) { + return false; + } + + return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; + } + + @Override public boolean isProtectedBroadcast(String actionName) { synchronized (mPackages) { return mProtectedBroadcasts.contains(actionName); diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index bf1ea4d0d4e9..3b7aa9f3fecb 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -218,6 +218,12 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public boolean shouldShowRequestPermissionRationale(String permission) { + throw new UnsupportedOperationException(); + } + @Override public int checkSignatures(String pkg1, String pkg2) { throw new UnsupportedOperationException(); |