From cb8894dbc76351ddb284eed7b0385d5aa66ccf44 Mon Sep 17 00:00:00 2001 From: Kyunglyul Hyun Date: Fri, 27 Dec 2019 14:24:46 +0900 Subject: [PATCH] MediaRouter: add session related apis into provider This CL adds session-related apis into MediaRouteProvider so MediaRouterService can call them. This CL also removes methods related to "route selection". Test: atest mediaroutertest Change-Id: I68c2cde91d305315e95e5d9ab1d6d23459c28c9d --- .../android/media/IMediaRoute2Provider.aidl | 12 +- .../media/IMediaRoute2ProviderClient.aidl | 2 - .../android/media/IMediaRouterService.aidl | 12 +- .../media/MediaRoute2ProviderService.java | 144 +++++++------- .../android/media/MediaRouter2Manager.java | 30 +-- .../java/android/media/RouteSessionInfo.java | 55 +++++- .../SampleMediaRoute2ProviderService.java | 86 +++++--- .../MediaRouterManagerTest.java | 25 ++- .../server/media/MediaRoute2Provider.java | 12 +- .../media/MediaRoute2ProviderProxy.java | 83 ++++---- .../server/media/MediaRouter2ServiceImpl.java | 187 +++--------------- .../server/media/MediaRouterService.java | 7 +- .../media/SystemMediaRoute2Provider.java | 25 +-- 13 files changed, 295 insertions(+), 385 deletions(-) diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl index a03aa868fd84f..abb2040bda67a 100644 --- a/media/java/android/media/IMediaRoute2Provider.aidl +++ b/media/java/android/media/IMediaRoute2Provider.aidl @@ -24,10 +24,14 @@ import android.media.IMediaRoute2ProviderClient; */ oneway interface IMediaRoute2Provider { void setClient(IMediaRoute2ProviderClient client); - void requestCreateSession(String packageName, String routeId, String controlCategory, - int requestId); - void requestSelectRoute(String packageName, String id, int seq); - void unselectRoute(String packageName, String id); + void requestCreateSession(String packageName, String routeId, + String controlCategory, int requestId); + void releaseSession(int sessionId); + + void addRoute(int sessionId, String routeId); + void removeRoute(int sessionId, String routeId); + void transferRoute(int sessionId, String routeId); + void notifyControlRequestSent(String id, in Intent request); void requestSetVolume(String id, int volume); void requestUpdateVolume(String id, int delta); diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl index 657f7039cf241..ce5fa8cd45042 100644 --- a/media/java/android/media/IMediaRoute2ProviderClient.aidl +++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl @@ -26,7 +26,5 @@ import android.os.Bundle; */ oneway interface IMediaRoute2ProviderClient { void updateProviderInfo(in MediaRoute2ProviderInfo info); - void notifyRouteSelected(String packageName, String routeId, in @nullable Bundle controlHints, - int seq); void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index faf25631a167d..86fa20f1cca97 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -55,15 +55,9 @@ interface IMediaRouterService { void registerManager(IMediaRouter2Manager manager, String packageName); void unregisterManager(IMediaRouter2Manager manager); - /** - * Changes the selected route of an application. - * - * @param manager the manager that calls the method - * @param packageName the package name of the client that will change the selected route - * @param route the route to be selected - */ - void selectClientRoute2(IMediaRouter2Manager manager, String packageName, - in @nullable MediaRoute2Info route); + + void requestCreateClientSession(IMediaRouter2Manager manager, String packageName, + in @nullable MediaRoute2Info route, int requestId); void requestSetVolume2Manager(IMediaRouter2Manager manager, in MediaRoute2Info route, int volume); diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 387b042bb891e..fc741c67f3902 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -22,10 +22,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; import android.content.Intent; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; @@ -47,7 +49,6 @@ public abstract class MediaRoute2ProviderService extends Service { private final Handler mHandler; private final Object mSessionLock = new Object(); private ProviderStub mStub; - // TODO: Rename this to mService (and accordingly IMediaRoute2ProviderClient to something else) private IMediaRoute2ProviderClient mClient; private MediaRoute2ProviderInfo mProviderInfo; @@ -70,28 +71,6 @@ public abstract class MediaRoute2ProviderService extends Service { return null; } - /** - * Called when selectRoute is called on a route of the provider. - * Once the route is ready to be used , call {@link #notifyRouteSelected(SelectToken, Bundle)} - * to notify that. - * - * @param packageName the package name of the application that selected the route - * @param routeId the id of the route being selected - * @param token token that contains select info - * - * @see #notifyRouteSelected - */ - public abstract void onSelectRoute(@NonNull String packageName, @NonNull String routeId, - @NonNull SelectToken token); - - /** - * Called when unselectRoute is called on a route of the provider. - * - * @param packageName the package name of the application that has selected the route. - * @param routeId the id of the route being unselected - */ - public abstract void onUnselectRoute(@NonNull String packageName, @NonNull String routeId); - /** * Called when sendControlRequest is called on a route of the provider * @@ -155,6 +134,8 @@ public abstract class MediaRoute2ProviderService extends Service { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); int sessionId = sessionInfo.getSessionId(); + //TODO: notify updated session info + synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); @@ -162,6 +143,9 @@ public abstract class MediaRoute2ProviderService extends Service { Log.w(TAG, "Ignoring unknown session info."); } } + if (sessionInfo.getSelectedRoutes().isEmpty()) { + releaseSession(sessionId); + } } /** @@ -169,15 +153,20 @@ public abstract class MediaRoute2ProviderService extends Service { * controlled, pass a {@link Bundle} that contains how to control it. * * @param sessionInfo information of the new session. - * Pass {@code null} to reject the request or inform clients that - * session creation has failed. + * The {@link RouteSessionInfo#getSessionId() id} of the session must be + * unique. Pass {@code null} to reject the request or inform clients that + * session creation is failed. * @param requestId id of the previous request to create this session */ //TODO: fail reason? public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) { - //TODO: validate sessionInfo.getSessionId() (it must be in "waiting list") if (sessionInfo != null) { + int sessionId = sessionInfo.getSessionId(); synchronized (mSessionLock) { + if (mSessionInfo.containsKey(sessionId)) { + Log.w(TAG, "Ignoring duplicate session id."); + return; + } mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo); } } @@ -200,6 +189,7 @@ public abstract class MediaRoute2ProviderService extends Service { * @see #onDestroySession(int, RouteSessionInfo) */ public final void releaseSession(int sessionId) { + //TODO: notify media router service of release. RouteSessionInfo sessionInfo; synchronized (mSessionLock) { sessionInfo = mSessionInfo.put(sessionId, null); @@ -283,29 +273,6 @@ public abstract class MediaRoute2ProviderService extends Service { publishState(); } - /** - * Notifies the client of that the selected route is ready for use. If the selected route can be - * controlled, pass a {@link Bundle} that contains how to control it. - * - * @param token token passed in {@link #onSelectRoute} - * @param controlHints a {@link Bundle} that contains how to control the given route. - * Pass {@code null} if the route is not available. - */ - public final void notifyRouteSelected(@NonNull SelectToken token, - @Nullable Bundle controlHints) { - Objects.requireNonNull(token, "token must not be null"); - - if (mClient == null) { - return; - } - try { - mClient.notifyRouteSelected(token.mPackageName, token.mRouteId, - controlHints, token.mSeq); - } catch (RemoteException ex) { - Log.w(TAG, "Failed to notify route selected"); - } - } - void setClient(IMediaRoute2ProviderClient client) { mClient = client; publishState(); @@ -323,68 +290,91 @@ public abstract class MediaRoute2ProviderService extends Service { } } - /** - * Route selection information. - * - * @see #notifyRouteSelected - */ - public final class SelectToken { - final String mPackageName; - final String mRouteId; - final int mSeq; - - SelectToken(String packageName, String routeId, int seq) { - mPackageName = packageName; - mRouteId = routeId; - mSeq = seq; - } - } - final class ProviderStub extends IMediaRoute2Provider.Stub { ProviderStub() { } + boolean checkCallerisSystem() { + return Binder.getCallingUid() == Process.SYSTEM_UID; + } + @Override public void setClient(IMediaRoute2ProviderClient client) { + if (!checkCallerisSystem()) { + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setClient, MediaRoute2ProviderService.this, client)); } @Override - public void requestCreateSession(String packageName, String routeId, String controlCategory, - int requestId) { + public void requestCreateSession(String packageName, String routeId, + String controlCategory, int requestId) { + if (!checkCallerisSystem()) { + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession, MediaRoute2ProviderService.this, packageName, routeId, controlCategory, requestId)); } - @Override - public void requestSelectRoute(String packageName, String routeId, int seq) { - //TODO: call onCreateSession instead - mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute, - MediaRoute2ProviderService.this, packageName, routeId, - new SelectToken(packageName, routeId, seq))); + public void releaseSession(int sessionId) { + if (!checkCallerisSystem()) { + return; + } + mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession, + MediaRoute2ProviderService.this, sessionId)); } @Override - public void unselectRoute(String packageName, String routeId) { - mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUnselectRoute, - MediaRoute2ProviderService.this, packageName, routeId)); + public void addRoute(int sessionId, String routeId) { + if (!checkCallerisSystem()) { + return; + } + mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onAddRoute, + MediaRoute2ProviderService.this, sessionId, routeId)); + } + + @Override + public void removeRoute(int sessionId, String routeId) { + if (!checkCallerisSystem()) { + return; + } + mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onRemoveRoute, + MediaRoute2ProviderService.this, sessionId, routeId)); + } + + @Override + public void transferRoute(int sessionId, String routeId) { + if (!checkCallerisSystem()) { + return; + } + mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferRoute, + MediaRoute2ProviderService.this, sessionId, routeId)); } @Override public void notifyControlRequestSent(String routeId, Intent request) { + if (!checkCallerisSystem()) { + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest, MediaRoute2ProviderService.this, routeId, request)); } @Override public void requestSetVolume(String routeId, int volume) { + if (!checkCallerisSystem()) { + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume, MediaRoute2ProviderService.this, routeId, volume)); } @Override public void requestUpdateVolume(String routeId, int delta) { + if (!checkCallerisSystem()) { + return; + } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume, MediaRoute2ProviderService.this, routeId, delta)); } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 5de337079581f..f67733631ae5b 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Handler; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; @@ -40,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; /** * @hide @@ -66,6 +68,8 @@ public class MediaRouter2Manager { @NonNull final ConcurrentMap> mControlCategoryMap = new ConcurrentHashMap<>(); + private AtomicInteger mNextRequestId = new AtomicInteger(1); + /** * Gets an instance of media router manager that controls media route of other applications. * @@ -204,7 +208,7 @@ public class MediaRouter2Manager { * Selects media route for the specified package name. * * @param packageName the package name of the application that should change it's media route - * @param route the route to be selected + * @param route the route to be selected. */ public void selectRoute(@NonNull String packageName, @NonNull MediaRoute2Info route) { Objects.requireNonNull(packageName, "packageName must not be null"); @@ -216,26 +220,10 @@ public class MediaRouter2Manager { } if (client != null) { try { - mMediaRouterService.selectClientRoute2(client, packageName, route); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to select media route", ex); - } - } - } - - /** - * Unselects media route for the specified package name. - * - * @param packageName the package name of the application that should stop routing - */ - public void unselectRoute(@NonNull String packageName) { - Client client; - synchronized (sLock) { - client = mClient; - } - if (client != null) { - try { - mMediaRouterService.selectClientRoute2(client, packageName, null); + //TODO: make request id globally unique + int requestId = Process.myPid() * 10000 + mNextRequestId.getAndIncrement(); + mMediaRouterService.requestCreateClientSession( + client, packageName, route, requestId); } catch (RemoteException ex) { Log.e(TAG, "Unable to select media route", ex); } diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java index c22fc4080f398..4fd2789457ba8 100644 --- a/media/java/android/media/RouteSessionInfo.java +++ b/media/java/android/media/RouteSessionInfo.java @@ -49,10 +49,13 @@ public class RouteSessionInfo implements Parcelable { final int mSessionId; final String mPackageName; final String mControlCategory; + @Nullable + final String mProviderId; final List mSelectedRoutes; final List mDeselectableRoutes; final List mGroupableRoutes; final List mTransferrableRoutes; + @Nullable final Bundle mControlHints; RouteSessionInfo(@NonNull Builder builder) { @@ -61,6 +64,7 @@ public class RouteSessionInfo implements Parcelable { mSessionId = builder.mSessionId; mPackageName = builder.mPackageName; mControlCategory = builder.mControlCategory; + mProviderId = builder.mProviderId; mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes); mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes); @@ -76,6 +80,7 @@ public class RouteSessionInfo implements Parcelable { mSessionId = src.readInt(); mPackageName = ensureString(src.readString()); mControlCategory = ensureString(src.readString()); + mProviderId = src.readString(); mSelectedRoutes = ensureList(src.createStringArrayList()); mDeselectableRoutes = ensureList(src.createStringArrayList()); @@ -116,6 +121,14 @@ public class RouteSessionInfo implements Parcelable { return mSessionId; } + /** + * Gets the client package name of the session + */ + @NonNull + public String getPackageName() { + return mPackageName; + } + /** * Gets the control category of the session. * Routes that don't support the category can't be added to the session. @@ -125,6 +138,15 @@ public class RouteSessionInfo implements Parcelable { return mControlCategory; } + /** + * Gets the provider id of the session. + * @hide + */ + @Nullable + public String getProviderId() { + return mProviderId; + } + /** * Gets the list of ids of selected routes for the session. It shouldn't be empty. */ @@ -175,6 +197,7 @@ public class RouteSessionInfo implements Parcelable { dest.writeInt(mSessionId); dest.writeString(mPackageName); dest.writeString(mControlCategory); + dest.writeString(mProviderId); dest.writeStringList(mSelectedRoutes); dest.writeStringList(mDeselectableRoutes); dest.writeStringList(mGroupableRoutes); @@ -187,13 +210,20 @@ public class RouteSessionInfo implements Parcelable { StringBuilder result = new StringBuilder() .append("RouteSessionInfo{ ") .append("sessionId=").append(mSessionId) - .append(", selectedRoutes={"); - for (int i = 0; i < mSelectedRoutes.size(); i++) { - if (i > 0) result.append(", "); - result.append(mSelectedRoutes.get(i)); - } - result.append("}"); - result.append(" }"); + .append(", controlCategory=").append(mControlCategory) + .append(", selectedRoutes={") + .append(String.join(",", mSelectedRoutes)) + .append("}") + .append(", deselectableRoutes={") + .append(String.join(",", mDeselectableRoutes)) + .append("}") + .append(", groupableRoutes={") + .append(String.join(",", mGroupableRoutes)) + .append("}") + .append(", transferrableRoutes={") + .append(String.join(",", mTransferrableRoutes)) + .append("}") + .append(" }"); return result.toString(); } @@ -204,6 +234,7 @@ public class RouteSessionInfo implements Parcelable { final String mPackageName; final int mSessionId; final String mControlCategory; + String mProviderId; final List mSelectedRoutes; final List mDeselectableRoutes; final List mGroupableRoutes; @@ -227,6 +258,7 @@ public class RouteSessionInfo implements Parcelable { mSessionId = sessionInfo.mSessionId; mPackageName = sessionInfo.mPackageName; mControlCategory = sessionInfo.mControlCategory; + mProviderId = sessionInfo.mProviderId; mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes); mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes); @@ -236,6 +268,15 @@ public class RouteSessionInfo implements Parcelable { mControlHints = sessionInfo.mControlHints; } + /** + * Sets the provider id of the session. + */ + @NonNull + public Builder setProviderId(String providerId) { + mProviderId = providerId; + return this; + } + /** * Clears the selected routes. */ diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index d04c9b22fbdfb..36a02c80e3faf 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -24,7 +24,6 @@ import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; import android.media.RouteSessionInfo; -import android.os.Bundle; import android.os.IBinder; import android.text.TextUtils; @@ -62,6 +61,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService public static final int SESSION_ID_1 = 1000; Map mRoutes = new HashMap<>(); + Map mRouteSessionMap = new HashMap<>(); private void initializeRoutes() { MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1) @@ -112,31 +112,6 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService return super.onBind(intent); } - @Override - public void onSelectRoute(String packageName, String routeId, SelectToken token) { - MediaRoute2Info route = mRoutes.get(routeId); - if (route == null) { - return; - } - mRoutes.put(routeId, new MediaRoute2Info.Builder(route) - .setClientPackageName(packageName) - .build()); - publishRoutes(); - notifyRouteSelected(token, Bundle.EMPTY); - } - - @Override - public void onUnselectRoute(String packageName, String routeId) { - MediaRoute2Info route = mRoutes.get(routeId); - if (route == null) { - return; - } - mRoutes.put(routeId, new MediaRoute2Info.Builder(route) - .setClientPackageName(null) - .build()); - publishRoutes(); - } - @Override public void onControlRequest(String routeId, Intent request) { String action = request.getAction(); @@ -180,26 +155,56 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onCreateSession(String packageName, String routeId, String controlCategory, int requestId) { - if (TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { + MediaRoute2Info route = mRoutes.get(routeId); + if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { // Tell the router that session cannot be created by passing null as sessionInfo. notifySessionCreated(/* sessionInfo= */ null, requestId); return; } + maybeRemoveRoute(routeId); + + int sessionId = SESSION_ID_1; + + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setClientPackageName(packageName) + .build()); + mRouteSessionMap.put(routeId, sessionId); RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( - SESSION_ID_1, packageName, controlCategory) + sessionId, packageName, controlCategory) .addSelectedRoute(routeId) .build(); - notifySessionCreated(sessionInfo, requestId); + notifySessionCreated(sessionInfo, requestId); + publishRoutes(); } @Override - public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {} + public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) { + for (String routeId : lastSessionInfo.getSelectedRoutes()) { + mRouteSessionMap.remove(routeId); + MediaRoute2Info route = mRoutes.get(routeId); + if (route != null) { + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setClientPackageName(null) + .build()); + } + } + } @Override public void onAddRoute(int sessionId, String routeId) { RouteSessionInfo sessionInfo = getSessionInfo(sessionId); - //TODO: we may want to remove route if it belongs to another session + MediaRoute2Info route = mRoutes.get(routeId); + if (route == null || sessionInfo == null) { + return; + } + maybeRemoveRoute(routeId); + + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setClientPackageName(sessionInfo.getPackageName()) + .build()); + mRouteSessionMap.put(routeId, sessionId); + RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) .addSelectedRoute(routeId) .build(); @@ -210,6 +215,16 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onRemoveRoute(int sessionId, String routeId) { RouteSessionInfo sessionInfo = getSessionInfo(sessionId); + MediaRoute2Info route = mRoutes.get(routeId); + + mRouteSessionMap.remove(routeId); + if (sessionInfo == null || route == null) { + return; + } + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setClientPackageName(null) + .build()); + RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) .removeSelectedRoute(routeId) .build(); @@ -228,6 +243,15 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService publishRoutes(); } + void maybeRemoveRoute(String routeId) { + if (!mRouteSessionMap.containsKey(routeId)) { + return; + } + + int sessionId = mRouteSessionMap.get(routeId); + onRemoveRoute(sessionId, routeId); + } + void publishRoutes() { MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder() .addRoutes(mRoutes.values()) diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 9761e6d41a59d..728745a9f94df 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -226,7 +226,8 @@ public class MediaRouterManagerTest { mManager.selectRoute(mPackageName, routeToSelect); assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); } finally { - mManager.unselectRoute(mPackageName); + //TODO: release the session + //mManager.selectRoute(mPackageName, null); } } @@ -234,7 +235,7 @@ public class MediaRouterManagerTest { * Tests if MR2Manager.Callback.onRouteSelected is called * when a route is selected by MR2Manager. */ - @Test + //TODO: test session created callback instead of onRouteSelected public void testManagerOnRouteSelected() throws Exception { CountDownLatch latch = new CountDownLatch(1); Map routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); @@ -257,14 +258,14 @@ public class MediaRouterManagerTest { mManager.selectRoute(mPackageName, routeToSelect); assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); } finally { - mManager.unselectRoute(mPackageName); + //TODO: release the session + //mManager.selectRoute(mPackageName, null); } } - @Test + //TODO: enable this when "releasing session" is implemented public void testGetActiveRoutes() throws Exception { CountDownLatch latch = new CountDownLatch(1); - CountDownLatch latch2 = new CountDownLatch(1); Map routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); addRouterCallback(new RouteCallback()); @@ -278,6 +279,7 @@ public class MediaRouterManagerTest { } }); + //TODO: it fails due to not releasing session assertEquals(0, mManager.getActiveRoutes().size()); mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)); @@ -285,17 +287,20 @@ public class MediaRouterManagerTest { assertEquals(1, mManager.getActiveRoutes().size()); + //TODO: release the session + /* awaitOnRouteChangedManager( - () -> mManager.unselectRoute(mPackageName), + () -> mManager.selectRoute(mPackageName, null), ROUTE_ID1, route -> TextUtils.equals(route.getClientPackageName(), null)); assertEquals(0, mManager.getActiveRoutes().size()); + */ } /** * Tests selecting and unselecting routes of a single provider. */ - @Test + //TODO: @Test when session is released public void testSingleProviderSelect() throws Exception { Map routes = waitAndGetRoutesWithManager(CATEGORIES_ALL); addRouterCallback(new RouteCallback()); @@ -310,10 +315,14 @@ public class MediaRouterManagerTest { ROUTE_ID2, route -> TextUtils.equals(route.getClientPackageName(), mPackageName)); + //TODO: release the session + /* awaitOnRouteChangedManager( - () -> mManager.unselectRoute(mPackageName), + () -> mManager.selectRoute(mPackageName, null), ROUTE_ID2, route -> TextUtils.equals(route.getClientPackageName(), null)); + + */ } @Test diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 0483431719c88..31c649e696b25 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.RouteSessionInfo; -import android.os.Bundle; import java.util.Objects; @@ -45,8 +44,12 @@ abstract class MediaRoute2Provider { public abstract void requestCreateSession(String packageName, String routeId, String controlCategory, int requestId); - public abstract void requestSelectRoute(String packageName, String routeId, int seq); - public abstract void unselectRoute(String packageName, String routeId); + public abstract void releaseSession(int sessionId); + + public abstract void addRoute(int sessionId, MediaRoute2Info route); + public abstract void removeRoute(int sessionId, MediaRoute2Info route); + public abstract void transferRoute(int sessionId, MediaRoute2Info route); + public abstract void sendControlRequest(MediaRoute2Info route, Intent request); public abstract void requestSetVolume(MediaRoute2Info route, int volume); public abstract void requestUpdateVolume(MediaRoute2Info route, int delta); @@ -82,9 +85,6 @@ abstract class MediaRoute2Provider { public interface Callback { void onProviderStateChanged(@Nullable MediaRoute2Provider provider); - void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider, - @NonNull String clientPackageName, @NonNull MediaRoute2Info route, - @Nullable Bundle controlHints, int seq); void onSessionCreated(@NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo, int requestId); } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 893747bd107a5..902a25774d4ef 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -28,7 +28,6 @@ import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; import android.media.RouteSessionInfo; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; @@ -86,18 +85,31 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void requestSelectRoute(String packageName, String routeId, int seq) { + public void releaseSession(int sessionId) { if (mConnectionReady) { - mActiveConnection.requestSelectRoute(packageName, routeId, seq); + mActiveConnection.releaseSession(sessionId); updateBinding(); } } @Override - public void unselectRoute(String packageName, String routeId) { + public void addRoute(int sessionId, MediaRoute2Info route) { if (mConnectionReady) { - mActiveConnection.unselectRoute(packageName, routeId); - updateBinding(); + mActiveConnection.addRoute(sessionId, route.getId()); + } + } + + @Override + public void removeRoute(int sessionId, MediaRoute2Info route) { + if (mConnectionReady) { + mActiveConnection.removeRoute(sessionId, route.getId()); + } + } + + @Override + public void transferRoute(int sessionId, MediaRoute2Info route) { + if (mConnectionReady) { + mActiveConnection.transferRoute(sessionId, route.getId()); } } @@ -266,20 +278,6 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv setAndNotifyProviderInfo(info); } - private void onRouteSelected(Connection connection, - String packageName, String routeId, Bundle controlHints, int seq) { - if (mActiveConnection != connection) { - return; - } - MediaRoute2ProviderInfo providerInfo = getProviderInfo(); - MediaRoute2Info route = (providerInfo == null) ? null : providerInfo.getRoute(routeId); - if (route == null) { - Slog.w(TAG, this + ": Unknown route " + routeId + " is selected from remove provider"); - return; - } - mCallback.onRouteSelected(this, packageName, route, controlHints, seq); - } - private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo, int requestId) { if (mActiveConnection != connection) { @@ -331,25 +329,42 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv public void requestCreateSession(String packageName, String routeId, String controlCategory, int requestId) { try { - mProvider.requestCreateSession(packageName, routeId, controlCategory, requestId); + mProvider.requestCreateSession(packageName, routeId, + controlCategory, requestId); } catch (RemoteException ex) { Slog.e(TAG, "Failed to deliver request to create a session.", ex); } } - public void requestSelectRoute(String packageName, String routeId, int seq) { + public void releaseSession(int sessionId) { try { - mProvider.requestSelectRoute(packageName, routeId, seq); + mProvider.releaseSession(sessionId); } catch (RemoteException ex) { - Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex); + Slog.e(TAG, "Failed to deliver request to release a session.", ex); } } - public void unselectRoute(String packageName, String routeId) { + public void addRoute(int sessionId, String routeId) { try { - mProvider.unselectRoute(packageName, routeId); + mProvider.addRoute(sessionId, routeId); } catch (RemoteException ex) { - Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex); + Slog.e(TAG, "Failed to deliver request to add a route to a session.", ex); + } + } + + public void removeRoute(int sessionId, String routeId) { + try { + mProvider.removeRoute(sessionId, routeId); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to deliver request to remove a route from a session.", ex); + } + } + + public void transferRoute(int sessionId, String routeId) { + try { + mProvider.transferRoute(sessionId, routeId); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to deliver request to transfer a session to a route.", ex); } } @@ -386,11 +401,6 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv mHandler.post(() -> onProviderInfoUpdated(Connection.this, info)); } - void postRouteSelected(String packageName, String routeId, Bundle controlHints, int seq) { - mHandler.post(() -> onRouteSelected(Connection.this, - packageName, routeId, controlHints, seq)); - } - void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) { mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo, requestId)); @@ -416,15 +426,6 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - @Override - public void notifyRouteSelected(String packageName, String routeId, - Bundle controlHints, int seq) { - Connection connection = mConnectionRef.get(); - if (connection != null) { - connection.postRouteSelected(packageName, routeId, controlHints, seq); - } - } - @Override public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) { Connection connection = mConnectionRef.get(); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index a71186340b9fc..a1ef850f9743e 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -28,14 +28,12 @@ import android.media.IMediaRouter2Client; import android.media.IMediaRouter2Manager; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; -import android.media.MediaRouter2; import android.media.RouteSessionInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; @@ -77,8 +75,6 @@ class MediaRouter2ServiceImpl { private final ArrayMap mAllManagerRecords = new ArrayMap<>(); @GuardedBy("mLock") private int mCurrentUserId = -1; - @GuardedBy("mLock") - private int mSelectRouteRequestSequenceNumber = 1; MediaRouter2ServiceImpl(Context context) { mContext = context; @@ -246,12 +242,12 @@ class MediaRouter2ServiceImpl { } } - public void selectClientRoute2(@NonNull IMediaRouter2Manager manager, - String packageName, @Nullable MediaRoute2Info route) { + public void requestCreateClientSession(IMediaRouter2Manager manager, String packageName, + MediaRoute2Info route, int requestId) { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - selectClientRoute2Locked(manager, packageName, route); + requestClientCreateSessionLocked(manager, packageName, route, requestId); } } finally { Binder.restoreCallingIdentity(token); @@ -374,42 +370,6 @@ class MediaRouter2ServiceImpl { } } - private void requestSelectRoute2Locked(Client2Record clientRecord, boolean selectedByManager, - MediaRoute2Info route) { - if (clientRecord != null) { - MediaRoute2Info oldRoute = clientRecord.mSelectedRoute; - clientRecord.mSelectingRoute = route; - clientRecord.mIsManagerSelecting = selectedByManager; - - UserHandler handler = clientRecord.mUserRecord.mHandler; - //TODO: Handle transfer instead of unselect and select - if (oldRoute != null) { - handler.sendMessage(obtainMessage( - UserHandler::unselectRoute, handler, clientRecord.mPackageName, oldRoute)); - } - if (route != null) { - final int seq = mSelectRouteRequestSequenceNumber; - mSelectRouteRequestSequenceNumber++; - - handler.sendMessage(obtainMessage( - UserHandler::requestSelectRoute, handler, clientRecord.mPackageName, - route, seq)); - // Remove all previous timeout messages - for (int previousSeq : clientRecord.mSelectRouteSequenceNumbers) { - clientRecord.mUserRecord.mHandler.removeMessages(previousSeq); - } - clientRecord.mSelectRouteSequenceNumbers.clear(); - - // When the request is not handled in timeout, set the client's route to default. - Message timeoutMsg = obtainMessage(UserHandler::handleRouteSelectionTimeout, - handler, clientRecord.mPackageName, route); - timeoutMsg.what = seq; // Make the message cancelable. - handler.sendMessageDelayed(timeoutMsg, ROUTE_SELECTION_REQUEST_TIMEOUT_MS); - clientRecord.mSelectRouteSequenceNumbers.add(seq); - } - } - } - private void setControlCategoriesLocked(Client2Record clientRecord, List categories) { if (clientRecord != null) { if (clientRecord.mControlCategories.equals(categories)) { @@ -512,17 +472,19 @@ class MediaRouter2ServiceImpl { } } - private void selectClientRoute2Locked(IMediaRouter2Manager manager, - String packageName, MediaRoute2Info route) { + private void requestClientCreateSessionLocked(IMediaRouter2Manager manager, + String packageName, MediaRoute2Info route, int requestId) { ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); if (managerRecord != null) { Client2Record clientRecord = managerRecord.mUserRecord.findClientRecordLocked(packageName); if (clientRecord == null) { - Slog.w(TAG, "Ignoring route selection for unknown client."); + Slog.w(TAG, "Ignoring session creation for unknown client."); } if (clientRecord != null && managerRecord.mTrusted) { - requestSelectRoute2Locked(clientRecord, true, route); + //TODO: select category properly + requestCreateSessionLocked(clientRecord.mClient, route, + route.getSupportedCategories().get(0), requestId); } } } @@ -551,7 +513,6 @@ class MediaRouter2ServiceImpl { } } - private void initializeUserLocked(UserRecord userRecord) { if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); @@ -737,14 +698,6 @@ class MediaRouter2ServiceImpl { sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider)); } - @Override - public void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider, - String clientPackageName, MediaRoute2Info route, Bundle controlHints, int seq) { - sendMessage(PooledLambda.obtainMessage( - UserHandler::updateSelectedRoute, this, provider, clientPackageName, route, - controlHints, seq)); - } - @Override public void onSessionCreated(@NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo, int requestId) { @@ -865,6 +818,7 @@ class MediaRouter2ServiceImpl { @NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo, int requestId) { SessionCreationRequest matchingRequest = null; + for (SessionCreationRequest request : mSessionCreationRequests) { if (request.mRequestId == requestId && TextUtils.equals( @@ -880,26 +834,34 @@ class MediaRouter2ServiceImpl { return; } + mSessionCreationRequests.remove(matchingRequest); + if (sessionInfo == null) { // Failed notifySessionCreationFailed(matchingRequest.mClientRecord, requestId); return; } + RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo) + .setProviderId(provider.getUniqueId()) + .build(); + String originalRouteId = matchingRequest.mRoute.getId(); String originalCategory = matchingRequest.mControlCategory; - if (!sessionInfo.getSelectedRoutes().contains(originalRouteId) - || !TextUtils.equals(originalCategory, sessionInfo.getControlCategory())) { + if (!sessionInfoWithProviderId.getSelectedRoutes().contains(originalRouteId) + || !TextUtils.equals(originalCategory, + sessionInfoWithProviderId.getControlCategory())) { Slog.w(TAG, "Created session doesn't match the original request." + " originalRouteId=" + originalRouteId - + ", originalCategory=" + originalCategory - + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo); + + ", originalCategory=" + originalCategory + ", requestId=" + requestId + + ", sessionInfo=" + sessionInfoWithProviderId); notifySessionCreationFailed(matchingRequest.mClientRecord, requestId); return; } // Succeeded - notifySessionCreated(matchingRequest.mClientRecord, sessionInfo, requestId); + notifySessionCreated(matchingRequest.mClientRecord, + sessionInfoWithProviderId, requestId); // TODO: Tell managers for the session creation } @@ -922,109 +884,6 @@ class MediaRouter2ServiceImpl { } } - private void updateSelectedRoute(MediaRoute2ProviderProxy provider, - String clientPackageName, MediaRoute2Info selectedRoute, Bundle controlHints, - int seq) { - if (selectedRoute == null - || !TextUtils.equals(clientPackageName, selectedRoute.getClientPackageName())) { - Log.w(TAG, "Ignoring route selection which has non-matching clientPackageName."); - return; - } - - MediaRouter2ServiceImpl service = mServiceRef.get(); - if (service == null) { - return; - } - - Client2Record clientRecord; - synchronized (service.mLock) { - clientRecord = mUserRecord.findClientRecordLocked(clientPackageName); - } - - //TODO: handle a case such that controlHints is null. (How should we notify MR2?) - - if (clientRecord.mSelectingRoute == null || !TextUtils.equals( - clientRecord.mSelectingRoute.getUniqueId(), selectedRoute.getUniqueId())) { - Log.w(TAG, "Ignoring invalid updateSelectedRoute call. selectingRoute=" - + clientRecord.mSelectingRoute + " route=" + selectedRoute); - unselectRoute(clientPackageName, selectedRoute); - return; - } - clientRecord.mSelectingRoute = null; - clientRecord.mSelectedRoute = selectedRoute; - - notifyRouteSelectedToClient(clientRecord.mClient, - selectedRoute, - clientRecord.mIsManagerSelecting - ? MediaRouter2.SELECT_REASON_SYSTEM_SELECTED : - MediaRouter2.SELECT_REASON_USER_SELECTED, - controlHints); - updateClientUsage(clientRecord); - - // Remove the fallback route selection message. - removeMessages(seq); - } - - private void handleRouteSelectionTimeout(String clientPackageName, - MediaRoute2Info selectingRoute) { - MediaRouter2ServiceImpl service = mServiceRef.get(); - if (service == null) { - return; - } - - Client2Record clientRecord; - synchronized (service.mLock) { - clientRecord = mUserRecord.findClientRecordLocked(clientPackageName); - } - - if (clientRecord == null) { - Log.w(TAG, "The client has gone. packageName=" + clientPackageName - + " selectingRoute=" + selectingRoute); - return; - } - - if (clientRecord.mSelectingRoute == null || !TextUtils.equals( - clientRecord.mSelectingRoute.getUniqueId(), selectingRoute.getUniqueId())) { - Log.w(TAG, "Ignoring invalid selectFallbackRoute call. " - + "Current selectingRoute=" + clientRecord.mSelectingRoute - + " , original selectingRoute=" + selectingRoute); - return; - } - - clientRecord.mSelectingRoute = null; - // TODO: When the default route is introduced, make mSelectedRoute always non-null. - MediaRoute2Info fallbackRoute = null; - clientRecord.mSelectedRoute = fallbackRoute; - - notifyRouteSelectedToClient(clientRecord.mClient, - fallbackRoute, - MediaRouter2.SELECT_REASON_FALLBACK, - Bundle.EMPTY /* controlHints */); - updateClientUsage(clientRecord); - } - - private void requestSelectRoute(String clientPackageName, MediaRoute2Info route, int seq) { - if (route != null) { - MediaRoute2Provider provider = findProvider(route.getProviderId()); - if (provider == null) { - Slog.w(TAG, "Ignoring to select route of unknown provider " + route); - } else { - provider.requestSelectRoute(clientPackageName, route.getId(), seq); - } - } - } - - private void unselectRoute(String clientPackageName, MediaRoute2Info route) { - if (route != null) { - MediaRoute2Provider provider = findProvider(route.getProviderId()); - if (provider == null) { - Slog.w(TAG, "Ignoring to unselect route of unknown provider " + route); - } else { - provider.unselectRoute(clientPackageName, route.getId()); - } - } - } - private void sendControlRequest(MediaRoute2Info route, Intent request) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider != null) { diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index a280f91717253..a51158b4ae023 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -487,11 +487,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void selectClientRoute2(IMediaRouter2Manager manager, - String packageName, MediaRoute2Info route) { - mService2.selectClientRoute2(manager, packageName, route); + public void requestCreateClientSession(IMediaRouter2Manager manager, String packageName, + MediaRoute2Info route, int requestId) { + mService2.requestCreateClientSession(manager, packageName, route, requestId); } - // Binder call @Override public void setControlCategories(IMediaRouter2Client client, List categories) { diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 6c4c8d50b41e4..23c67e2dd02c2 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -94,21 +94,24 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // Do nothing } - //TODO: implement method @Override - public void requestSelectRoute(@NonNull String packageName, @NonNull String routeId, int seq) { - try { - mAudioService.setBluetoothA2dpOn( - !TextUtils.equals(routeId, mDefaultRoute.getId())); - } catch (RemoteException ex) { - Log.e(TAG, "Error changing Bluetooth A2DP route"); - } + public void releaseSession(int sessionId) { + // Do nothing } - //TODO: implement method @Override - public void unselectRoute(@NonNull String packageName, @NonNull String routeId) { - // does nothing..? + public void addRoute(int sessionId, MediaRoute2Info route) { + //TODO: implement method + } + + @Override + public void removeRoute(int sessionId, MediaRoute2Info route) { + //TODO: implement method + } + + @Override + public void transferRoute(int sessionId, MediaRoute2Info route) { + //TODO: implement method } //TODO: implement method