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

Commit 71e5d2f4 authored by Santiago Seifert's avatar Santiago Seifert
Browse files

Add support for app route listing preferences

Test: atest MediaRouter2HostSideTest
Bug: 241888071
Bug: 235352899
Change-Id: Ic9545a035631f2adb79d46e6432356dd7af4f5a0
parent 16eaa6fe
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -23507,6 +23507,7 @@ package android.media {
    method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback, @NonNull android.media.RouteDiscoveryPreference);
    method public void registerTransferCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.TransferCallback);
    method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
    method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
    method public void stop();
    method public void transferTo(@NonNull android.media.MediaRoute2Info);
    method public void unregisterControllerCallback(@NonNull android.media.MediaRouter2.ControllerCallback);
@@ -23880,6 +23881,22 @@ package android.media {
    method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
  }
  public final class RouteListingPreference implements android.os.Parcelable {
    ctor public RouteListingPreference(@NonNull java.util.List<android.media.RouteListingPreference.Item>);
    method public int describeContents();
    method @NonNull public java.util.List<android.media.RouteListingPreference.Item> getItems();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference> CREATOR;
  }
  public static final class RouteListingPreference.Item implements android.os.Parcelable {
    ctor public RouteListingPreference.Item(@NonNull String);
    method public int describeContents();
    method @NonNull public String getRouteId();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference.Item> CREATOR;
  }
  public final class RoutingSessionInfo implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public String getClientPackageName();
+3 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.media;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
import android.media.RouteDiscoveryPreference;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;

