Merge "MediaRouter2: Add OnCreateSessionListener"
This commit is contained in:
@@ -18,13 +18,15 @@ package android.media;
|
||||
|
||||
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);
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouterClientState;
|
||||
import android.media.RouteDiscoveryPreference;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
@@ -52,7 +53,8 @@ interface IMediaRouterService {
|
||||
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);
|
||||
|
||||
@@ -161,8 +161,8 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* @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 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* 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 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* @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 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* @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 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@@ -92,6 +92,7 @@ public class MediaRouter2 {
|
||||
@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.
|
||||
@@ -280,6 +281,19 @@ public class MediaRouter2 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -300,13 +314,24 @@ public class MediaRouter2 {
|
||||
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,
|
||||
@@ -706,6 +731,34 @@ public class MediaRouter2 {
|
||||
public void onSessionReleased(@NonNull RoutingController controller) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -19,10 +19,12 @@ package com.android.mediarouteprovider.example;
|
||||
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 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
}
|
||||
|
||||
@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 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
.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();
|
||||
|
||||
@@ -43,12 +43,14 @@ import android.annotation.NonNull;
|
||||
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 @@ public class MediaRouter2Test {
|
||||
|
||||
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();
|
||||
@@ -327,6 +332,74 @@ public class MediaRouter2Test {
|
||||
}
|
||||
}
|
||||
|
||||
@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<>();
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.ComponentName;
|
||||
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 @@ abstract class MediaRoute2Provider {
|
||||
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);
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.media.IMediaRoute2ProviderClient;
|
||||
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 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
}
|
||||
|
||||
@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 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.media.MediaRouter2Utils.getProviderId;
|
||||
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 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
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 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
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 @@ class MediaRouter2ServiceImpl {
|
||||
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 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
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 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
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 @@ class MediaRouter2ServiceImpl {
|
||||
mSessionCreationRequests.add(request);
|
||||
|
||||
provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
|
||||
requestId);
|
||||
requestId, sessionHints);
|
||||
}
|
||||
|
||||
private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.media.RemoteDisplayState.RemoteDisplayInfo;
|
||||
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 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
// 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
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.media.IAudioRoutesObserver;
|
||||
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 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestCreateSession(String packageName, String routeId, long requestId) {
|
||||
public void requestCreateSession(String packageName, String routeId, long requestId,
|
||||
Bundle sessionHints) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user