Merge "MediaRouter2: Add OnCreateSessionListener"

This commit is contained in:
Hyundo Moon
2020-01-17 09:36:07 +00:00
committed by Android (Google) Code Review
11 changed files with 178 additions and 27 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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<>();

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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
}