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

Commit 1fcff7e0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add MR2ServiceProvider-provided system media routes" into main

parents b2b2b3d5 42a3c29a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -933,6 +933,15 @@ public final class MediaRoute2Info implements Parcelable {
                != 0;
    }

    /**
     * Returns whether this route supports {@link #FLAG_ROUTING_TYPE_REMOTE remote routing}.
     *
     * @hide
     */
    public boolean supportsRemoteRouting() {
        return (mRoutingTypeFlags & MediaRoute2Info.FLAG_ROUTING_TYPE_REMOTE) != 0;
    }

    /**
     * Returns true if the route info has all of the required field.
     * A route is valid if and only if it is obtained from
+3 −1
Original line number Diff line number Diff line
@@ -289,7 +289,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
        // doesn't have any registered discovery preference, we should still be able to route their
        // system media.
        boolean bindDueToSystemMediaRoutingSupport =
                mIsManagerScanning && mSupportsSystemMediaRouting;
                mLastDiscoveryPreference != null
                        && mLastDiscoveryPreference.shouldPerformActiveScan()
                        && mSupportsSystemMediaRouting;
        if (!getSessionInfos().isEmpty()
                || bindDueToManagerScan
                || bindDueToSystemMediaRoutingSupport) {
+10 −1
Original line number Diff line number Diff line
@@ -848,7 +848,7 @@ class MediaRouter2ServiceImpl {
                UserRecord userRecord = getOrCreateUserRecordLocked(userId);
                List<RoutingSessionInfo> sessionInfos;
                if (hasSystemRoutingPermissions) {
                    if (setDeviceRouteSelected) {
                    if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) {
                        // Return a fake system session that shows the device route as selected and
                        // available bluetooth routes as transferable.
                        return userRecord.mHandler.getSystemProvider()
@@ -2733,6 +2733,15 @@ class MediaRouter2ServiceImpl {
                newRoutes = Collections.emptySet();
            }

            if (Flags.enableMirroringInMediaRouter2()
                    && provider instanceof MediaRoute2ProviderServiceProxy proxyProvider) {
                // We notify the system provider of service updates, so that it can update the
                // system routing session by adding them as transferable routes. And we remove those
                // that don't support remote routing.
                mSystemProvider.updateSystemMediaRoutesFromProxy(proxyProvider);
                newRoutes.removeIf(it -> !it.supportsRemoteRouting());
            }

            // Add new routes to the maps.
            ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>();
            boolean hasAddedOrModifiedRoutes = false;
+58 −8
Original line number Diff line number Diff line
@@ -73,6 +73,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
    // For apps without MODIFYING_AUDIO_ROUTING permission.
    // This should be the currently selected route.
    MediaRoute2Info mDefaultRoute;

    @GuardedBy("mLock")
    RoutingSessionInfo mSystemSessionInfo;

    RoutingSessionInfo mDefaultSessionInfo;

    private final AudioManagerBroadcastReceiver mAudioReceiver =
@@ -180,7 +184,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
                RoutingSessionInfo currentSessionInfo;
                synchronized (mLock) {
                    currentSessionInfo = mSessionInfos.get(0);
                    currentSessionInfo =
                            Flags.enableMirroringInMediaRouter2()
                                    ? mSystemSessionInfo
                                    : mSessionInfos.get(0);
                }
                mCallback.onSessionCreated(this, requestId, currentSessionInfo);
                return;
@@ -354,7 +361,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            }

            if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
                RoutingSessionInfo oldSessionInfo = mSessionInfos.get(0);
                var oldSessionInfo =
                        Flags.enableMirroringInMediaRouter2()
                                ? mSystemSessionInfo
                                : mSessionInfos.get(0);
                builder.setTransferReason(oldSessionInfo.getTransferReason())
                        .setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(),
                                oldSessionInfo.getTransferInitiatorPackageName());
@@ -364,6 +374,31 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        }
    }

    /**
     * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update.
     *
     * <p>To be overridden so as to generate system media routes for {@link
     * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting()
     * support system media routing}).
     *
     * @param serviceProxy The proxy of the service that updated its state.
     */
    public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) {
        // Do nothing. This implementation doesn't support MR2ProviderService system media routes.
        // The subclass overrides this method to implement app-managed system media routing (aka
        // mirroring).
    }

    /**
     * Called when the system provider state changes.
     *
     * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media
     * routing routes are added before setting the provider state.
     */
    public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
        setProviderState(providerInfo);
    }

    protected void updateProviderState() {
        MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();

@@ -373,7 +408,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            for (MediaRoute2Info route : deviceRoutes) {
                builder.addRoute(route);
            }
            if (!Flags.enableMirroringInMediaRouter2()) {
                setProviderState(builder.build());
            }
        } else {
            builder.addRoute(mDeviceRouteController.getSelectedRoute());
        }
@@ -382,7 +419,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            builder.addRoute(route);
        }
        MediaRoute2ProviderInfo providerInfo = builder.build();
        setProviderState(providerInfo);
        onSystemProviderRoutesChanged(providerInfo);
        if (DEBUG) {
            Slog.d(TAG, "Updating system provider info : " + providerInfo);
        }
@@ -393,8 +430,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
     */
    boolean updateSessionInfosIfNeeded() {
        synchronized (mLock) {
            RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
                    0);
            RoutingSessionInfo oldSessionInfo;
            if (Flags.enableMirroringInMediaRouter2()) {
                oldSessionInfo = mSystemSessionInfo;
            } else {
                oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
            }

            RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
                    SYSTEM_SESSION_ID, "" /* clientPackageName */)
