diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index fb72e651cebd4..f3e4892afdff7 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -834,9 +834,12 @@ public static String flagsToString(@OpFlags int flags) { public static final int OP_ACCESS_ACCESSIBILITY = 88; /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */ public static final int OP_READ_DEVICE_IDENTIFIERS = 89; + /** @hide Read location metadata from media */ + public static final int OP_ACCESS_MEDIA_LOCATION = 90; + /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 90; + public static final int _NUM_OP = 91; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1107,6 +1110,9 @@ public static String flagsToString(@OpFlags int flags) { @TestApi @SystemApi public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; + /** @hide Read location metadata from media */ + public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location"; + /** @hide Interact with accessibility. */ @SystemApi public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; @@ -1134,6 +1140,7 @@ public static String flagsToString(@OpFlags int flags) { // Storage OP_READ_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE, + OP_ACCESS_MEDIA_LOCATION, // Location OP_COARSE_LOCATION, OP_FINE_LOCATION, @@ -1273,6 +1280,7 @@ public static String flagsToString(@OpFlags int flags) { OP_LEGACY_STORAGE, // LEGACY_STORAGE OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS + OP_ACCESS_MEDIA_LOCATION, // ACCESS_MEDIA_LOCATION }; /** @@ -1369,6 +1377,7 @@ public static String flagsToString(@OpFlags int flags) { OPSTR_LEGACY_STORAGE, OPSTR_ACCESS_ACCESSIBILITY, OPSTR_READ_DEVICE_IDENTIFIERS, + OPSTR_ACCESS_MEDIA_LOCATION, }; /** @@ -1466,6 +1475,7 @@ public static String flagsToString(@OpFlags int flags) { "LEGACY_STORAGE", "ACCESS_ACCESSIBILITY", "READ_DEVICE_IDENTIFIERS", + "ACCESS_MEDIA_LOCATION", }; /** @@ -1564,6 +1574,7 @@ public static String flagsToString(@OpFlags int flags) { null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS + Manifest.permission.ACCESS_MEDIA_LOCATION, }; /** @@ -1662,6 +1673,7 @@ public static String flagsToString(@OpFlags int flags) { null, // LEGACY_STORAGE null, // ACCESS_ACCESSIBILITY null, // READ_DEVICE_IDENTIFIERS + null, // ACCESS_MEDIA_LOCATION }; /** @@ -1759,6 +1771,7 @@ public static String flagsToString(@OpFlags int flags) { false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS + false, // ACCESS_MEDIA_LOCATION }; /** @@ -1855,6 +1868,7 @@ public static String flagsToString(@OpFlags int flags) { AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS + AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION }; /** @@ -1955,6 +1969,7 @@ public static String flagsToString(@OpFlags int flags) { false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS + false, // ACCESS_MEDIA_LOCATION }; /** diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index 6fe6e991fb1e7..95286e4bc1dbf 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -50,6 +50,19 @@ * permission model for which the user had disabled the "permission" * which is achieved by disallowing the corresponding app op. *
+ *+ * This class has two types of methods and you should be careful which + * type to call based on whether permission protected data is being + * passed to the app or you are just checking whether the app holds a + * permission. The reason is that a permission check requires checking + * the runtime permission and if it is granted checking the corresponding + * app op as for apps not supporting the runtime mode we never revoke + * permissions but disable app ops. Since there are two types of app op + * checks, one that does not leave a record an action was performed and + * another the does, one needs to call the preflight flavor of the checks + * named xxxForPreflight only if no private data is being delivered but + * a permission check is what is needed and the xxxForDataDelivery where + * the permission check is right before private data delivery. * * @hide */ @@ -63,6 +76,9 @@ public final class PermissionChecker { /** Permission result: The permission is denied because the app op is not allowed. */ public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1; + /** Constant when the PID for which we check permissions is unknown. */ + public static final int PID_UNKNOWN = -1; + /** @hide */ @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED, @@ -78,47 +94,127 @@ private PermissionChecker() { * Checks whether a given package in a UID and PID has a given permission * and whether the app op that corresponds to this permission is allowed. * + * NOTE: Use this method only for permission checks at the + * point where you will deliver the permission protected data to clients. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)} + * to determine if the app has or may have location permission (if app has only foreground + * location the grant state depends on the app's fg/gb state) and this check will not + * leave a trace that permission protected data was delivered. When you are about to + * deliver the location data to a registered listener you should use this method which + * will evaluate the permission access based on the current fg/bg state of the app and + * leave a record that the data was accessed. + * * @param context Context for accessing resources. * @param permission The permission to check. - * @param pid The process id for which to check. + * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID + * is not known. * @param uid The uid for which to check. * @param packageName The package name for which to check. If null the * the first package for the calling UID will be used. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkPermissionForPreflight(Context, String, int, int, String) */ @PermissionResult - public static int checkPermission(@NonNull Context context, @NonNull String permission, - int pid, int uid, @Nullable String packageName) { - if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) { - return PERMISSION_DENIED; - } - - AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - String op = appOpsManager.permissionToOp(permission); - if (op == null) { - return PERMISSION_GRANTED; - } - - if (packageName == null) { - String[] packageNames = context.getPackageManager().getPackagesForUid(uid); - if (packageNames == null || packageNames.length <= 0) { - return PERMISSION_DENIED; - } - packageName = packageNames[0]; - } + public static int checkPermissionForDataDelivery(@NonNull Context context, + @NonNull String permission, int pid, int uid, @Nullable String packageName) { + return checkPermissionCommon(context, permission, pid, uid, packageName, + true /*forDataDelivery*/); + } - if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid) != AppOpsManager.MODE_ALLOWED) { - return PERMISSION_DENIED_APP_OP; - } + /** + * Checks whether a given package in a UID and PID has a given permission + * and whether the app op that corresponds to this permission is allowed. + * + * NOTE: Use this method only for permission checks at the + * preflight point where you will not deliver the permission protected data + * to clients but schedule permission data delivery, apps register listeners, + * etc. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use this method to determine if the app has or may have location + * permission (if app has only foreground location the grant state depends on the app's + * fg/gb state) and this check will not leave a trace that permission protected data + * was delivered. When you are about to deliver the location data to a registered + * listener you should use {@link #checkPermissionForDataDelivery(Context, String, + * int, int, String)} which will evaluate the permission access based on the current + * fg/bg state of the app and leave a record that the data was accessed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param pid The process id for which to check. + * @param uid The uid for which to check. + * @param packageName The package name for which to check. If null the + * the first package for the calling UID will be used. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkPermissionForDataDelivery(Context, String, int, int, String) + */ + @PermissionResult + public static int checkPermissionForPreflight(@NonNull Context context, + @NonNull String permission, int pid, int uid, @Nullable String packageName) { + return checkPermissionCommon(context, permission, pid, uid, packageName, + false /*forDataDelivery*/); + } - return PERMISSION_GRANTED; + /** + * Checks whether your app has a given permission and whether the app op + * that corresponds to this permission is allowed. + * + * NOTE: Use this method only for permission checks at the + * point where you will deliver the permission protected data to clients. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use {@link #checkSelfPermissionForPreflight(Context, String)} + * to determine if the app has or may have location permission (if app has only foreground + * location the grant state depends on the app's fg/gb state) and this check will not + * leave a trace that permission protected data was delivered. When you are about to + * deliver the location data to a registered listener you should use this method + * which will evaluate the permission access based on the current fg/bg state of the + * app and leave a record that the data was accessed. + * + *
This API assumes the the {@link Binder#getCallingUid()} is the same as + * {@link Process#myUid()}. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkSelfPermissionForPreflight(Context, String) + */ + @PermissionResult + public static int checkSelfPermissionForDataDelivery(@NonNull Context context, + @NonNull String permission) { + return checkPermissionForDataDelivery(context, permission, Process.myPid(), + Process.myUid(), context.getPackageName()); } /** * Checks whether your app has a given permission and whether the app op * that corresponds to this permission is allowed. * + * NOTE: Use this method only for permission checks at the + * preflight point where you will not deliver the permission protected data + * to clients but schedule permission data delivery, apps register listeners, + * etc. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use this method to determine if the app has or may have location + * permission (if app has only foreground location the grant state depends on the + * app's fg/gb state) and this check will not leave a trace that permission protected + * data was delivered. When you are about to deliver the location data to a registered + * listener you should use this method which will evaluate the permission access based + * on the current fg/bg state of the app and leave a record that the data was accessed. + * *
This API assumes the the {@link Binder#getCallingUid()} is the same as * {@link Process#myUid()}. * @@ -126,11 +222,13 @@ public static int checkPermission(@NonNull Context context, @NonNull String perm * @param permission The permission to check. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkSelfPermissionForDataDelivery(Context, String) */ @PermissionResult - public static int checkSelfPermission(@NonNull Context context, + public static int checkSelfPermissionForPreflight(@NonNull Context context, @NonNull String permission) { - return checkPermission(context, permission, Process.myPid(), + return checkPermissionForPreflight(context, permission, Process.myPid(), Process.myUid(), context.getPackageName()); } @@ -138,20 +236,106 @@ public static int checkSelfPermission(@NonNull Context context, * Checks whether the IPC you are handling has a given permission and whether * the app op that corresponds to this permission is allowed. * + * NOTE: Use this method only for permission checks at the + * point where you will deliver the permission protected data to clients. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use {@link #checkCallingPermissionForPreflight(Context, String, String)} + * to determine if the app has or may have location permission (if app has only foreground + * location the grant state depends on the app's fg/gb state) and this check will not + * leave a trace that permission protected data was delivered. When you are about to + * deliver the location data to a registered listener you should use this method which + * will evaluate the permission access based on the current fg/bg state of the app and + * leave a record that the data was accessed. + * * @param context Context for accessing resources. * @param permission The permission to check. * @param packageName The package name making the IPC. If null the * the first package for the calling UID will be used. * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkCallingPermissionForPreflight(Context, String, String) */ @PermissionResult - public static int checkCallingPermission(@NonNull Context context, + public static int checkCallingPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, @Nullable String packageName) { if (Binder.getCallingPid() == Process.myPid()) { return PERMISSION_DENIED; } - return checkPermission(context, permission, Binder.getCallingPid(), + return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(), + Binder.getCallingUid(), packageName); + } + + /** + * Checks whether the IPC you are handling has a given permission and whether + * the app op that corresponds to this permission is allowed. + * + * NOTE: Use this method only for permission checks at the + * preflight point where you will not deliver the permission protected data + * to clients but schedule permission data delivery, apps register listeners, + * etc. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use this method to determine if the app has or may have location + * permission (if app has only foreground location the grant state depends on the app's + * fg/gb state) and this check will not leave a trace that permission protected data + * was delivered. When you are about to deliver the location data to a registered + * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context, + * String)} which will evaluate the permission access based on the current fg/bg state + * of the app and leave a record that the data was accessed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param packageName The package name making the IPC. If null the + * the first package for the calling UID will be used. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkCallingPermissionForDataDelivery(Context, String, String) + */ + @PermissionResult + public static int checkCallingPermissionForPreflight(@NonNull Context context, + @NonNull String permission, @Nullable String packageName) { + if (Binder.getCallingPid() == Process.myPid()) { + return PERMISSION_DENIED; + } + return checkPermissionForPreflight(context, permission, Binder.getCallingPid(), + Binder.getCallingUid(), packageName); + } + + /** + * Checks whether the IPC you are handling or your app has a given permission + * and whether the app op that corresponds to this permission is allowed. + * + * NOTE: Use this method only for permission checks at the + * point where you will deliver the permission protected data to clients. + * + *
For example, if an app registers a location listener it should have the location + * permission but no data is actually sent to the app at the moment of registration + * and you should use {@link #checkCallingOrSelfPermissionForPreflight(Context, String)} + * to determine if the app has or may have location permission (if app has only foreground + * location the grant state depends on the app's fg/gb state) and this check will not + * leave a trace that permission protected data was delivered. When you are about to + * deliver the location data to a registered listener you should use this method which + * will evaluate the permission access based on the current fg/bg state of the app and + * leave a record that the data was accessed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + * + * @see #checkCallingOrSelfPermissionForPreflight(Context, String) + */ + @PermissionResult + public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context, + @NonNull String permission) { + String packageName = (Binder.getCallingPid() == Process.myPid()) + ? context.getPackageName() : null; + return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(), Binder.getCallingUid(), packageName); } @@ -159,17 +343,69 @@ public static int checkCallingPermission(@NonNull Context context, * Checks whether the IPC you are handling or your app has a given permission * and whether the app op that corresponds to this permission is allowed. * + * NOTE: Use this method only for permission checks at the + * preflight point where you will not deliver the permission protected data + * to clients but schedule permission data delivery, apps register listeners, + * etc. + * + *
For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the
+ * app's fg/gb state) and this check will not leave a trace that permission protected
+ * data was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+ * String)} which will evaluate the permission access based on the current fg/bg state
+ * of the app and leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String)
*/
@PermissionResult
- public static int checkCallingOrSelfPermission(@NonNull Context context,
+ public static int checkCallingOrSelfPermissionForPreflight(@NonNull Context context,
@NonNull String permission) {
String packageName = (Binder.getCallingPid() == Process.myPid())
? context.getPackageName() : null;
- return checkPermission(context, permission, Binder.getCallingPid(),
+ return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
Binder.getCallingUid(), packageName);
}
+
+ private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
+ int pid, int uid, @Nullable String packageName, boolean forDataDelivery) {
+ if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ return PERMISSION_DENIED;
+ }
+
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ String op = appOpsManager.permissionToOp(permission);
+ if (op == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length <= 0) {
+ return PERMISSION_DENIED;
+ }
+ packageName = packageNames[0];
+ }
+
+ if (forDataDelivery) {
+ if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid)
+ != AppOpsManager.MODE_ALLOWED) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+ } else {
+ final int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_FOREGROUND) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+ }
+
+ return PERMISSION_GRANTED;
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index cb7d41bdf21a2..93bcdbffd1330 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1639,7 +1639,7 @@ public static class ZenRule implements Parcelable {
@UnsupportedAppUsage
public String name; // required for automatic
@UnsupportedAppUsage
- public int zenMode;
+ public int zenMode; // ie: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
@UnsupportedAppUsage
public Uri conditionId; // required for automatic
public Condition condition; // optional
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 70dfef574ca53..fb13c1f5dde3b 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -170,13 +170,23 @@ public StartListeningArgs(Intent intent, IRecognitionListener listener, int call
* Checks whether the caller has sufficient permissions
*
* @param listener to send the error message to in case of error
+ * @param forDataDelivery If the permission check is for delivering the sensitive data.
* @return {@code true} if the caller has enough permissions, {@code false} otherwise
*/
- private boolean checkPermissions(IRecognitionListener listener) {
+ private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery) {
if (DBG) Log.d(TAG, "checkPermissions");
- if (PermissionChecker.checkCallingOrSelfPermission(this,
- android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) {
- return true;
+ if (forDataDelivery) {
+ if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
+ android.Manifest.permission.RECORD_AUDIO)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return true;
+ }
+ } else {
+ if (PermissionChecker.checkCallingOrSelfPermissionForPreflight(this,
+ android.Manifest.permission.RECORD_AUDIO)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return true;
+ }
}
try {
Log.e(TAG, "call for recognition service without RECORD_AUDIO permissions");
@@ -342,7 +352,7 @@ public RecognitionServiceBinder(RecognitionService service) {
public void startListening(Intent recognizerIntent, IRecognitionListener listener) {
if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, true /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_START_LISTENING, service.new StartListeningArgs(
recognizerIntent, listener, Binder.getCallingUid())));
@@ -353,7 +363,7 @@ MSG_START_LISTENING, service.new StartListeningArgs(
public void stopListening(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_STOP_LISTENING, listener));
}
@@ -363,7 +373,7 @@ public void stopListening(IRecognitionListener listener) {
public void cancel(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_CANCEL, listener));
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 233f82640a203..65f784dbee832 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -205,6 +205,10 @@
targetSdk="29">