Merge changes from topic "system_mr2_media_content_control" into sc-dev

* changes:
  System MediaRouter2: Resolve API review
  Change MODIFY_AUDIO_ROUTING permission to MEDIA_CONTENT_CONTROL
This commit is contained in:
Hyundo Moon
2021-04-21 01:45:42 +00:00
committed by Android (Google) Code Review
10 changed files with 84 additions and 76 deletions

View File

@@ -23307,6 +23307,7 @@ package android.media {
}
public final class MediaRouter2 {
method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();

View File

@@ -5327,12 +5327,11 @@ package android.media {
public final class MediaRouter2 {
method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
method @Nullable public String getClientPackageName();
method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method public void startScan();
method public void stopScan();
method public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
method @Nullable @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
}
public abstract static class MediaRouter2.RouteCallback {

View File

@@ -509,7 +509,7 @@ applications that come with the platform
<!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
<permission name="android.permission.SCHEDULE_PRIORITIZED_ALARM" />
<!-- Permission required for CTS test - SystemMediaRouter2Test -->
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<!-- Permission required for CTS test - CtsPermission5TestCases -->
<permission name="android.permission.RENOUNCE_PERMISSIONS" />
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />

View File

@@ -48,7 +48,7 @@ interface IMediaRouterService {
// MediaRouterService.java for readability.
// Methods for MediaRouter2
void checkModifyAudioRoutingPermission();
void enforceMediaContentControlPermission();
List<MediaRoute2Info> getSystemRoutes();
RoutingSessionInfo getSystemSessionInfo();

View File

@@ -18,6 +18,7 @@ package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -93,7 +94,7 @@ public final class MediaRouter2 {
// TODO: Specify the fields that are only used (or not used) by system media router.
private final String mClientPackageName;
private final ManagerCallback mManagerCallback;
final ManagerCallback mManagerCallback;
private final String mPackageName;
@@ -164,13 +165,24 @@ public final class MediaRouter2 {
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
@Nullable
public static MediaRouter2 getInstance(@NonNull Context context,
@NonNull String clientPackageName) {
Objects.requireNonNull(context, "context must not be null");
Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
// Note: Even though this check could be somehow bypassed, the other permission checks
// in system server will not allow MediaRouter2Manager to be registered.
IMediaRouterService serviceBinder = IMediaRouterService.Stub.asInterface(
ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
try {
// SecurityException will be thrown if there's no permission.
serviceBinder.enforceMediaContentControlPermission();
} catch (RemoteException e) {
Log.e(TAG, "Unable to check MEDIA_CONTENT_CONTROL permission.");
}
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo(clientPackageName, 0);
@@ -183,20 +195,13 @@ public final class MediaRouter2 {
MediaRouter2 instance = sSystemMediaRouter2Map.get(clientPackageName);
if (instance == null) {
if (sManager == null) {
IMediaRouterService serviceBinder = IMediaRouterService.Stub.asInterface(
ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
try {
// MediaRouterService will throw a SecurityException if the caller
// doesn't have MODIFY_AUDIO_ROUTING permission.
serviceBinder.checkModifyAudioRoutingPermission();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
sManager = MediaRouter2Manager.getInstance(context.getApplicationContext());
}
instance = new MediaRouter2(context, clientPackageName);
sSystemMediaRouter2Map.put(clientPackageName, instance);
instance.registerManagerCallbackForSystemRouter();
// Using direct executor here, since MediaRouter2Manager also posts
// to the main handler.
sManager.registerCallback(Runnable::run, instance.mManagerCallback);
}
return instance;
}
@@ -213,11 +218,15 @@ public final class MediaRouter2 {
* Use {@link RouteCallback} to get the route related events.
* <p>
* Note that calling start/stopScan is applied to all system routers in the same process.
* <p>
* This will be no-op for non-system media routers.
*
* @see #stopScan()
* @see #getInstance(Context, String)
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void startScan() {
if (isSystemRouter()) {
sManager.startScan();
@@ -236,11 +245,15 @@ public final class MediaRouter2 {
* Use {@link RouteCallback} to get the route related events.
* <p>
* Note that calling start/stopScan is applied to all system routers in the same process.
* <p>
* This will be no-op for non-system media routers.
*
* @see #startScan()
* @see #getInstance(Context, String)
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void stopScan() {
if (isSystemRouter()) {
sManager.stopScan();
@@ -314,7 +327,8 @@ public final class MediaRouter2 {
/**
* Gets the client package name of the app which this media router controls.
* This is only non-null when the router instance is created with the client package name.
* <p>
* This will return null for non-system media routers.
*
* @see #getInstance(Context, String)
* @hide
@@ -573,9 +587,25 @@ public final class MediaRouter2 {
return;
}
Objects.requireNonNull(route, "route must not be null");
Log.v(TAG, "Transferring to route: " + route);
transfer(getCurrentController(), route);
boolean routeFound;
synchronized (mLock) {
// TODO: Check thread-safety
routeFound = mRoutes.containsKey(route.getId());
}
if (!routeFound) {
notifyTransferFailure(route);
return;
}
RoutingController controller = getCurrentController();
if (controller.getRoutingSessionInfo().getTransferableRoutes().contains(route.getId())) {
controller.transferToRoute(route);
return;
}
requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
}
/**
@@ -594,36 +624,20 @@ public final class MediaRouter2 {
/**
* Transfers the media of a routing controller to the given route.
* <p>
* This will be no-op for non-system media routers.
*
* @param controller a routing controller controlling media routing.
* @param route the route you want to transfer the media to.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
if (isSystemRouter()) {
sManager.transfer(controller.getRoutingSessionInfo(), route);
return;
}
Objects.requireNonNull(controller, "controller must not be null");
Objects.requireNonNull(route, "route must not be null");
boolean routeFound;
synchronized (mLock) {
// TODO: Check thread-safety
routeFound = mRoutes.containsKey(route.getId());
}
if (!routeFound) {
notifyTransferFailure(route);
return;
}
if (controller.getRoutingSessionInfo().getTransferableRoutes().contains(route.getId())) {
controller.transferToRoute(route);
return;
}
requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
}
void requestCreateController(@NonNull RoutingController controller,
@@ -687,9 +701,7 @@ public final class MediaRouter2 {
/**
* Gets a {@link RoutingController} whose ID is equal to the given ID.
* Returns {@code null} if there is no matching controller.
* @hide
*/
@SystemApi
@Nullable
public RoutingController getController(@NonNull String id) {
Objects.requireNonNull(id, "id must not be null");
@@ -739,14 +751,16 @@ public final class MediaRouter2 {
/**
* Requests a volume change for the route asynchronously.
* <p>
* It may have no effect if the route is currently not selected.
* </p>
* <p>
* This will be no-op for non-system media routers.
*
* @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
* @see #getInstance(Context, String)
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
Objects.requireNonNull(route, "route must not be null");
@@ -754,18 +768,7 @@ public final class MediaRouter2 {
sManager.setRouteVolume(route, volume);
return;
}
MediaRouter2Stub stub;
synchronized (mLock) {
stub = mStub;
}
if (stub != null) {
try {
mMediaRouterService.setRouteVolumeWithRouter2(stub, route, volume);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to set route volume.", ex);
}
}
// If this API needs to be public, use IMediaRouterService#setRouteVolumeWithRouter2()
}
void syncRoutesOnHandler(List<MediaRoute2Info> currentRoutes,
@@ -1039,15 +1042,6 @@ public final class MediaRouter2 {
return mClientPackageName != null;
}
/**
* Registers {@link MediaRouter2Manager.Callback} for getting events.
* Should only used for system media routers.
*/
private void registerManagerCallbackForSystemRouter() {
// Using direct executor here, since MediaRouter2Manager also posts to the main handler.
sManager.registerCallback(Runnable::run, mManagerCallback);
}
/**
* Returns a {@link RoutingSessionInfo} which has the client package name.
* The client package name is set only when the given sessionInfo doesn't have it.
@@ -1073,6 +1067,9 @@ public final class MediaRouter2 {
}
private void updateAllRoutesFromManager() {
if (!isSystemRouter()) {
return;
}
synchronized (mLock) {
mRoutes.clear();
for (MediaRoute2Info route : sManager.getAllRoutes()) {

View File

@@ -49,6 +49,8 @@ import java.util.stream.Collectors;
/**
* A class that monitors and controls media routing of other apps.
* {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} is required to use this class,
* or {@link SecurityException} will be thrown.
* @hide
*/
public final class MediaRouter2Manager {

View File

@@ -42,6 +42,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.Manifest;
import android.app.UiAutomation;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
@@ -87,6 +89,7 @@ public class MediaRouter2ManagerTest {
private static final String TEST_NAME_UNKNOWN = "unknown";
private Context mContext;
private UiAutomation mUiAutomation;
private MediaRouter2Manager mManager;
private MediaRouter2 mRouter2;
private Executor mExecutor;
@@ -110,6 +113,8 @@ public class MediaRouter2ManagerTest {
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL);
mManager = MediaRouter2Manager.getInstance(mContext);
mRouter2 = MediaRouter2.getInstance(mContext);
// If we need to support thread pool executors, change this to thread pool executor.
@@ -129,6 +134,8 @@ public class MediaRouter2ManagerTest {
instance.setProxy(null);
instance.setSpy(null);
}
mUiAutomation.dropShellPermissionIdentity();
}
@Test

View File

@@ -541,7 +541,7 @@
<uses-permission android:name="android.permission.SCHEDULE_PRIORITIZED_ALARM" />
<!-- Permission required for CTS test - SystemMediaRouter2Test -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<!-- Permission required for CTS test - CtsRotationResolverServiceDeviceTestCases -->
<uses-permission android:name="android.permission.MANAGE_ROTATION_RESOLVER" />

View File

@@ -25,6 +25,7 @@ import static android.media.MediaRouter2Utils.getProviderId;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -142,16 +143,14 @@ class MediaRouter2ServiceImpl {
////////////////////////////////////////////////////////////////
@NonNull
public void checkModifyAudioRoutingPermission() {
public void enforceMediaContentControlPermission() {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
if (mContext.checkPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Must hold the MODIFY_AUDIO_ROUTING permission.");
}
mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid,
"Must hold MEDIA_CONTENT_CONTROL permission.");
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -890,6 +889,9 @@ class MediaRouter2ServiceImpl {
return;
}
mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid,
"Must hold MEDIA_CONTENT_CONTROL permission.");
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName);
try {

View File

@@ -438,8 +438,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
public void checkModifyAudioRoutingPermission() {
mService2.checkModifyAudioRoutingPermission();
public void enforceMediaContentControlPermission() {
mService2.enforceMediaContentControlPermission();
}
// Binder call