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

Commit 23b3aaa4 authored by Kyunglyul Hyun's avatar Kyunglyul Hyun
Browse files

Media: add MediaRouter2.getRoutes()

As with this CL, media apps can get the list of available routes using
MediaRouter2.getRoutes() or MediaRouter2.Callback.onRouteListChanged().

For that MediaRouterService notifies MediaRouter2 of providerinfos.

This CL also clarifies provider info notification logic.

1) When a new client or a new manager is registered, it will be
notified.
2) When a provider info is updated, it will be notified.

onRouteListChanged will be called for a newly registerd callback.

Fixed a bug that MediaRoute2ProviderProxy didn't report state update
when the provider is disonnected.

Test: atest mediaroutertest
Change-Id: I50f5c3cabce80e7fb0a1b5596883c35911d6f122
parent 05c94cec
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -22,5 +22,5 @@ import android.media.MediaRoute2ProviderInfo;
 * @hide
 */
oneway interface IMediaRoute2ProviderClient {
    void notifyProviderInfoUpdated(in MediaRoute2ProviderInfo info);
    void updateProviderInfo(in MediaRoute2ProviderInfo info);
}
+3 −1
Original line number Diff line number Diff line
@@ -16,10 +16,12 @@

package android.media;

import android.media.MediaRoute2ProviderInfo;

/**
 * @hide
 */
