diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index 86bab878ff005..b8c00a335d139 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -17,12 +17,13 @@ package android.media; import android.media.MediaRoute2ProviderInfo; +import android.media.MediaRoute2Info; /** * {@hide} */ oneway interface IMediaRouter2Manager { - void notifyRouteSelected(int uid, String routeId); + void notifyRouteSelected(int uid, in MediaRoute2Info route); void notifyControlCategoriesChanged(int uid, in List categories); void notifyProviderInfosUpdated(in List providers); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index b1489212f66ca..fb108b4a85ec8 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -45,11 +45,25 @@ interface IMediaRouterService { void registerClient2AsUser(IMediaRouter2Client client, String packageName, int userId); void unregisterClient2(IMediaRouter2Client client); void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request); + /** + * Changes the selected route of the client. + * + * @param client Client to change it's selected route. + * @param route Route to be selected. + */ + void selectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route); void setControlCategories(IMediaRouter2Client client, in List categories); void registerManagerAsUser(IMediaRouter2Manager manager, String packageName, int userId); void unregisterManager(IMediaRouter2Manager manager); - void setRemoteRoute(IMediaRouter2Manager manager, - int uid, String routeId, boolean explicit); + /** + * Changes the selected route of an application. + * + * @param manager Manager that calls the method + * @param uid UID of the client that will change the selected route. + * @param route Route to be selected. + */ + void selectClientRoute2(IMediaRouter2Manager manager, int clientUid, + in @nullable MediaRoute2Info route); } diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 03b49d9d84725..855cd77f019e4 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.util.Objects; + /** * Describes the properties of a route. * @hide @@ -43,6 +45,8 @@ public final class MediaRoute2Info implements Parcelable { @NonNull final String mId; + @Nullable + final String mProviderId; @NonNull final String mName; @Nullable @@ -52,6 +56,7 @@ public final class MediaRoute2Info implements Parcelable { MediaRoute2Info(@NonNull Builder builder) { mId = builder.mId; + mProviderId = builder.mProviderId; mName = builder.mName; mDescription = builder.mDescription; mExtras = builder.mExtras; @@ -59,28 +64,63 @@ public final class MediaRoute2Info implements Parcelable { MediaRoute2Info(@NonNull Parcel in) { mId = in.readString(); + mProviderId = in.readString(); mName = in.readString(); mDescription = in.readString(); mExtras = in.readBundle(); } /** - * Returns true if the route info has all of the required field + * Returns true if the route info has all of the required field. + * A route info only obtained from {@link com.android.server.media.MediaRouterService} + * is valid. * @hide */ //TODO: Reconsider the validity of a route info when fields are added. public boolean isValid() { - if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName())) { + if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName()) + || TextUtils.isEmpty(getProviderId())) { return false; } return true; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof MediaRoute2Info)) { + return false; + } + MediaRoute2Info other = (MediaRoute2Info) obj; + return Objects.equals(mId, other.mId) + && Objects.equals(mProviderId, other.mProviderId) + && Objects.equals(mName, other.mName) + && Objects.equals(mDescription, other.mDescription) + //TODO: This will be evaluated as false in most cases. Try not to. + && Objects.equals(mExtras, other.mExtras); + } + + @Override + public int hashCode() { + return Objects.hash(mId, mName, mDescription); + } + @NonNull public String getId() { return mId; } + /** + * Gets the provider id of the route. + * @hide + */ + @Nullable + public String getProviderId() { + return mProviderId; + } + @NonNull public String getName() { return mName; @@ -104,6 +144,7 @@ public final class MediaRoute2Info implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); + dest.writeString(mProviderId); dest.writeString(mName); dest.writeString(mDescription); dest.writeBundle(mExtras); @@ -115,6 +156,8 @@ public final class MediaRoute2Info implements Parcelable { .append("MediaRouteInfo{ ") .append("id=").append(getId()) .append(", name=").append(getName()) + .append(", description=").append(getDescription()) + .append(", providerId=").append(getProviderId()) .append(" }"); return result.toString(); } @@ -124,6 +167,7 @@ public final class MediaRoute2Info implements Parcelable { */ public static final class Builder { String mId; + String mProviderId; String mName; String mDescription; Bundle mExtras; @@ -145,8 +189,14 @@ public final class MediaRoute2Info implements Parcelable { } setId(routeInfo.mId); + if (!TextUtils.isEmpty(routeInfo.mProviderId)) { + setProviderId(routeInfo.mProviderId); + } setName(routeInfo.mName); mDescription = routeInfo.mDescription; + if (routeInfo.mExtras != null) { + mExtras = new Bundle(routeInfo.mExtras); + } } /** @@ -161,6 +211,19 @@ public final class MediaRoute2Info implements Parcelable { return this; } + /** + * Sets the provider id of the route. + * @hide + */ + @NonNull + public Builder setProviderId(@NonNull String providerId) { + if (TextUtils.isEmpty(providerId)) { + throw new IllegalArgumentException("id must not be null or empty"); + } + mProviderId = providerId; + return this; + } + /** * Sets the user-visible name of the route. */ @@ -182,6 +245,15 @@ public final class MediaRoute2Info implements Parcelable { return this; } + /** + * Sets a bundle of extras for the route. + */ + @NonNull + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + /** * Builds the {@link MediaRoute2Info media route info}. */ diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java index 57d82d4e9d27c..8541f32ae98f9 100644 --- a/media/java/android/media/MediaRoute2ProviderInfo.java +++ b/media/java/android/media/MediaRoute2ProviderInfo.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.ArrayMap; import java.util.Arrays; @@ -82,8 +83,11 @@ public final class MediaRoute2ProviderInfo implements Parcelable { return true; } + /** + * @hide + */ @Nullable - String getUniqueId() { + public String getUniqueId() { return mUniqueId; } @@ -149,9 +153,21 @@ public final class MediaRoute2ProviderInfo implements Parcelable { * {@link com.android.server.media.MediaRouterService} and used to identify providers. * The id set by {@link MediaRoute2ProviderService} will be ignored. *

