Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 013cf241 authored by Hyundo Moon's avatar Hyundo Moon Committed by Android (Google) Code Review
Browse files

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
parents 2e2e479e 057b4b8d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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();
+5 −6
Original line number Diff line number Diff line
@@ -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 {
+1 −1
Original line number Diff line number Diff line
@@ -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" />
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ interface IMediaRouterService {
    // MediaRouterService.java for readability.

    // Methods for MediaRouter2
    void checkModifyAudioRoutingPermission();
    void enforceMediaContentControlPermission();
    List<MediaRoute2Info> getSystemRoutes();
    RoutingSessionInfo getSystemSessionInfo();

+57 −60
Original line number Diff line number Diff line
@@ -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()) {
Loading