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

Commit e2dbbf79 authored by Kyunglyul Hyun's avatar Kyunglyul Hyun
Browse files

Introduce APIs to deal with RCN case

This CL does following:
 - MR2Manager#getRoutingSessionForMediaController is added
 - MR2Manager#getAvailableRoutesForRoutingSession is added
 - Add a workaround for provider to provide a routing session
   not requested by the user (RCN case)

Using the added methods, we exepect System UI to implement UX easily.

Bug: 154780833
Bug: 152582294
Test: It is tested manually with two demo APKs. MediaRoute2ProviderDemo
app is updated to enable the user to create a routing session w/o
MediaRouter2 clients and Sample Output Switcher is updated to use the
new APIs. With updated APKs, transfer from cast to cast (Variable
Volume1 -> Variable Volume2) is enabled.

Change-Id: I03c261c1779725f43933bacad429e1a22f602818
parent aa795a68
Loading
Loading
Loading
Loading
+80 −2
Original line number Diff line number Diff line
@@ -171,8 +171,7 @@ public final class MediaRouter2Manager {
    public MediaController getMediaControllerForRoutingSession(
            @NonNull RoutingSessionInfo sessionInfo) {
        for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
            String volumeControlId = controller.getPlaybackInfo().getVolumeControlId();
            if (TextUtils.equals(sessionInfo.getId(), volumeControlId)) {
            if (areSessionsMatched(controller, sessionInfo)) {
                return controller;
            }
        }
@@ -206,6 +205,37 @@ public final class MediaRouter2Manager {
        return routes;
    }

    /**
     * Gets available routes for the given routing session.
     * The returned routes can be passed to
     * {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} for transferring the routing session.
     *
     * @param sessionInfo the routing session that would be transferred
     */
    @NonNull
    public List<MediaRoute2Info> getAvailableRoutesForRoutingSession(
            @NonNull RoutingSessionInfo sessionInfo) {
        Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");

        List<MediaRoute2Info> routes = new ArrayList<>();

        String packageName = sessionInfo.getClientPackageName();
        List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
        if (preferredFeatures == null) {
            preferredFeatures = Collections.emptyList();
        }
        synchronized (mRoutesLock) {
            for (MediaRoute2Info route : mRoutes.values()) {
                if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)
                        || sessionInfo.getSelectedRoutes().contains(route.getId())
                        || sessionInfo.getTransferableRoutes().contains(route.getId())) {
                    routes.add(route);
                }
            }
        }
        return routes;
    }

    /**
     * Gets the system routing session associated with no specific application.
     */
@@ -219,6 +249,33 @@ public final class MediaRouter2Manager {
        throw new IllegalStateException("No system routing session");
    }

    /**
     * Gets the routing session of a media session.
     * If the session is using {#link PlaybackInfo#PLAYBACK_TYPE_LOCAL local playback},
     * the system routing session is returned.
     * If the session is using {#link PlaybackInfo#PLAYBACK_TYPE_REMOTE remote playback},
     * it returns the corresponding routing session or {@code null} if it's unavailable.
     */
    @Nullable
    public RoutingSessionInfo getRoutingSessionForMediaController(MediaController mediaController) {
        MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
        if (playbackInfo == null) {
            return null;
        }
        if (playbackInfo.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
            return new RoutingSessionInfo.Builder(getSystemRoutingSession())
                    .setClientPackageName(mediaController.getPackageName())
                    .build();
        }
        for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
            if (!sessionInfo.isSystemSession()
                    && areSessionsMatched(mediaController, sessionInfo)) {
                return sessionInfo;
            }
        }
        return null;
    }

    /**
     * Gets routing sessions of an application with the given package name.
     * The first element of the returned list is the system routing session.
@@ -782,6 +839,27 @@ public final class MediaRouter2Manager {
        }
    }

    private boolean areSessionsMatched(MediaController mediaController,
            RoutingSessionInfo sessionInfo) {
        MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
        if (playbackInfo == null) {
            return false;
        }

        String volumeControlId = playbackInfo.getVolumeControlId();
        if (volumeControlId == null) {
            return false;
        }

        if (TextUtils.equals(volumeControlId, sessionInfo.getId())) {
            return true;
        }
        // Workaround for provider not being able to know the unique session ID.
        return TextUtils.equals(volumeControlId, sessionInfo.getOriginalId())
                && TextUtils.equals(mediaController.getPackageName(),
                sessionInfo.getOwnerPackageName());
    }

    private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
        synchronized (sLock) {
            return routeIds.stream().map(mRoutes::get)
+27 −1
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ public final class RoutingSessionInfo implements Parcelable {

    final String mId;
    final CharSequence mName;
    final String mOwnerPackageName;
    final String mClientPackageName;
    @Nullable
    final String mProviderId;
@@ -71,6 +72,7 @@ public final class RoutingSessionInfo implements Parcelable {

        mId = builder.mId;
        mName = builder.mName;
        mOwnerPackageName = builder.mOwnerPackageName;
        mClientPackageName = builder.mClientPackageName;
        mProviderId = builder.mProviderId;

@@ -96,6 +98,7 @@ public final class RoutingSessionInfo implements Parcelable {

        mId = ensureString(src.readString());
        mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
        mOwnerPackageName = src.readString();
        mClientPackageName = ensureString(src.readString());
        mProviderId = src.readString();

@@ -158,6 +161,15 @@ public final class RoutingSessionInfo implements Parcelable {
        return mId;
    }

    /**
     * Gets the package name of the session owner.
     * @hide
     */
    @Nullable
    public String getOwnerPackageName() {
        return mOwnerPackageName;
    }

    /**
     * Gets the client package name of the session
     */