oneway interface IMediaRouter2Client {
    void notifyStateChanged();
    void notifyRestoreRoute();
    void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers);
}
+2 −3
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ interface IMediaRouterService {
    void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);

    // Methods for media router 2
    void registerClient2AsUser(IMediaRouter2Client client, String packageName, int userId);
    void registerClient2(IMediaRouter2Client client, String packageName);
    void unregisterClient2(IMediaRouter2Client client);
    void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
    /**
@@ -54,8 +54,7 @@ interface IMediaRouterService {
    void selectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
    void setControlCategories(IMediaRouter2Client client, in List<String> categories);

    void registerManagerAsUser(IMediaRouter2Manager manager,
            String packageName, int userId);
    void registerManager(IMediaRouter2Manager manager, String packageName);
    void unregisterManager(IMediaRouter2Manager manager);
    /**
     * Changes the selected route of an application.
+1 −1
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ public abstract class MediaRoute2ProviderService extends Service {
            return;
        }
        try {
            mClient.notifyProviderInfoUpdated(mProviderInfo);
            mClient.updateProviderInfo(mProviderInfo);
        } catch (RemoteException ex) {
            Log.w(TAG, "Failed to send onProviderInfoUpdated");
        }
+125 −23
Original line number Diff line number Diff line
@@ -16,13 +16,16 @@

package android.media;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
@@ -58,6 +61,12 @@ public class MediaRouter2 {
    private Client mClient;

    private final String mPackageName;
    final Handler mHandler;

    List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList();
    volatile List<MediaRoute2Info> mRoutes = Collections.emptyList();

    MediaRoute2Info mSelectedRoute;

    /**
     * Gets an instance of the media router associated with the context.
@@ -78,6 +87,7 @@ public class MediaRouter2 {
                ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
        mPackageName = mContext.getPackageName();
        //TODO: read control categories from the manifest
        mHandler = new Handler(Looper.getMainLooper());
    }

    /**
@@ -100,25 +110,10 @@ public class MediaRouter2 {
        Objects.requireNonNull(executor, "executor must not be null");
        Objects.requireNonNull(callback, "callback must not be null");

        CallbackRecord record;
        // This is required to prevent adding the same callback twice.
        synchronized (mCallbackRecords) {
            if (mCallbackRecords.size() == 0) {
                synchronized (sLock) {
                    Client client = new Client();
                    try {
                        mMediaRouterService.registerClient2AsUser(client, mPackageName,
                                UserHandle.myUserId());
                        //TODO: We should merge control categories of callbacks.
                        mMediaRouterService.setControlCategories(client, mControlCategories);
                        mClient = client;
                    } catch (RemoteException ex) {
                        Log.e(TAG, "Unable to register media router.", ex);
                    }
                }
            }

            final int index = findCallbackRecordIndexLocked(callback);
            CallbackRecord record;
            if (index < 0) {
                record = new CallbackRecord(callback);
                mCallbackRecords.add(record);
@@ -129,6 +124,20 @@ public class MediaRouter2 {
            record.mFlags = flags;
        }

        synchronized (sLock) {
            if (mClient == null) {
                Client client = new Client();
                try {
                    mMediaRouterService.registerClient2(client, mPackageName);
                    mMediaRouterService.setControlCategories(client, mControlCategories);
                    mClient = client;
                } catch (RemoteException ex) {
                    Log.e(TAG, "Unable to register media router.", ex);
                }
            }
        }
        record.notifyRoutes();

        //TODO: Update discovery request here.
    }

@@ -172,10 +181,9 @@ public class MediaRouter2 {
        Objects.requireNonNull(controlCategories, "control categories must not be null");

        Client client;
        List<String> newControlCategories;
        List<String> newControlCategories = new ArrayList<>(controlCategories);
        synchronized (sLock) {
            mControlCategories = new ArrayList<>(controlCategories);
            newControlCategories = mControlCategories;
            mControlCategories = newControlCategories;
            client = mClient;
        }
        if (client != null) {
@@ -185,8 +193,29 @@ public class MediaRouter2 {
                Log.e(TAG, "Unable to set control categories.", ex);
            }
        }
        mHandler.sendMessage(obtainMessage(MediaRouter2::refreshAndNotifyRoutes, this));
    }

    /**
     * Gets the list of {@link MediaRoute2Info routes} currently known to the media router.
     *
     * @return the list of routes that support at least one of the control categories set by
     * the application
     */
    @NonNull
    public List<MediaRoute2Info> getRoutes() {
        return mRoutes;
    }

    /**
     * Gets the currently selected route.
     *
     * @return the selected route
     */
    @NonNull
    public MediaRoute2Info getSelectedRoute() {
        return mSelectedRoute;
    }

    /**
     * Selects the specified route.
@@ -199,6 +228,7 @@ public class MediaRouter2 {

        Client client;
        synchronized (sLock) {
            mSelectedRoute = route;
            client = mClient;
        }
        if (client != null) {
@@ -247,6 +277,61 @@ public class MediaRouter2 {
        return -1;
    }

    void onProviderInfosUpdated(List<MediaRoute2ProviderInfo> providers) {
        if (providers == null) {
            Log.w(TAG, "Providers info is null.");
            return;
        }

        mProviders = providers;
        refreshAndNotifyRoutes();
    }

    void refreshAndNotifyRoutes() {
        ArrayList<MediaRoute2Info> routes = new ArrayList<>();

        List<String> controlCategories;
        synchronized (sLock) {
            controlCategories = mControlCategories;
        }

        for (MediaRoute2ProviderInfo provider : mProviders) {
            updateProvider(provider, controlCategories, routes);
        }

        //TODO: Can orders be changed?
        if (!Objects.equals(mRoutes, routes)) {
            mRoutes = Collections.unmodifiableList(routes);
            notifyRouteListChanged(mRoutes);
        }
    }

    void updateProvider(MediaRoute2ProviderInfo provider, List<String> controlCategories,
            List<MediaRoute2Info> outRoutes) {
        if (provider == null || !provider.isValid()) {
            Log.w(TAG, "Ignoring invalid provider : " + provider);
        }

        final Collection<MediaRoute2Info> routes = provider.getRoutes();
        for (MediaRoute2Info route : routes) {
            if (!route.isValid()) {
                Log.w(TAG, "Ignoring invalid route : " + route);
                continue;
            }
            if (!route.supportsControlCategory(controlCategories)) {
                continue;
            }
            outRoutes.add(route);
        }
    }

    void notifyRouteListChanged(List<MediaRoute2Info> routes) {
        for (CallbackRecord record: mCallbackRecords) {
            record.mExecutor.execute(
                    () -> record.mCallback.onRoutesChanged(routes));
        }
    }

    /**
     * Interface for receiving events about media routing changes.
     */
@@ -265,9 +350,14 @@ public class MediaRouter2 {
         * Called when a route is removed.
         */
        public void onRouteRemoved(MediaRoute2Info routeInfo) {}

        /**
         * Called when the list of routes is changed.
         */
        public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
    }

    static final class CallbackRecord {
    final class CallbackRecord {
        public final Callback mCallback;
        public Executor mExecutor;
        public int mFlags;
@@ -275,13 +365,25 @@ public class MediaRouter2 {
        CallbackRecord(@NonNull Callback callback) {
            mCallback = callback;
        }

        void notifyRoutes() {
            final List<MediaRoute2Info> routes = mRoutes;
            // notify only when bound to media router service.
            //TODO: Correct the condition when control category, default rotue, .. are finalized.
            if (routes.size() > 0) {
                mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
            }
        }
    }

    class Client extends IMediaRouter2Client.Stub {
        @Override
        public void notifyStateChanged() throws RemoteException {}
        public void notifyRestoreRoute() throws RemoteException {}

        @Override
        public void notifyRestoreRoute() throws RemoteException {}
        public void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> info) {
            mHandler.sendMessage(obtainMessage(MediaRouter2::onProviderInfosUpdated,
                    MediaRouter2.this, info));
        }
    }
}
Loading