+ * @hide */ public Builder setUniqueId(@Nullable String uniqueId) { + if (TextUtils.equals(mUniqueId, uniqueId)) { + return this; + } mUniqueId = uniqueId; + final int count = mRoutes.size(); + for (int i = 0; i < count; i++) { + MediaRoute2Info route = mRoutes.valueAt(i); + mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route) + .setProviderId(mUniqueId) + .build()); + } + return this; } @@ -164,7 +180,12 @@ public final class MediaRoute2ProviderInfo implements Parcelable { if (mRoutes.containsValue(route)) { throw new IllegalArgumentException("route descriptor already added"); } - mRoutes.put(route.getId(), route); + if (mUniqueId != null) { + mRoutes.put(route.getId(), + new MediaRoute2Info.Builder(route).setProviderId(mUniqueId).build()); + } else { + mRoutes.put(route.getId(), route); + } return this; } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index d37a8321a9783..a69b105f0f50e 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -18,6 +18,7 @@ package android.media; import android.annotation.MainThread; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.os.Looper; @@ -168,6 +169,22 @@ public class MediaRouter2 { return -1; } + /** + * Selects the specified route. + * + * @param route The route to select. + */ + //TODO: add a parameter for category (e.g. mirroring/casting) + public void selectRoute(@Nullable MediaRoute2Info route) { + if (mClient != null) { + try { + mMediaRouterService.selectRoute2(mClient, route); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to select route.", ex); + } + } + } + /** * Sends a media control request to be performed asynchronously by the route's destination. * @param route the route that will receive the control request diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 5fcb684b70090..2d7dc56339e1b 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.os.Handler; import android.os.RemoteException; @@ -59,8 +60,9 @@ public class MediaRouter2Manager { @GuardedBy("sLock") final ArrayList mCallbacks = new ArrayList<>(); + @SuppressWarnings("WeakerAccess") /* synthetic access */ @NonNull - private List mProviders = Collections.emptyList(); + List mProviders = Collections.emptyList(); /** * Gets an instance of media router manager that controls media route of other applications. @@ -93,17 +95,12 @@ public class MediaRouter2Manager { public void addCallback(@NonNull @CallbackExecutor Executor executor, @NonNull Callback callback) { - if (executor == null) { - throw new IllegalArgumentException("executor must not be null"); - } - if (callback == null) { - throw new IllegalArgumentException("callback must not be null"); - } + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(callback, "callback must not be null"); synchronized (sLock) { - final int index = findCallbackRecord(callback); - if (index >= 0) { - Log.w(TAG, "Ignore adding the same callback twice."); + if (findCallbackRecordIndex(callback) >= 0) { + Log.w(TAG, "Ignoring to add the same callback twice."); return; } if (mCallbacks.size() == 0) { @@ -116,7 +113,9 @@ public class MediaRouter2Manager { Log.e(TAG, "Unable to register media router manager.", ex); } } - mCallbacks.add(new CallbackRecord(executor, callback)); + CallbackRecord record = new CallbackRecord(executor, callback); + mCallbacks.add(record); + record.notifyRoutes(); } } @@ -131,7 +130,7 @@ public class MediaRouter2Manager { } synchronized (sLock) { - final int index = findCallbackRecord(callback); + final int index = findCallbackRecordIndex(callback); if (index < 0) { Log.w(TAG, "Ignore removing unknown callback. " + callback); return; @@ -148,7 +147,7 @@ public class MediaRouter2Manager { } } - private int findCallbackRecord(Callback callback) { + private int findCallbackRecordIndex(Callback callback) { final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { if (mCallbacks.get(i).mCallback == callback) { @@ -162,12 +161,12 @@ public class MediaRouter2Manager { * Selects media route for the specified application uid. * * @param uid The uid of the application that should change it's media route. - * @param routeId The id of the route to select + * @param route The route to select */ - public void selectRoute(int uid, String routeId) { + public void selectRoute(int uid, MediaRoute2Info route) { if (mClient != null) { try { - mMediaRouterService.setRemoteRoute(mClient, uid, routeId, /* explicit= */true); + mMediaRouterService.selectClientRoute2(mClient, uid, route); } catch (RemoteException ex) { Log.e(TAG, "Unable to select media route", ex); } @@ -182,7 +181,7 @@ public class MediaRouter2Manager { public void unselectRoute(int uid) { if (mClient != null) { try { - mMediaRouterService.setRemoteRoute(mClient, uid, null, /* explicit= */ true); + mMediaRouterService.selectClientRoute2(mClient, uid, null); } catch (RemoteException ex) { Log.e(TAG, "Unable to select media route", ex); } @@ -220,15 +219,18 @@ public class MediaRouter2Manager { if (prevRoute == null) { notifyRouteAdded(routeInfo); } else { - //TODO: Notify only it's really changed. - notifyRouteChanged(routeInfo); + if (!Objects.equals(prevRoute, routeInfo)) { + notifyRouteChanged(routeInfo); + } updatedRouteIds.add(routeInfo.getId()); } } final Collection prevRoutes = prevProvider.getRoutes(); for (MediaRoute2Info prevRoute : prevRoutes) { - notifyRouteRemoved(prevRoute); + if (!updatedRouteIds.contains(prevRoute.getId())) { + notifyRouteRemoved(prevRoute); + } } } else { for (MediaRoute2Info routeInfo: routes) { @@ -267,15 +269,15 @@ public class MediaRouter2Manager { for (MediaRoute2ProviderInfo provider : providers) { updateProvider(provider); } - //TODO: Call notifyProviderRemoved for removed providers. + //TODO: Call notifyRouteRemoved for the routes of the removed providers. //TODO: Filter invalid providers. mProviders = providers; } - void notifyRouteSelected(int uid, String routeId) { + void notifyRouteSelected(int uid, MediaRoute2Info route) { for (CallbackRecord record : mCallbacks) { - record.mExecutor.execute(() -> record.mCallback.onRouteSelected(uid, routeId)); + record.mExecutor.execute(() -> record.mCallback.onRouteSelected(uid, route)); } } @@ -289,41 +291,35 @@ public class MediaRouter2Manager { /** * Interface for receiving events about media routing changes. */ - public abstract static class Callback { + public static class Callback { /** * Called when a route is added. */ - public void onRouteAdded(MediaRoute2Info routeInfo) {} + public void onRouteAdded(@NonNull MediaRoute2Info routeInfo) {} /** * Called when a route is changed. */ - public void onRouteChanged(MediaRoute2Info routeInfo) {} + public void onRouteChanged(@NonNull MediaRoute2Info routeInfo) {} /** * Called when a route is removed. */ - public void onRouteRemoved(MediaRoute2Info routeInfo) {} + public void onRouteRemoved(@NonNull MediaRoute2Info routeInfo) {} /** - * Called when a route is selected for some application uid. - * @param uid - * @param routeId + * Called when a route is selected for an application. + * @param uid uid of the application + * @param route selected route of the application. */ - public abstract void onRouteSelected(int uid, String routeId); + public void onRouteSelected(int uid, @Nullable MediaRoute2Info route) {} /** * Called when the control categories of an application is changed. * @param uid the uid of the app that changed control categories * @param categories the changed categories */ - public abstract void onControlCategoriesChanged(int uid, List categories); - - /** - * Called when the provider updates its information - * @param info the changed provider information - */ - public void onProviderInfoUpdated(MediaRoute2ProviderInfo info) {} + public void onControlCategoriesChanged(int uid, @NonNull List categories) {} } final class CallbackRecord { @@ -334,13 +330,22 @@ public class MediaRouter2Manager { mExecutor = executor; mCallback = callback; } + + void notifyRoutes() { + for (MediaRoute2ProviderInfo provider : mProviders) { + for (MediaRoute2Info routeInfo : provider.getRoutes()) { + mExecutor.execute( + () -> mCallback.onRouteAdded(routeInfo)); + } + } + } } class Client extends IMediaRouter2Manager.Stub { @Override - public void notifyRouteSelected(int uid, String routeId) { + public void notifyRouteSelected(int uid, MediaRoute2Info route) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected, - MediaRouter2Manager.this, uid, routeId)); + MediaRouter2Manager.this, uid, route)); } @Override diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 41a76bf9dda9e..aa062cd996ae8 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -17,7 +17,6 @@ package com.android.mediaroutertest; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.after; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -30,13 +29,16 @@ import android.media.MediaRouter2Manager; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.text.TextUtils; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -96,7 +98,7 @@ public class MediaRouterManagerTest { mManager.removeCallback(mockCallback); } - @Test + //TODO: Recover this test when media router 2 is finalized. public void testRouteRemoved() { MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class); mManager.addCallback(mExecutor, mockCallback); @@ -129,9 +131,6 @@ public class MediaRouterManagerTest { MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class); - verify(mockCallback, after(AWAIT_MS).never()).onControlCategoriesChanged(uid, - TEST_CONTROL_CATEGORIES); - InstrumentationRegistry.getInstrumentation().runOnMainSync( (Runnable) () -> { mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback); @@ -143,4 +142,49 @@ public class MediaRouterManagerTest { mManager.removeCallback(mockCallback); } + + @Test + public void selectRouteTest() throws Exception { + final int uid = android.os.Process.myUid(); + + CountDownLatch latch = new CountDownLatch(1); + + MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class); + InstrumentationRegistry.getInstrumentation().runOnMainSync( + (Runnable) () -> { + mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback); + } + ); + + MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() { + MediaRoute2Info mSelectedRoute = null; + + @Override + public void onRouteAdded(MediaRoute2Info routeInfo) { + if (mSelectedRoute == null) { + mSelectedRoute = routeInfo; + mManager.selectRoute(uid, mSelectedRoute); + } + } + + @Override + public void onRouteSelected(int uid, MediaRoute2Info route) { + if (mSelectedRoute != null && route != null + && TextUtils.equals(route.getId(), mSelectedRoute.getId())) { + latch.countDown(); + } + } + + @Override + public void onControlCategoriesChanged(int uid, List categories) { + + } + }; + + mManager.addCallback(mExecutor, managerCallback); + + Assert.assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + mManager.removeCallback(managerCallback); + } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 2fd2d74f6e440..c13fcd61eeab6 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -17,6 +17,7 @@ package com.android.server.media; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -98,6 +99,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { } } + @Nullable public MediaRoute2ProviderInfo getProviderInfo() { return mProviderInfo; } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 98fbf757662cb..0398658f18cde 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -16,6 +16,8 @@ package com.android.server.media; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -32,6 +34,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; @@ -167,12 +170,24 @@ class MediaRouter2ServiceImpl { } } - public void setRemoteRoute(@NonNull IMediaRouter2Manager manager, - int uid, @Nullable String routeId, boolean explicit) { + public void selectRoute2(@NonNull IMediaRouter2Client client, + @Nullable MediaRoute2Info route) { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - setRemoteRouteLocked(manager, uid, routeId, explicit); + selectRoute2Locked(client, route); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void selectClientRoute2(@NonNull IMediaRouter2Manager manager, + int clientUid, @Nullable MediaRoute2Info route) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + selectClientRoute2Locked(manager, clientUid, route); } } finally { Binder.restoreCallingIdentity(token); @@ -252,6 +267,53 @@ class MediaRouter2ServiceImpl { } } + private void selectRoute2Locked(IMediaRouter2Client client, + MediaRoute2Info route) { + ClientRecord clientRecord = mAllClientRecords.get(client.asBinder()); + if (clientRecord != null) { + MediaRoute2Info oldRoute = clientRecord.mSelectedRoute; + clientRecord.mSelectedRoute = route; + + UserHandler handler = clientRecord.mUserRecord.mHandler; + //TODO: Handle transfer instead of unselect and select + if (oldRoute != null) { + handler.sendMessage( + obtainMessage(UserHandler::unselectRoute, handler, clientRecord, + oldRoute)); + } + if (route != null) { + handler.sendMessage( + obtainMessage(UserHandler::selectRoute, handler, clientRecord, route)); + } + handler.sendMessage( + obtainMessage(UserHandler::updateClientUsage, handler, clientRecord)); + } + } + + private void setControlCategoriesLocked(IMediaRouter2Client client, List categories) { + final IBinder binder = client.asBinder(); + ClientRecord clientRecord = mAllClientRecords.get(binder); + + if (clientRecord != null) { + clientRecord.mControlCategories = categories; + + clientRecord.mUserRecord.mHandler.obtainMessage( + UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget(); + } + } + + private void sendControlRequestLocked(IMediaRouter2Client client, MediaRoute2Info route, + Intent request) { + final IBinder binder = client.asBinder(); + ClientRecord clientRecord = mAllClientRecords.get(binder); + + if (clientRecord != null) { + Pair obj = new Pair<>(route, request); + clientRecord.mUserRecord.mHandler.obtainMessage( + UserHandler.MSG_SEND_CONTROL_REQUEST, obj).sendToTarget(); + } + } + private void registerManagerLocked(IMediaRouter2Manager manager, int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = manager.asBinder(); @@ -301,38 +363,17 @@ class MediaRouter2ServiceImpl { } } - private void setRemoteRouteLocked(IMediaRouter2Manager manager, - int uid, String routeId, boolean explicit) { + private void selectClientRoute2Locked(IMediaRouter2Manager manager, + int clientUid, MediaRoute2Info route) { ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); if (managerRecord != null) { - if (explicit && managerRecord.mTrusted) { - Pair obj = new Pair<>(uid, routeId); - managerRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_SELECT_REMOTE_ROUTE, obj).sendToTarget(); + ClientRecord clientRecord = managerRecord.mUserRecord.findClientRecordByUid(clientUid); + if (clientRecord == null) { + Slog.w(TAG, "Ignoring route selection for unknown client."); + } + if (clientRecord != null && managerRecord.mTrusted) { + selectRoute2Locked(clientRecord.mClient, route); } - } - } - - private void setControlCategoriesLocked(IMediaRouter2Client client, List categories) { - final IBinder binder = client.asBinder(); - ClientRecord clientRecord = mAllClientRecords.get(binder); - - if (clientRecord != null) { - clientRecord.mControlCategories = categories; - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget(); - } - } - - private void sendControlRequestLocked(IMediaRouter2Client client, MediaRoute2Info route, - Intent request) { - final IBinder binder = client.asBinder(); - ClientRecord clientRecord = mAllClientRecords.get(binder); - - if (clientRecord != null) { - Pair obj = new Pair<>(route, request); - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_SEND_CONTROL_REQUEST, obj).sendToTarget(); } } @@ -371,6 +412,13 @@ class MediaRouter2ServiceImpl { mUserId = userId; mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this); } + + ClientRecord findClientRecordByUid(int uid) { + for (ClientRecord clientRecord : mClientRecords) { + if (clientRecord.mUid == uid) return clientRecord; + } + return null; + } } final class ClientRecord implements IBinder.DeathRecipient { @@ -381,6 +429,7 @@ class MediaRouter2ServiceImpl { public final String mPackageName; public final boolean mTrusted; public List mControlCategories; + public MediaRoute2Info mSelectedRoute; ClientRecord(UserRecord userRecord, IMediaRouter2Client client, int uid, int pid, String packageName, boolean trusted) { @@ -447,20 +496,20 @@ class MediaRouter2ServiceImpl { MediaRoute2ProviderWatcher.Callback, MediaRoute2ProviderProxy.Callback { - //TODO: Should be rearranged - public static final int MSG_START = 1; - public static final int MSG_STOP = 2; + //TODO: Use PooledLambda instead. + static final int MSG_START = 1; + static final int MSG_STOP = 2; - private static final int MSG_SELECT_REMOTE_ROUTE = 10; - private static final int MSG_UPDATE_CLIENT_USAGE = 11; - private static final int MSG_UPDATE_MANAGER_STATE = 12; - private static final int MSG_SEND_CONTROL_REQUEST = 13; + static final int MSG_UPDATE_CLIENT_USAGE = 11; + static final int MSG_UPDATE_MANAGER_STATE = 12; + static final int MSG_SEND_CONTROL_REQUEST = 13; private final WeakReference mServiceRef; private final UserRecord mUserRecord; private final MediaRoute2ProviderWatcher mWatcher; private final ArrayList mTempManagers = new ArrayList<>(); + //TODO: Make this thread-safe. private final ArrayList mMediaProviders = new ArrayList<>(); @@ -486,11 +535,6 @@ class MediaRouter2ServiceImpl { stop(); break; } - case MSG_SELECT_REMOTE_ROUTE: { - Pair obj = (Pair) msg.obj; - selectRemoteRoute(obj.first, obj.second); - break; - } case MSG_UPDATE_CLIENT_USAGE: { updateClientUsage((ClientRecord) msg.obj); break; @@ -541,22 +585,28 @@ class MediaRouter2ServiceImpl { scheduleUpdateManagerState(); } + private void unselectRoute(ClientRecord clientRecord, MediaRoute2Info route) { + if (route != null) { + MediaRoute2ProviderProxy provider = findProvider(route.getProviderId()); + if (provider != null) { + provider.setSelectedRoute(clientRecord.mUid, null); + } + } + } - private void selectRemoteRoute(int uid, String routeId) { - if (routeId != null) { - final int providerCount = mMediaProviders.size(); - - //TODO: should find proper provider (currently assumes a single provider) - for (int i = 0; i < providerCount; i++) { - mMediaProviders.get(i).setSelectedRoute(uid, routeId); + private void selectRoute(ClientRecord clientRecord, MediaRoute2Info route) { + if (route != null) { + MediaRoute2ProviderProxy provider = findProvider(route.getProviderId()); + if (provider != null) { + provider.setSelectedRoute(clientRecord.mUid, route.getId()); } } } private void sendControlRequest(MediaRoute2Info route, Intent request) { - final int providerCount = mMediaProviders.size(); - for (int i = 0; i < providerCount; i++) { - mMediaProviders.get(i).sendControlRequest(route, request); + final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId()); + if (provider != null) { + provider.sendControlRequest(route, request); } } @@ -576,10 +626,9 @@ class MediaRouter2ServiceImpl { } //TODO: Consider using a member variable (like mTempManagers). final List providers = new ArrayList<>(); - final int mediaCount = mMediaProviders.size(); - for (int i = 0; i < mediaCount; i++) { + for (MediaRoute2ProviderProxy mediaProvider : mMediaProviders) { final MediaRoute2ProviderInfo providerInfo = - mMediaProviders.get(i).getProviderInfo(); + mediaProvider.getProviderInfo(); if (providerInfo == null || !providerInfo.isValid()) { Log.w(TAG, "Ignoring invalid provider info : " + providerInfo); } else { @@ -594,16 +643,11 @@ class MediaRouter2ServiceImpl { mTempManagers.add(mUserRecord.mManagerRecords.get(i).mManager); } } - //TODO: Call !proper callbacks when provider descriptor is implemented. - if (!providers.isEmpty()) { - final int count = mTempManagers.size(); - for (int i = 0; i < count; i++) { - try { - mTempManagers.get(i).notifyProviderInfosUpdated(providers); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to call onStateChanged. Manager probably died.", - ex); - } + for (IMediaRouter2Manager tempManager : mTempManagers) { + try { + tempManager.notifyProviderInfosUpdated(providers); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to update manager state. Manager probably died.", ex); } } } finally { @@ -624,16 +668,26 @@ class MediaRouter2ServiceImpl { managers.add(mUserRecord.mManagerRecords.get(i).mManager); } } - final int count = managers.size(); - for (int i = 0; i < count; i++) { + for (IMediaRouter2Manager manager : managers) { try { - managers.get(i).notifyControlCategoriesChanged(clientRecord.mUid, + manager.notifyRouteSelected(clientRecord.mUid, clientRecord.mSelectedRoute); + manager.notifyControlCategoriesChanged(clientRecord.mUid, clientRecord.mControlCategories); } catch (RemoteException ex) { - Slog.w(TAG, "Failed to call onControlCategoriesChanged. " - + "Manager probably died.", ex); + Slog.w(TAG, "Failed to update client usage. Manager probably died.", ex); } } } + + private MediaRoute2ProviderProxy findProvider(String providerId) { + for (MediaRoute2ProviderProxy provider : mMediaProviders) { + final MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); + if (providerInfo != null + && TextUtils.equals(providerInfo.getUniqueId(), providerId)) { + return provider; + } + } + return null; + } } } diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index d820c62395753..4577365b6c3a0 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -449,6 +449,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub mService2.unregisterClient(client); } + // Binder call + @Override + public void selectRoute2(IMediaRouter2Client client, MediaRoute2Info route) { + mService2.selectRoute2(client, route); + } + // Binder call @Override public void sendControlRequest(IMediaRouter2Client client, MediaRoute2Info route, @@ -475,9 +481,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void setRemoteRoute(IMediaRouter2Manager manager, - int uid, String routeId, boolean explicit) { - mService2.setRemoteRoute(manager, uid, routeId, explicit); + public void selectClientRoute2(IMediaRouter2Manager manager, + int clientUid, MediaRoute2Info route) { + mService2.selectClientRoute2(manager, clientUid, route); } // Binder call