@@ -263,6 +275,7 @@ public final class RoutingSessionInfo implements Parcelable {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString(mId);
        dest.writeCharSequence(mName);
        dest.writeString(mOwnerPackageName);
        dest.writeString(mClientPackageName);
        dest.writeString(mProviderId);
        dest.writeStringList(mSelectedRoutes);
@@ -288,6 +301,7 @@ public final class RoutingSessionInfo implements Parcelable {
        RoutingSessionInfo other = (RoutingSessionInfo) obj;
        return Objects.equals(mId, other.mId)
                && Objects.equals(mName, other.mName)
                && Objects.equals(mOwnerPackageName, other.mOwnerPackageName)
                && Objects.equals(mClientPackageName, other.mClientPackageName)
                && Objects.equals(mProviderId, other.mProviderId)
                && Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
@@ -301,7 +315,7 @@ public final class RoutingSessionInfo implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(mId, mName, mClientPackageName, mProviderId,
        return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId,
                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
                mVolumeMax, mVolumeHandling, mVolume);
    }
@@ -356,6 +370,7 @@ public final class RoutingSessionInfo implements Parcelable {
        // TODO: Reorder these (important ones first)
        final String mId;
        CharSequence mName;
        String mOwnerPackageName;
        String mClientPackageName;
        String mProviderId;
        final List<String> mSelectedRoutes;
@@ -439,6 +454,17 @@ public final class RoutingSessionInfo implements Parcelable {
            return this;
        }

        /**
         * Sets the package name of the session owner. It is expected to be called by the system.
         *
         * @hide
         */
        @NonNull
        public Builder setOwnerPackageName(@Nullable String packageName) {
            mOwnerPackageName = packageName;
            return this;
        }

        /**
         * Sets the client package name of the session.
         *
+10 −9
Original line number Diff line number Diff line
@@ -315,9 +315,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
            return;
        }

        sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                .setProviderId(getUniqueId())
                .build();
        sessionInfo = updateSessionInfo(sessionInfo);

        boolean duplicateSessionAlreadyExists = false;
        synchronized (mLock) {
@@ -348,9 +346,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
            return;
        }

        sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                .setProviderId(getUniqueId())
                .build();
        sessionInfo = updateSessionInfo(sessionInfo);

        boolean found = false;
        synchronized (mLock) {
@@ -380,9 +376,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
            return;
        }

        sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
                .setProviderId(getUniqueId())
                .build();
        sessionInfo = updateSessionInfo(sessionInfo);

        boolean found = false;
        synchronized (mLock) {
@@ -403,6 +397,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        mCallback.onSessionReleased(this, sessionInfo);
    }

    private RoutingSessionInfo updateSessionInfo(RoutingSessionInfo sessionInfo) {
        return new RoutingSessionInfo.Builder(sessionInfo)
                .setOwnerPackageName(mComponentName.getPackageName())
                .setProviderId(getUniqueId())
                .build();
    }

    private void onRequestFailed(Connection connection, long requestId, int reason) {
        if (mActiveConnection != connection) {
            return;