diff options
| author | 2023-10-05 11:56:12 +0100 | |
|---|---|---|
| committer | 2023-11-02 18:34:40 +0000 | |
| commit | 0534fe137286a0991085c9c50846af2beb4a3ac7 (patch) | |
| tree | 9ea4c856723b8033aee8e3d652e66125a7cbda6f | |
| parent | 30c1f0029379a06996ef2949bdbed21e38b861df (diff) | |
Create MEDIA_ROUTING_CONTROL app op permission for proxy routing
The new permission allows holders of COMPANION_DEVICE_WATCH to use
MediaRouter2 to control the routing of other apps from the watch.
Users will grant the permission from a Special App Access setting.
Bug: 305919655
Bug: 192657812
Test: atest CtsMediaBetterTogetherTestCases
Change-Id: I204ddbf545c3e8952bd6bec1ef86bffadbe58cbd
| -rw-r--r-- | core/api/current.txt | 1 | ||||
| -rw-r--r-- | core/api/system-current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/app/AppOpsManager.java | 30 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 7 | ||||
| -rw-r--r-- | media/java/android/media/flags/media_better_together.aconfig | 7 | ||||
| -rw-r--r-- | packages/Shell/AndroidManifest.xml | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java | 44 |
7 files changed, 80 insertions, 11 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 7d5157457cd2..d8fd7a317ab9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -211,6 +211,7 @@ package android { field public static final String MANAGE_WIFI_NETWORK_SELECTION = "android.permission.MANAGE_WIFI_NETWORK_SELECTION"; field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR"; field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL"; + field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String MEDIA_ROUTING_CONTROL = "android.permission.MEDIA_ROUTING_CONTROL"; field public static final String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS"; field public static final String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE"; field public static final String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 7691c1e8f5f7..3309aee9a058 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -639,6 +639,7 @@ package android.app { field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels"; field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls"; + field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control"; field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone"; field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells"; field public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera"; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b03bd59c305e..a99dfa605407 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -48,6 +48,7 @@ import android.content.pm.ParceledListSlice; import android.database.DatabaseUtils; import android.health.connect.HealthConnectManager; import android.media.AudioAttributes.AttributeUsage; +import android.media.MediaRouter2; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -89,6 +90,7 @@ import com.android.internal.util.DataClass; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.Parcelling; import com.android.internal.util.Preconditions; +import com.android.media.flags.Flags; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -1506,9 +1508,15 @@ public class AppOpsManager { public static final int OP_CREATE_ACCESSIBILITY_OVERLAY = AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY; + /** + * See {@link #OPSTR_MEDIA_ROUTING_CONTROL}. + * @hide + */ + public static final int OP_MEDIA_ROUTING_CONTROL = AppProtoEnums.APP_OP_MEDIA_ROUTING_CONTROL; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 139; + public static final int _NUM_OP = 140; /** * All app ops represented as strings. @@ -1654,6 +1662,7 @@ public class AppOpsManager { OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA, OPSTR_CREATE_ACCESSIBILITY_OVERLAY, + OPSTR_MEDIA_ROUTING_CONTROL, }) public @interface AppOpString {} @@ -1981,6 +1990,19 @@ public class AppOpsManager { public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls"; /** + * Allows apps holding this permission to control the routing of other apps via {@link + * MediaRouter2}. + * + * <p>For example, holding this permission allows watches (via companion apps) to control the + * routing of applications running on the phone. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL) + public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control"; + + /** * AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage} * * <p>MediaProvider is the only component (outside of system server) that should care about this @@ -2404,7 +2426,8 @@ public class AppOpsManager { OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD, OP_USE_FULL_SCREEN_INTENT, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, - OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA + OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA, + OP_MEDIA_ROUTING_CONTROL, }; static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{ @@ -2846,6 +2869,9 @@ public class AppOpsManager { OPSTR_CREATE_ACCESSIBILITY_OVERLAY, "CREATE_ACCESSIBILITY_OVERLAY") .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), + new AppOpInfo.Builder(OP_MEDIA_ROUTING_CONTROL, OPSTR_MEDIA_ROUTING_CONTROL, + "MEDIA_ROUTING_CONTROL") + .setPermission(Manifest.permission.MEDIA_ROUTING_CONTROL).build(), }; // The number of longs needed to form a full bitmask of app ops diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c75f99616f5b..72e0fe9e6827 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5981,6 +5981,13 @@ <permission android:name="android.permission.MEDIA_CONTENT_CONTROL" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to control the routing of media apps. + <p>Only for use by role COMPANION_DEVICE_WATCH</p> + @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") + --> + <permission android:name="android.permission.MEDIA_ROUTING_CONTROL" + android:protectionLevel="signature|appop" /> + <!-- @SystemApi @hide Allows an application to set the volume key long-press listener. <p>When it's set, the application will receive the volume key long-press event instead of changing volume.</p> diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index dd1df475d95b..283d61b957d1 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -48,3 +48,10 @@ flag { description: "Enables the following type constants in MediaRoute2Info: CAR, COMPUTER, GAME_CONSOLE, SMARTPHONE, SMARTWATCH, TABLET, TABLET_DOCKED. Note that this doesn't gate any behavior. It only guards some API int symbols." bug: "301713440" } + +flag { + name: "enable_privileged_routing_for_media_routing_control" + namespace: "media_solutions" + description: "Allow access to privileged routing capabilities to MEDIA_ROUTING_CONTROL holders." + bug: "305919655" +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 10d04d3ff6b3..c7e5bf98850a 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -668,6 +668,7 @@ <!-- Permission required for CTS test - SystemMediaRouter2Test --> <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" /> + <uses-permission android:name="android.permission.MEDIA_ROUTING_CONTROL" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <!-- Permission required for CTS test - SoundDoseHelperTest --> diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index d456a7478551..6a43697770cf 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -36,6 +36,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.media.IMediaRouter2; import android.media.IMediaRouter2Manager; @@ -200,7 +201,8 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { - enforcePrivilegedRoutingPermissions(uid, pid); + // TODO (b/305919655) - Handle revoking of MEDIA_ROUTING_CONTROL at runtime. + enforcePrivilegedRoutingPermissions(uid, pid, /* callerPackageName */ null); PackageManager pm = mContext.getPackageManager(); pm.getPackageInfo(clientPackageName, PackageManager.PackageInfoFlags.of(0)); return true; @@ -727,13 +729,36 @@ class MediaRouter2ServiceImpl { return hasBluetoothRoutingPermission; } - @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) - private void enforcePrivilegedRoutingPermissions(int callerUid, int callerPid) { - mContext.enforcePermission( - Manifest.permission.MEDIA_CONTENT_CONTROL, - callerPid, - callerUid, - "Must hold MEDIA_CONTENT_CONTROL permission."); + @RequiresPermission( + anyOf = { + Manifest.permission.MEDIA_ROUTING_CONTROL, + Manifest.permission.MEDIA_CONTENT_CONTROL + }) + private void enforcePrivilegedRoutingPermissions( + int callerUid, int callerPid, @Nullable String callerPackageName) { + if (mContext.checkPermission( + Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid) + == PackageManager.PERMISSION_GRANTED) { + return; + } + + if (!Flags.enablePrivilegedRoutingForMediaRoutingControl()) { + throw new SecurityException("Must hold MEDIA_CONTENT_CONTROL"); + } + + if (PermissionChecker.checkPermissionForDataDelivery( + mContext, + Manifest.permission.MEDIA_ROUTING_CONTROL, + callerPid, + callerUid, + callerPackageName, + /* attributionTag */ null, + /* message */ "Checking permissions for registering manager in" + + " MediaRouter2ServiceImpl.") + != PermissionChecker.PERMISSION_GRANTED) { + throw new SecurityException( + "Must hold MEDIA_CONTENT_CONTROL or MEDIA_ROUTING_CONTROL permissions."); + } } // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager. @@ -1195,7 +1220,8 @@ class MediaRouter2ServiceImpl { + " callerUserId: %d", callerUid, callerPid, callerPackageName, callerUserId)); - enforcePrivilegedRoutingPermissions(callerUid, callerPid); + // TODO (b/305919655) - Handle revoking of MEDIA_ROUTING_CONTROL at runtime. + enforcePrivilegedRoutingPermissions(callerUid, callerPid, callerPackageName); UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId); managerRecord = new ManagerRecord( |