/**
@@ -30,6 +31,8 @@ oneway interface IMediaRouter2Manager {
    void notifySessionReleased(in RoutingSessionInfo session);
    void notifyDiscoveryPreferenceChanged(String packageName,
            in RouteDiscoveryPreference discoveryPreference);
    void notifyRouteListingPreferenceChange(String packageName,
            in @nullable RouteListingPreference routeListingPreference);
    void notifyRoutesUpdated(in List<MediaRoute2Info> routes);
    void notifyRequestFailed(int requestId, int reason);
}
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.media.IMediaRouterClient;
import android.media.MediaRoute2Info;
import android.media.MediaRouterClientState;
import android.media.RouteDiscoveryPreference;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;

@@ -57,6 +58,8 @@ interface IMediaRouterService {
    void unregisterRouter2(IMediaRouter2 router);
    void setDiscoveryRequestWithRouter2(IMediaRouter2 router,
            in RouteDiscoveryPreference preference);
    void setRouteListingPreference(IMediaRouter2 router,
            in @nullable RouteListingPreference routeListingPreference);
    void setRouteVolumeWithRouter2(IMediaRouter2 router, in MediaRoute2Info route, int volume);

    void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
+50 −0
Original line number Diff line number Diff line
@@ -112,6 +112,10 @@ public final class MediaRouter2 {
    @GuardedBy("mLock")
    final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>();

    @GuardedBy("mLock")
    @Nullable
    private RouteListingPreference mRouteListingPreference;

    final RoutingController mSystemController;

    @GuardedBy("mLock")
@@ -461,6 +465,52 @@ public final class MediaRouter2 {
        }
    }

    /**
     * Sets the {@link RouteListingPreference} of the app associated to this media router.
     *
     * <p>Use this method to inform the system UI of the routes that you would like to list for
     * media routing, via the Output Switcher.
     *
     * <p>You should call this method before {@link #registerRouteCallback registering any route
     * callbacks} and immediately after receiving any {@link RouteCallback#onRoutesUpdated route
     * updates} in order to keep the system UI in a consistent state. You can also call this method
     * at any other point to update the listing preference dynamically.
     *
     * <p>Notes:
     *
     * <ol>
     *   <li>You should not include the ids of two or more routes with a match in their {@link
     *       MediaRoute2Info#getDeduplicationIds() deduplication ids}. If you do, the system will
     *       deduplicate them using its own criteria.
     *   <li>You can use this method to rank routes in the output switcher, placing the more
     *       important routes first. The system might override the proposed ranking.
     *   <li>You can use this method to avoid listing routes using dynamic criteria. For example,
     *       you can limit access to a specific type of device according to runtime criteria.
     * </ol>
     *
     * @param routeListingPreference The {@link RouteListingPreference} for the system to use for
     *     route listing. When null, the system uses its default listing criteria.
     */
    public void setRouteListingPreference(@Nullable RouteListingPreference routeListingPreference) {
        synchronized (mLock) {
            if (Objects.equals(mRouteListingPreference, routeListingPreference)) {
                // Nothing changed. We return early to save a call to the system server.
                return;
            }
            mRouteListingPreference = routeListingPreference;
            try {
                if (mStub == null) {
                    MediaRouter2Stub stub = new MediaRouter2Stub();
                    mMediaRouterService.registerRouter2(stub, mPackageName);
                    mStub = stub;
                }
                mMediaRouterService.setRouteListingPreference(mStub, mRouteListingPreference);
            } catch (RemoteException ex) {
                ex.rethrowFromSystemServer();
            }
        }
    }

    @GuardedBy("mLock")
    private boolean updateDiscoveryPreferenceIfNeededLocked() {
        RouteDiscoveryPreference newDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+58 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.util.ArrayList;
import java.util.Collections;
@@ -93,6 +94,11 @@ public final class MediaRouter2Manager {
    @NonNull
    final ConcurrentMap<String, RouteDiscoveryPreference> mDiscoveryPreferenceMap =
            new ConcurrentHashMap<>();
    // TODO(b/241888071): Merge mDiscoveryPreferenceMap and mPackageToRouteListingPreferenceMap into
    //     a single record object maintained by a single package-to-record map.
    @NonNull
    private final ConcurrentMap<String, RouteListingPreference>
            mPackageToRouteListingPreferenceMap = new ConcurrentHashMap<>();

    private final AtomicInteger mNextRequestId = new AtomicInteger(1);
    private final CopyOnWriteArrayList<TransferRequest> mTransferRequests =
@@ -354,6 +360,16 @@ public final class MediaRouter2Manager {
        return mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);
    }

    /**
     * Returns the {@link RouteListingPreference} of the app with the given {@code packageName}, or
     * null if the app has not set any.
     */
    @Nullable
    public RouteListingPreference getRouteListingPreference(@NonNull String packageName) {
        Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
        return mPackageToRouteListingPreferenceMap.get(packageName);
    }

    /**
     * Gets the system routing session for the given {@code packageName}.
     * Apps can select a route that is not the global route. (e.g. an app can select the device
@@ -686,6 +702,24 @@ public final class MediaRouter2Manager {
        }
    }

    private void updateRouteListingPreference(
            @NonNull String packageName, @Nullable RouteListingPreference routeListingPreference) {
        RouteListingPreference oldRouteListingPreference =
                routeListingPreference == null
                        ? mPackageToRouteListingPreferenceMap.remove(packageName)
                        : mPackageToRouteListingPreferenceMap.put(
                                packageName, routeListingPreference);
        if (Objects.equals(oldRouteListingPreference, routeListingPreference)) {
            return;
        }
        for (CallbackRecord record : mCallbackRecords) {
            record.mExecutor.execute(
                    () ->
                            record.mCallback.onRouteListingPreferenceUpdated(
                                    packageName, routeListingPreference));
        }
    }

    /**
     * Gets the unmodifiable list of selected routes for the session.
     */
@@ -970,6 +1004,19 @@ public final class MediaRouter2Manager {
            onPreferredFeaturesChanged(packageName, discoveryPreference.getPreferredFeatures());
        }

        /**
         * Called when the app with the given {@code packageName} updates its {@link
         * MediaRouter2#setRouteListingPreference route listing preference}.
         *
         * @param packageName The package name of the app that changed its listing preference.
         * @param routeListingPreference The new {@link RouteListingPreference} set by the app with
         *     the given {@code packageName}. Maybe null if an app has unset its preference (by
         *     passing null to {@link MediaRouter2#setRouteListingPreference}).
         */
        default void onRouteListingPreferenceUpdated(
                @NonNull String packageName,
                @Nullable RouteListingPreference routeListingPreference) {}

        /**
         * Called when a previous request has failed.
         *
@@ -1055,6 +1102,17 @@ public final class MediaRouter2Manager {
                    MediaRouter2Manager.this, packageName, discoveryPreference));
        }

        @Override
        public void notifyRouteListingPreferenceChange(
                String packageName, @Nullable RouteListingPreference routeListingPreference) {
            mHandler.sendMessage(
                    obtainMessage(
                            MediaRouter2Manager::updateRouteListingPreference,
                            MediaRouter2Manager.this,
                            packageName,
                            routeListingPreference));
        }

        @Override
        public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
            mHandler.sendMessage(
Loading