@@ -483,8 +524,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
                if (DEBUG) {
                    Slog.d(TAG, "Updating system routing session info : " + newSessionInfo);
                }
                mSessionInfos.clear();
                mSessionInfos.add(newSessionInfo);
                mSystemSessionInfo = newSessionInfo;
                onSystemSessionInfoUpdated();
                mDefaultSessionInfo =
                        new RoutingSessionInfo.Builder(
                                        SYSTEM_SESSION_ID, "" /* clientPackageName */)
@@ -501,6 +542,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        }
    }

    @GuardedBy("mLock")
    protected void onSystemSessionInfoUpdated() {
        mSessionInfos.clear();
        mSessionInfos.add(mSystemSessionInfo);
    }

    @GuardedBy("mRequestLock")
    private void reportPendingSessionRequestResultLockedIfNeeded(
            RoutingSessionInfo newSessionInfo) {
@@ -587,6 +634,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        RoutingSessionInfo sessionInfo;
        synchronized (mLock) {
            sessionInfo = mSessionInfos.get(0);
            if (sessionInfo == null) {
                return;
            }
        }

        mCallback.onSessionUpdated(this, sessionInfo);
+137 −0
Original line number Diff line number Diff line
@@ -16,11 +16,26 @@

package com.android.server.media;

import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;

import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
import android.media.RoutingSessionInfo;
import android.os.Looper;
import android.os.UserHandle;
import android.util.ArraySet;

import com.android.internal.annotations.GuardedBy;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

/**
 * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link
@@ -30,6 +45,15 @@ import android.os.UserHandle;
 */
/* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider {

    private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM";
    private static final String ROUTE_ID_SYSTEM_SEPARATOR = ".";

    @GuardedBy("mLock")
    private MediaRoute2ProviderInfo mLastSystemProviderInfo;

    @GuardedBy("mLock")
    private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>();

    private static final ComponentName COMPONENT_NAME =
            new ComponentName(
                    SystemMediaRoute2Provider2.class.getPackage().getName(),
@@ -46,4 +70,117 @@ import android.os.UserHandle;
    private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
        super(context, COMPONENT_NAME, user, looper);
    }

    @Override
    protected void onSystemSessionInfoUpdated() {
        updateSessionInfo();
    }

    @Override
    public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) {
        var proxyRecord = ProviderProxyRecord.createFor(serviceProxy);
        synchronized (mLock) {
            if (proxyRecord == null) {
                mProxyRecords.remove(serviceProxy.mUniqueId);
            } else {
                mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord);
            }
            setProviderState(buildProviderInfo());
        }
        updateSessionInfo();
        notifyProviderState();
        notifySessionInfoUpdated();
    }

    @Override
    public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
        synchronized (mLock) {
            mLastSystemProviderInfo = providerInfo;
            setProviderState(buildProviderInfo());
        }
        updateSessionInfo();
        notifySessionInfoUpdated();
    }

    /**
     * Updates the {@link #mSessionInfos} by expanding the {@link SystemMediaRoute2Provider} session
     * with information from the {@link MediaRoute2ProviderService provider services}.
     */
    private void updateSessionInfo() {
        synchronized (mLock) {
            var systemSessionInfo = mSystemSessionInfo;
            if (systemSessionInfo == null) {
                // The system session info hasn't been initialized yet. Do nothing.
                return;
            }
            var builder = new RoutingSessionInfo.Builder(systemSessionInfo);
            mProxyRecords.values().stream()
                    .flatMap(ProviderProxyRecord::getRoutesStream)
                    .map(MediaRoute2Info::getId)
                    .forEach(builder::addTransferableRoute);
            mSessionInfos.clear();
            mSessionInfos.add(builder.build());
        }
    }

    /**
     * Returns a new a provider info that includes all routes from the system provider {@link
     * SystemMediaRoute2Provider}, along with system routes from {@link MediaRoute2ProviderService
     * provider services}.
     */
    @GuardedBy("mLock")
    private MediaRoute2ProviderInfo buildProviderInfo() {
        MediaRoute2ProviderInfo.Builder builder =
                new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo);
        mProxyRecords.values().stream()
                .flatMap(ProviderProxyRecord::getRoutesStream)
                .forEach(builder::addRoute);
        return builder.build();
    }

    /**
     * Holds information about {@link MediaRoute2ProviderService provider services} registered in
     * the system.
     *
     * @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}.
     * @param mSystemMediaRoutes The last snapshot of routes from the service that support system
     *     media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}.
     */
    private record ProviderProxyRecord(
            MediaRoute2ProviderServiceProxy mProxy,
            Collection<MediaRoute2Info> mSystemMediaRoutes) {

        /** Returns a stream representation of the {@link #mSystemMediaRoutes}. */
        public Stream<MediaRoute2Info> getRoutesStream() {
            return mSystemMediaRoutes.stream();
        }

        /**
         * Returns a new instance, or null if the given {@code serviceProxy} doesn't have an
         * associated {@link MediaRoute2ProviderInfo}.
         */
        @Nullable
        public static ProviderProxyRecord createFor(MediaRoute2ProviderServiceProxy serviceProxy) {
            MediaRoute2ProviderInfo providerInfo = serviceProxy.getProviderInfo();
            if (providerInfo == null) {
                return null;
            }
            ArraySet<MediaRoute2Info> routes = new ArraySet<>();
            providerInfo.getRoutes().stream()
                    .filter(MediaRoute2Info::supportsSystemMediaRouting)
                    .forEach(
                            route -> {
                                String id =
                                        ROUTE_ID_PREFIX_SYSTEM
                                                + route.getProviderId()
                                                + ROUTE_ID_SYSTEM_SEPARATOR
                                                + route.getOriginalId();
                                routes.add(
                                        new MediaRoute2Info.Builder(id, route.getName())
                                                .addFeature(FEATURE_LIVE_AUDIO)
                                                .build());
                            });
            return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes));
        }
    }
}