summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java49
-rw-r--r--core/java/android/app/ContextImpl.java21
-rw-r--r--core/java/android/content/Context.java59
-rw-r--r--core/java/android/content/ContextWrapper.java6
-rw-r--r--core/java/android/permission/IPermissionManager.aidl2
-rw-r--r--core/java/android/permission/PermissionManager.java70
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java11
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java5
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java12
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt44
13 files changed, 292 insertions, 9 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 03ef669c0675..fee8cdb1ce51 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5782,8 +5782,7 @@ public class Activity extends ContextThemeWrapper
}
if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
- final int permissionCount = permissions.length;
- for (int i = 0; i < permissionCount; i++) {
+ for (int i = 0; i < permissions.length; i++) {
if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
throw new IllegalArgumentException("Cannot request renounced permission: "
+ permissions[i]);
@@ -5791,13 +5790,55 @@ public class Activity extends ContextThemeWrapper
}
}
- PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
- : createDeviceContext(deviceId).getPackageManager();
+ final Context context = getDeviceId() == deviceId ? this : createDeviceContext(deviceId);
+ if (Flags.permissionRequestShortCircuitEnabled()) {
+ int[] permissionsState = getPermissionRequestStates(context, permissions);
+ boolean hasRequestablePermission = false;
+ for (int i = 0; i < permissionsState.length; i++) {
+ if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_REQUESTABLE) {
+ hasRequestablePermission = true;
+ break;
+ }
+ }
+ // If none of the permissions is requestable, finish the request here.
+ if (!hasRequestablePermission) {
+ mHasCurrentPermissionsRequest = true;
+ Log.v(TAG, "No requestable permission in the request.");
+ int[] results = new int[permissionsState.length];
+ for (int i = 0; i < permissionsState.length; i++) {
+ if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_GRANTED) {
+ results[i] = PackageManager.PERMISSION_GRANTED;
+ } else {
+ results[i] = PackageManager.PERMISSION_DENIED;
+ }
+ }
+ // Currently permission request result is passed to the client app asynchronously
+ // in onRequestPermissionsResult, lets keep async behavior here as well.
+ mHandler.post(() -> {
+ mHasCurrentPermissionsRequest = false;
+ onRequestPermissionsResult(requestCode, permissions, results, deviceId);
+ });
+ return;
+ }
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
final Intent intent = packageManager.buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
+ @NonNull
+ private int[] getPermissionRequestStates(@NonNull Context deviceContext,
+ @NonNull String[] permissions) {
+ final int size = permissions.length;
+ int[] results = new int[size];
+ for (int i = 0; i < size; i++) {
+ results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+ }
+ return results;
+ }
+
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {@link #requestPermissions}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index dcbdc2348fbc..d8aa8b3df622 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2366,7 +2366,11 @@ class ContextImpl extends Context {
Log.v(TAG, "Treating renounced permission " + permission + " as denied");
return PERMISSION_DENIED;
}
+ int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ return PermissionManager.checkPermission(permission, pid, uid, deviceId);
+ }
+ private int resolveDeviceIdForPermissionCheck(String permission) {
// When checking a device-aware permission on a remote device, if the permission is CAMERA
// or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
// device doesn't have capability fall back to checking permission on the default device.
@@ -2387,9 +2391,9 @@ class ContextImpl extends Context {
VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
if (virtualDevice != null) {
if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
- && !virtualDevice.hasCustomAudioInputSupport())
+ && !virtualDevice.hasCustomAudioInputSupport())
|| (Objects.equals(permission, Manifest.permission.CAMERA)
- && !virtualDevice.hasCustomCameraSupport())) {
+ && !virtualDevice.hasCustomCameraSupport())) {
deviceId = Context.DEVICE_ID_DEFAULT;
}
} else {
@@ -2400,8 +2404,7 @@ class ContextImpl extends Context {
}
}
}
-
- return PermissionManager.checkPermission(permission, pid, uid, deviceId);
+ return deviceId;
}
/** @hide */
@@ -2503,6 +2506,16 @@ class ContextImpl extends Context {
message);
}
+ /** @hide */
+ @Override
+ public int getPermissionRequestState(String permission) {
+ Objects.requireNonNull(permission, "Permission name can't be null");
+ int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ PermissionManager permissionManager = getSystemService(PermissionManager.class);
+ return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
+ deviceId);
+ }
+
@Override
public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ec6a62ff639..7abf5600d659 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -788,6 +788,40 @@ public abstract class Context {
public static final int RECEIVER_NOT_EXPORTED = 0x4;
/**
+ * The permission is granted.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_REQUEST_STATE_GRANTED = 0;
+
+ /**
+ * The permission isn't granted, but apps can request the permission. When the app request
+ * the permission, user will be prompted with permission dialog to grant or deny the request.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_REQUEST_STATE_REQUESTABLE = 1;
+
+ /**
+ * The permission is denied, and shouldn't be requested by apps. Permission request
+ * will be automatically denied by the system, preventing the permission dialog from being
+ * displayed to the user.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_REQUEST_STATE_UNREQUESTABLE = 2;
+
+
+ /** @hide */
+ @IntDef(prefix = { "PERMISSION_REQUEST_STATE_" }, value = {
+ PERMISSION_REQUEST_STATE_GRANTED,
+ PERMISSION_REQUEST_STATE_REQUESTABLE,
+ PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionRequestState {}
+
+ /**
* Returns an AssetManager instance for the application's package.
* <p>
* <strong>Note:</strong> Implementations of this method should return
@@ -6989,6 +7023,31 @@ public abstract class Context {
@NonNull @PermissionName String permission, @Nullable String message);
/**
+ * Returns the permission request state for a given runtime permission. This method provides a
+ * streamlined mechanism for applications to determine whether a permission can be
+ * requested (i.e. whether the user will be prompted with a permission dialog).
+ *
+ * <p>Traditionally, determining if a permission has been permanently denied (unrequestable)
+ * required applications to initiate a permission request and subsequently analyze the result
+ * of {@link android.app.Activity#shouldShowRequestPermissionRationale} in conjunction with the
+ * grant result within the {@link android.app.Activity#onRequestPermissionsResult} callback.
+ *
+ * @param permission The name of the permission.
+ *
+ * @return The current request state of the specified permission, represented by one of the
+ * following constants: {@link PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
+ * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
+ * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}.
+ *
+ * @hide
+ */
+ @CheckResult
+ @PermissionRequestState
+ public int getPermissionRequestState(@NonNull String permission) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Grant permission to access a specific Uri to another package, regardless
* of whether that package has general permission to access the Uri's
* content provider. This can be used to grant specific, temporary
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 413eb9886392..a146807b9fcf 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1012,6 +1012,12 @@ public class ContextWrapper extends Context {
mBase.enforceCallingOrSelfPermission(permission, message);
}
+ /** @hide */
+ @Override
+ public int getPermissionRequestState(String permission) {
+ return mBase.getPermissionRequestState(permission);
+ }
+
@Override
public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
mBase.grantUriPermission(toPackage, uri, modeFlags);
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 55011e52724c..b37581260bb1 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -108,6 +108,8 @@ interface IPermissionManager {
int checkUidPermission(int uid, String permissionName, int deviceId);
Map<String, PermissionState> getAllPermissionStates(String packageName, String persistentDeviceId, int userId);
+
+ int getPermissionRequestState(String packageName, String permissionName, int deviceId);
}
/**
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 2473de4ff6d7..bdf8d23438df 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1742,6 +1742,16 @@ public final class PermissionManager {
}
}
+ private static int getPermissionRequestStateUncached(String packageName, String permission,
+ int deviceId) {
+ try {
+ return AppGlobals.getPermissionManager().getPermissionRequestState(
+ packageName, permission, deviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Identifies a permission query.
*
@@ -1795,6 +1805,46 @@ public final class PermissionManager {
}
}
+ private static final class PermissionRequestStateQuery {
+ final String mPackageName;
+ final String mPermission;
+ final int mDeviceId;
+
+ PermissionRequestStateQuery(@NonNull String packageName, @NonNull String permission,
+ int deviceId) {
+ mPackageName = packageName;
+ mPermission = permission;
+ mDeviceId = deviceId;
+ }
+
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("PermissionRequestStateQuery(package=\"%s\","
+ + " permission=\"%s\", " + "deviceId=%d)",
+ mPackageName, mPermission, mDeviceId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mPermission, mDeviceId);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object rval) {
+ if (rval == null) {
+ return false;
+ }
+ PermissionRequestStateQuery other;
+ try {
+ other = (PermissionRequestStateQuery) rval;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+ return mDeviceId == other.mDeviceId && Objects.equals(mPackageName, other.mPackageName)
+ && Objects.equals(mPermission, other.mPermission);
+ }
+ }
+
// The legacy system property "package_info" had two purposes: to invalidate PIC caches and to
// signal that package information, and therefore permissions, might have changed.
// AudioSystem is the only client of the signaling behavior. The "separate permissions
@@ -1842,10 +1892,30 @@ public final class PermissionManager {
};
/** @hide */
+ private static final PropertyInvalidatedCache<PermissionRequestStateQuery, Integer>
+ sPermissionRequestStateCache =
+ new PropertyInvalidatedCache<>(
+ 512, CACHE_KEY_PACKAGE_INFO_CACHE, "getPermissionRequestState") {
+ @Override
+ public Integer recompute(PermissionRequestStateQuery query) {
+ return getPermissionRequestStateUncached(query.mPackageName, query.mPermission,
+ query.mDeviceId);
+ }
+ };
+
+ /** @hide */
public static int checkPermission(@Nullable String permission, int pid, int uid, int deviceId) {
return sPermissionCache.query(new PermissionQuery(permission, pid, uid, deviceId));
}
+ /** @hide */
+ @Context.PermissionRequestState
+ public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
+ int deviceId) {
+ return sPermissionRequestStateCache.query(
+ new PermissionRequestStateQuery(packageName, permission, deviceId));
+ }
+
/**
* Gets the permission states for requested package and persistent device.
* <p>
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 05bc69a9f1f0..672eb4caf798 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -264,6 +264,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
persistentDeviceId, mPermissionManagerServiceImpl::checkUidPermission);
}
+ @Override
+ @Context.PermissionRequestState
+ public int getPermissionRequestState(@NonNull String packageName,
+ @NonNull String permissionName, int deviceId) {
+ Objects.requireNonNull(permissionName, "permission can't be null.");
+ Objects.requireNonNull(packageName, "package name can't be null.");
+ return mPermissionManagerServiceImpl.getPermissionRequestState(packageName, permissionName,
+ getPersistentDeviceId(deviceId));
+ }
+
private String getPersistentDeviceId(int deviceId) {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index ea71953d7cb3..ca70bddc5ac1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1014,6 +1014,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ throw new IllegalStateException("getPermissionRequestState is not supported.");
+ }
+
+ @Override
public Map<String, PermissionManager.PermissionState> getAllPermissionStates(
@NonNull String packageName, @NonNull String deviceId, int userId) {
throw new UnsupportedOperationException(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 754b141ff10d..b607832767a1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -20,6 +20,7 @@ import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -407,6 +408,16 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
int checkUidPermission(int uid, String permName, String deviceId);
/**
+ * Returns one of the permission state
+ * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
+ * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
+ * {@link Context.PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}
+ * for permission request permission flow.
+ */
+ int getPermissionRequestState(@NonNull String packageName, @NonNull String permName,
+ @NonNull String deviceId);
+
+ /**
* Gets the permission states for requested package, persistent device and user.
* <p>
* <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index c18f856594ed..ba5e97e7b113 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -247,6 +247,13 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ Log.i(LOG_TAG, "checkUidPermissionState(permName = " + permName + ", deviceId = "
+ + deviceId + ", packageName = " + packageName + ")");
+ return mService.getPermissionRequestState(packageName, permName, deviceId);
+ }
+
+ @Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
@NonNull String deviceId, int userId) {
Log.i(LOG_TAG,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 40139baf0e98..008c14db8b65 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -319,6 +319,11 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ return mNewImplementation.getPermissionRequestState(packageName, permName, deviceId);
+ }
+
+ @Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
@NonNull String deviceId, int userId) {
return mNewImplementation.getAllPermissionStates(packageName, deviceId, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index 981d3d92b15a..2a47f51da951 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -345,6 +345,18 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
}
+
+ @Override
+ public int getPermissionRequestState(String packageName, String permName, String deviceId) {
+ Trace.traceBegin(TRACE_TAG,
+ "TaggedTracingPermissionManagerServiceImpl#checkUidPermissionState");
+ try {
+ return mService.getPermissionRequestState(packageName, permName, deviceId);
+ } finally {
+ Trace.traceEnd(TRACE_TAG);
+ }
+ }
+
@Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
@NonNull String deviceId, int userId) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0b7438cd1b17..018cf20e914f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -464,6 +464,48 @@ class PermissionService(private val service: AccessCheckingService) :
return size
}
+ override fun getPermissionRequestState(
+ packageName: String,
+ permissionName: String,
+ deviceId: String
+ ): Int {
+ val uid = Binder.getCallingUid()
+ val result = context.checkPermission(permissionName, Binder.getCallingPid(), uid)
+ if (result == PackageManager.PERMISSION_GRANTED) {
+ return Context.PERMISSION_REQUEST_STATE_GRANTED
+ }
+
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(uid, userId).use {
+ it.getPackageState(packageName)
+ } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ val androidPackage = packageState.androidPackage
+ ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ if (appId != packageState.appId) {
+ return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ }
+ val permission = service.getState {
+ with(policy) { getPermissions()[permissionName] }
+ }
+ if (permission == null || !permission.isRuntime) {
+ return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ }
+ if (permissionName !in androidPackage.requestedPermissions) {
+ return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ }
+
+ val permissionFlags = service.getState {
+ getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ }
+ return if (permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)) {
+ Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+ } else {
+ Context.PERMISSION_REQUEST_STATE_REQUESTABLE
+ }
+ }
+
override fun checkUidPermission(uid: Int, permissionName: String, deviceId: String): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
@@ -472,7 +514,7 @@ class PermissionService(private val service: AccessCheckingService) :
// PackageManagerInternal.getPackage(int) already checks package visibility and enforces
// that instant apps can't see shared UIDs. Note that on the contrary,
- // Note that PackageManagerInternal.getPackage(String) doesn't perform any checks.
+ // PackageManagerInternal.getPackage(String) doesn't perform any checks.
val androidPackage = packageManagerInternal.getPackage(uid)
if (androidPackage != null) {
// Note that PackageManagerInternal.getPackageStateInternal() is not filtered.