Merge "MediaRouter2: Add OnCreateSessionListener"
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index aa38e51..a25aff6 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -18,13 +18,15 @@
import android.content.Intent;
import android.media.IMediaRoute2ProviderClient;
+import android.os.Bundle;
/**
* {@hide}
*/
oneway interface IMediaRoute2Provider {
void setClient(IMediaRoute2ProviderClient client);
- void requestCreateSession(String packageName, String routeId, long requestId);
+ void requestCreateSession(String packageName, String routeId, long requestId,
+ in @nullable Bundle sessionHints);
void releaseSession(String sessionId);
void selectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 2d3e185..5275399 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -24,6 +24,7 @@
import android.media.MediaRouterClientState;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
/**
* {@hide}
@@ -52,7 +53,8 @@
void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
- void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId);
+ void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId,
+ in @nullable Bundle sessionHints);
void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryPreference preference);
void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 5a3de6d..20ca997 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -161,8 +161,8 @@
* @param sessionInfo information of the new session.
* The {@link RoutingSessionInfo#getId() id} of the session must be unique.
* @param requestId id of the previous request to create this session provided in
- * {@link #onCreateSession(String, String, long)}
- * @see #onCreateSession(String, String, long)
+ * {@link #onCreateSession(String, String, long, Bundle)}
+ * @see #onCreateSession(String, String, long, Bundle)
* @hide
*/
public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
@@ -196,8 +196,8 @@
* Notifies clients of that the session could not be created.
*
* @param requestId id of the previous request to create the session provided in
- * {@link #onCreateSession(String, String, long)}.
- * @see #onCreateSession(String, String, long)
+ * {@link #onCreateSession(String, String, long, Bundle)}.
+ * @see #onCreateSession(String, String, long, Bundle)
* @hide
*/
public final void notifySessionCreationFailed(long requestId) {
@@ -290,6 +290,9 @@
* @param packageName the package name of the application that selected the route
* @param routeId the id of the route initially being connected
* @param requestId the id of this session creation request
+ * @param sessionHints an optional bundle of app-specific arguments sent by
+ * {@link MediaRouter2}, or null if none. The contents of this bundle
+ * may affect the result of session creation.
*
* @see RoutingSessionInfo.Builder#Builder(String, String)
* @see RoutingSessionInfo.Builder#addSelectedRoute(String)
@@ -297,7 +300,7 @@
* @hide
*/
public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
- long requestId);
+ long requestId, @Nullable Bundle sessionHints);
/**
* Called when the session should be released. A client of the session or system can request
@@ -432,12 +435,14 @@
}
@Override
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ @Nullable Bundle requestCreateSession) {
if (!checkCallerisSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
- MediaRoute2ProviderService.this, packageName, routeId, requestId));
+ MediaRoute2ProviderService.this, packageName, routeId, requestId,
+ requestCreateSession));
}
@Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 971b08d..51d08ec 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -92,6 +92,7 @@
@GuardedBy("sRouterLock")
private boolean mShouldUpdateRoutes;
private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
+ private volatile OnCreateSessionListener mOnCreateSessionListener;
/**
* Gets an instance of the media router associated with the context.
@@ -281,6 +282,19 @@
}
/**
+ * Sets an {@link OnCreateSessionListener} to send hints when creating a session.
+ * To send the hints, listener should be set <em>BEFORE</em> calling
+ * {@link #requestCreateSession(MediaRoute2Info)}.
+ *
+ * @param listener A listener to send optional app-specific hints when creating a session.
+ * {@code null} for unset.
+ * @hide
+ */
+ public void setOnCreateSessionListener(@Nullable OnCreateSessionListener listener) {
+ mOnCreateSessionListener = listener;
+ }
+
+ /**
* Requests the media route provider service to create a session with the given route.
*
* @param route the route you want to create a session with.
@@ -300,13 +314,24 @@
SessionCreationRequest request = new SessionCreationRequest(requestId, route);
mSessionCreationRequests.add(request);
+
+ OnCreateSessionListener listener = mOnCreateSessionListener;
+ Bundle sessionHints = null;
+ if (listener != null) {
+ sessionHints = listener.onCreateSession(route);
+ if (sessionHints != null) {
+ sessionHints = new Bundle(sessionHints);
+ }
+ }
+
Client2 client;
synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.requestCreateSession(client, route, requestId);
+ mMediaRouterService.requestCreateSession(client, route, requestId,
+ sessionHints);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to request to create session.", ex);
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -707,6 +732,34 @@
}
/**
+ * A listener interface to send an optional app-specific hints when creating a session.
+ *
+ * @hide
+ */
+ public interface OnCreateSessionListener {
+ /**
+ * Called when the {@link MediaRouter2} is about to request
+ * the media route provider service to create a session with the given route.
+ * The {@link Bundle} returned here will be sent to media route provider service as a hint
+ * for creating a session.
+ * <p>
+ * To send hints when creating the session, set this listener before calling
+ * {@link #requestCreateSession(MediaRoute2Info)}.
+ * <p>
+ * This will be called on the same thread which calls
+ * {@link #requestCreateSession(MediaRoute2Info)}.
+ *
+ * @param route The route to create session with
+ * @return An optional bundle of app-specific arguments to send to the provider,
+ * or null if none. The contents of this bundle may affect the result of
+ * session creation.
+ * @see MediaRoute2ProviderService#onCreateSession(String, String, long, Bundle)
+ */
+ @Nullable
+ Bundle onCreateSession(@NonNull MediaRoute2Info route);
+ }
+
+ /**
* A class to control media routing session in media route provider.
* For example, selecting/deselcting/transferring routes to session can be done through this
* class. Instances are created by {@link MediaRouter2}.
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 cc2d1b1..825b547 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -19,10 +19,12 @@
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import android.annotation.Nullable;
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderService;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
@@ -167,7 +169,8 @@
}
@Override
- public void onCreateSession(String packageName, String routeId, long requestId) {
+ public void onCreateSession(String packageName, String routeId, long requestId,
+ @Nullable Bundle sessionHints) {
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.
@@ -188,6 +191,8 @@
.addSelectedRoute(routeId)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+ // Set control hints with given sessionHints
+ .setControlHints(sessionHints)
.build();
notifySessionCreated(sessionInfo, requestId);
publishRoutes();
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index e782aae..32d03db 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -43,12 +43,14 @@
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
+import android.media.MediaRouter2.OnCreateSessionListener;
import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2.RoutingController;
import android.media.MediaRouter2.SessionCallback;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -80,6 +82,9 @@
private static final int TIMEOUT_MS = 5000;
+ private static final String TEST_KEY = "test_key";
+ private static final String TEST_VALUE = "test_value";
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -328,6 +333,74 @@
}
@Test
+ public void testSetOnCreateSessionListener() throws Exception {
+ final List<String> sampleRouteFeature = new ArrayList<>();
+ sampleRouteFeature.add(FEATURE_SAMPLE);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature);
+ MediaRoute2Info route = routes.get(ROUTE_ID1);
+ assertNotNull(route);
+
+ final Bundle createSessionHints = new Bundle();
+ createSessionHints.putString(TEST_KEY, TEST_VALUE);
+ final OnCreateSessionListener listener = new OnCreateSessionListener() {
+ @Override
+ public Bundle onCreateSession(MediaRoute2Info route) {
+ return createSessionHints;
+ }
+ };
+
+ final CountDownLatch successLatch = new CountDownLatch(1);
+ final CountDownLatch failureLatch = new CountDownLatch(1);
+ final List<RoutingController> controllers = new ArrayList<>();
+
+ // Create session with this route
+ SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public void onSessionCreated(RoutingController controller) {
+ assertNotNull(controller);
+ assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
+
+ // The SampleMediaRoute2ProviderService supposed to set control hints
+ // with the given creationSessionHints.
+ Bundle controlHints = controller.getControlHints();
+ assertNotNull(controlHints);
+ assertTrue(controlHints.containsKey(TEST_KEY));
+ assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
+
+ controllers.add(controller);
+ successLatch.countDown();
+ }
+
+ @Override
+ public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
+ failureLatch.countDown();
+ }
+ };
+
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ RouteCallback routeCallback = new RouteCallback();
+ mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
+
+ try {
+ mRouter2.registerSessionCallback(mExecutor, sessionCallback);
+
+ // The SampleMediaRoute2ProviderService supposed to set control hints
+ // with the given creationSessionHints.
+ mRouter2.setOnCreateSessionListener(listener);
+ mRouter2.requestCreateSession(route);
+ assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // onSessionCreationFailed should not be called.
+ assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ releaseControllers(controllers);
+ mRouter2.unregisterRouteCallback(routeCallback);
+ mRouter2.unregisterSessionCallback(sessionCallback);
+ }
+ }
+
+ @Test
public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index b186771..1cd8aad 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.media.MediaRoute2ProviderInfo;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
@@ -49,7 +50,8 @@
mCallback = callback;
}
- public abstract void requestCreateSession(String packageName, String routeId, long requestId);
+ public abstract void requestCreateSession(String packageName, String routeId, long requestId,
+ @Nullable Bundle sessionHints);
public abstract void releaseSession(String sessionId);
public abstract void selectRoute(String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 4b992be..9761461 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -26,6 +26,7 @@
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
@@ -73,9 +74,11 @@
}
@Override
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ Bundle sessionHints) {
if (mConnectionReady) {
- mActiveConnection.requestCreateSession(packageName, routeId, requestId);
+ mActiveConnection.requestCreateSession(
+ packageName, routeId, requestId, sessionHints);
updateBinding();
}
}
@@ -427,9 +430,10 @@
mClient.dispose();
}
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ Bundle sessionHints) {
try {
- mProvider.requestCreateSession(packageName, routeId, requestId);
+ mProvider.requestCreateSession(packageName, routeId, requestId, sessionHints);
} catch (RemoteException ex) {
Slog.e(TAG, "Failed to deliver request to create a session.", ex);
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 45d50b3..fd8b4f5 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,6 +22,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -178,14 +179,14 @@
}
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
- int requestId) {
+ int requestId, Bundle sessionHints) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionLocked(client, route, requestId);
+ requestCreateSessionLocked(client, route, requestId, sessionHints);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -446,7 +447,7 @@
}
private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
- @NonNull MediaRoute2Info route, long requestId) {
+ @NonNull MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) {
final IBinder binder = client.asBinder();
final Client2Record clientRecord = mAllClientRecords.get(binder);
@@ -458,7 +459,8 @@
if (clientRecord != null) {
clientRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::requestCreateSessionOnHandler,
- clientRecord.mUserRecord.mHandler, clientRecord, route, requestId));
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, route, requestId, sessionHints));
}
}
@@ -617,9 +619,9 @@
}
long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId);
if (clientRecord != null && managerRecord.mTrusted) {
- //TODO: select route feature properly
+ //TODO: Use client's OnCreateSessionListener to send proper session hints.
requestCreateSessionLocked(clientRecord.mClient, route,
- uniqueRequestId);
+ uniqueRequestId, null /* sessionHints */);
}
}
}
@@ -980,7 +982,7 @@
}
private void requestCreateSessionOnHandler(Client2Record clientRecord,
- MediaRoute2Info route, long requestId) {
+ MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
@@ -996,7 +998,7 @@
mSessionCreationRequests.add(request);
provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
- requestId);
+ requestId, sessionHints);
}
private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index aad9636..40fca39 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -42,6 +42,7 @@
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -460,8 +461,8 @@
// Binder call
@Override
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
- int requestId) {
- mService2.requestCreateSession(client, route, requestId);
+ int requestId, Bundle sessionHints) {
+ mService2.requestCreateSession(client, route, requestId, sessionHints);
}
// Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 3759ba9..56c33fe 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -26,6 +26,7 @@
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -96,7 +97,8 @@
}
@Override
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ Bundle sessionHints) {
// Do nothing
}