Merge "MediaRouter2: Add requestCreateSession"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9d1ff5e4c0
@@ -24,6 +24,8 @@ 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 notifyControlRequestSent(String id, in Intent request);
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.media;
|
||||
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.RouteSessionInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
@@ -25,5 +26,8 @@ import android.os.Bundle;
|
||||
*/
|
||||
oneway interface IMediaRoute2ProviderClient {
|
||||
void updateProviderInfo(in MediaRoute2ProviderInfo info);
|
||||
void notifyRouteSelected(String packageName, String routeId, in Bundle controlHints, int seq);
|
||||
void notifyRouteSelected(String packageName, String routeId, in @nullable Bundle controlHints,
|
||||
int seq);
|
||||
void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo,
|
||||
in @nullable Bundle controlHints, int requestId);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media;
|
||||
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.RouteSessionInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
@@ -27,5 +28,6 @@ oneway interface IMediaRouter2Client {
|
||||
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
|
||||
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
|
||||
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
|
||||
void notifyRouteSelected(in MediaRoute2Info route, int reason, in Bundle controlHints);
|
||||
void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo,
|
||||
in @nullable Bundle controlHints, int requestId);
|
||||
}
|
||||
|
||||
@@ -48,13 +48,9 @@ interface IMediaRouterService {
|
||||
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
|
||||
void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
|
||||
void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
|
||||
/**
|
||||
* Changes the selected route of the client.
|
||||
*
|
||||
* @param client the client that changes it's selected route
|
||||
* @param route the route to be selected
|
||||
*/
|
||||
void requestSelectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
|
||||
|
||||
void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
|
||||
String controlCategory, int requestId);
|
||||
void setControlCategories(IMediaRouter2Client client, in List<String> categories);
|
||||
|
||||
void registerManager(IMediaRouter2Manager manager, String packageName);
|
||||
|
||||
@@ -47,6 +47,7 @@ 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;
|
||||
|
||||
@@ -166,19 +167,29 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* Notifies clients of that the session is created and ready for use. If the session can be
|
||||
* controlled, pass a {@link Bundle} that contains how to control it.
|
||||
*
|
||||
* @param sessionId id of the session
|
||||
* @param sessionInfo information of the new session.
|
||||
* Pass {@code null} to reject the request or inform clients that
|
||||
* session creation has failed.
|
||||
* @param controlHints a {@link Bundle} that contains how to control the session.
|
||||
* @param requestId id of the previous request to create this session
|
||||
*/
|
||||
//TODO: fail reason?
|
||||
public final void notifySessionCreated(int sessionId, @Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints) {
|
||||
//TODO: validate sessionId (it must be in "waiting list")
|
||||
synchronized (mSessionLock) {
|
||||
mSessionInfo.put(sessionId, sessionInfo);
|
||||
//TODO: notify media router service of session creation.
|
||||
public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
//TODO: validate sessionInfo.getSessionId() (it must be in "waiting list")
|
||||
if (sessionInfo != null) {
|
||||
synchronized (mSessionLock) {
|
||||
mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (mClient == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClient.notifySessionCreated(sessionInfo, controlHints, requestId);
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Failed to notify session created.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,18 +214,19 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
/**
|
||||
* Called when a session should be created.
|
||||
* You should create and maintain your own session and notifies the client of
|
||||
* session info. Call {@link #notifySessionCreated(int, RouteSessionInfo, Bundle)}
|
||||
* to notify the information of a new session.
|
||||
* session info. Call {@link #notifySessionCreated(RouteSessionInfo, Bundle, int)}
|
||||
* with the given {@code requestId} to notify the information of a new session.
|
||||
* If you can't create the session or want to reject the request, pass {@code null}
|
||||
* as session info in {@link #notifySessionCreated(int, RouteSessionInfo, Bundle)}.
|
||||
* as session info in {@link #notifySessionCreated(RouteSessionInfo, Bundle, int)}
|
||||
* with the given {@code requestId}.
|
||||
*
|
||||
* @param packageName the package name of the application that selected the route
|
||||
* @param routeId the id of the route initially being connected
|
||||
* @param controlCategory the control category of the new session
|
||||
* @param sessionId the id of a new session
|
||||
* @param requestId the id of this session creation request
|
||||
*/
|
||||
public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
|
||||
@NonNull String controlCategory, int sessionId);
|
||||
@NonNull String controlCategory, int requestId);
|
||||
|
||||
/**
|
||||
* Called when a session is about to be destroyed.
|
||||
@@ -338,6 +350,14 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
MediaRoute2ProviderService.this, client));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestCreateSession(String packageName, String routeId, String controlCategory,
|
||||
int requestId) {
|
||||
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
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.text.TextUtils;
|
||||
@@ -46,6 +47,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A new Media Router
|
||||
@@ -98,7 +100,10 @@ public class MediaRouter2 {
|
||||
private final Context mContext;
|
||||
private final IMediaRouterService mMediaRouterService;
|
||||
|
||||
private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
|
||||
private final CopyOnWriteArrayList<RouteCallbackRecord> mRouteCallbackRecords =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
private final CopyOnWriteArrayList<SessionCreationRequest> mSessionCreationRequests =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
private final String mPackageName;
|
||||
@@ -108,12 +113,12 @@ public class MediaRouter2 {
|
||||
@GuardedBy("sLock")
|
||||
private List<String> mControlCategories = Collections.emptyList();
|
||||
|
||||
private MediaRoute2Info mSelectedRoute;
|
||||
@GuardedBy("sLock")
|
||||
private MediaRoute2Info mSelectingRoute;
|
||||
// TODO: Make MediaRouter2 is always connected to the MediaRouterService.
|
||||
@GuardedBy("sLock")
|
||||
private Client2 mClient;
|
||||
|
||||
private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
|
||||
|
||||
final Handler mHandler;
|
||||
@GuardedBy("sLock")
|
||||
private boolean mShouldUpdateRoutes;
|
||||
@@ -154,18 +159,14 @@ public class MediaRouter2 {
|
||||
for (MediaRoute2Info route : currentSystemRoutes) {
|
||||
mRoutes.put(route.getId(), route);
|
||||
}
|
||||
// The first route is the currently selected system route.
|
||||
// For example, if there are two system routes (BT and device speaker),
|
||||
// BT will be the first route in the list.
|
||||
mSelectedRoute = currentSystemRoutes.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback to discover routes and to receive events when they change.
|
||||
*/
|
||||
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull Callback callback) {
|
||||
registerCallback(executor, callback, 0);
|
||||
@NonNull RouteCallback routeCallback) {
|
||||
registerCallback(executor, routeCallback, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,12 +176,12 @@ public class MediaRouter2 {
|
||||
* </p>
|
||||
*/
|
||||
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull Callback callback, int flags) {
|
||||
@NonNull RouteCallback routeCallback, int flags) {
|
||||
Objects.requireNonNull(executor, "executor must not be null");
|
||||
Objects.requireNonNull(callback, "callback must not be null");
|
||||
Objects.requireNonNull(routeCallback, "callback must not be null");
|
||||
|
||||
CallbackRecord record = new CallbackRecord(callback, executor, flags);
|
||||
if (!mCallbackRecords.addIfAbsent(record)) {
|
||||
RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, flags);
|
||||
if (!mRouteCallbackRecords.addIfAbsent(record)) {
|
||||
Log.w(TAG, "Ignoring the same callback");
|
||||
return;
|
||||
}
|
||||
@@ -205,19 +206,20 @@ public class MediaRouter2 {
|
||||
* Unregisters the given callback. The callback will no longer receive events.
|
||||
* If the callback has not been added or been removed already, it is ignored.
|
||||
*
|
||||
* @param callback the callback to unregister
|
||||
* @param routeCallback the callback to unregister
|
||||
* @see #registerCallback
|
||||
*/
|
||||
public void unregisterCallback(@NonNull Callback callback) {
|
||||
Objects.requireNonNull(callback, "callback must not be null");
|
||||
public void unregisterCallback(@NonNull RouteCallback routeCallback) {
|
||||
Objects.requireNonNull(routeCallback, "callback must not be null");
|
||||
|
||||
if (!mCallbackRecords.remove(new CallbackRecord(callback, null, 0))) {
|
||||
if (!mRouteCallbackRecords.remove(
|
||||
new RouteCallbackRecord(null, routeCallback, 0))) {
|
||||
Log.w(TAG, "Ignoring unknown callback");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (sLock) {
|
||||
if (mCallbackRecords.size() == 0 && mClient != null) {
|
||||
if (mRouteCallbackRecords.size() == 0 && mClient != null) {
|
||||
try {
|
||||
mMediaRouterService.unregisterClient2(mClient);
|
||||
} catch (RemoteException ex) {
|
||||
@@ -282,38 +284,51 @@ public class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently selected route.
|
||||
* Requests the media route provider service to create a session with the given route.
|
||||
*
|
||||
* @return the selected route
|
||||
* @param route the route you want to create a session with.
|
||||
* @param controlCategory the control category of the session. Should not be empty
|
||||
* @param executor the executor to get the result of the session creation
|
||||
* @param callback the callback to get the result of the session creation
|
||||
*
|
||||
* @see SessionCreationCallback#onSessionCreated(RouteSessionController, Bundle)
|
||||
* @see SessionCreationCallback#onSessionCreationFailed()
|
||||
*/
|
||||
@NonNull
|
||||
public MediaRoute2Info getSelectedRoute() {
|
||||
return mSelectedRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to select the specified route. When the route is selected,
|
||||
* {@link Callback#onRouteSelected(MediaRoute2Info, int, Bundle)} will be called.
|
||||
*
|
||||
* @param route the route to select
|
||||
*/
|
||||
public void requestSelectRoute(@NonNull MediaRoute2Info route) {
|
||||
public void requestCreateSession(@NonNull MediaRoute2Info route,
|
||||
@NonNull String controlCategory,
|
||||
@CallbackExecutor Executor executor, @NonNull SessionCreationCallback callback) {
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
if (TextUtils.isEmpty(controlCategory)) {
|
||||
throw new IllegalArgumentException("controlCategory must not be empty");
|
||||
}
|
||||
Objects.requireNonNull(executor, "executor must not be null");
|
||||
Objects.requireNonNull(callback, "callback must not be null");
|
||||
|
||||
// TODO: Check the given route exists
|
||||
// TODO: Check the route supports the given controlCategory
|
||||
|
||||
final int requestId;
|
||||
// TODO: This does not ensure the uniqueness of the request ID.
|
||||
// Find the way to ensure it. (e.g. have mapping inside MediaRouterService)
|
||||
requestId = Process.myPid() * 10000 + mSessionCreationRequestCnt.getAndIncrement();
|
||||
|
||||
SessionCreationRequest request = new SessionCreationRequest(
|
||||
requestId, route, controlCategory, executor, callback);
|
||||
mSessionCreationRequests.add(request);
|
||||
|
||||
Client2 client;
|
||||
synchronized (sLock) {
|
||||
if (mSelectingRoute == route) {
|
||||
Log.w(TAG, "The route selection request is already sent.");
|
||||
return;
|
||||
}
|
||||
mSelectingRoute = route;
|
||||
client = mClient;
|
||||
}
|
||||
if (client != null) {
|
||||
try {
|
||||
mMediaRouterService.requestSelectRoute2(client, route);
|
||||
mMediaRouterService.requestCreateSession(
|
||||
client, route, controlCategory, requestId);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to request to select route.", ex);
|
||||
Log.e(TAG, "Unable to request to create session.", ex);
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
|
||||
MediaRouter2.this, null, null, requestId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,57 +487,71 @@ public class MediaRouter2 {
|
||||
}
|
||||
}
|
||||
|
||||
void selectRouteOnHandler(MediaRoute2Info route, int reason, Bundle controlHints) {
|
||||
synchronized (sLock) {
|
||||
if (reason == SELECT_REASON_USER_SELECTED) {
|
||||
if (mSelectingRoute == null
|
||||
|| !TextUtils.equals(mSelectingRoute.getUniqueId(), route.getUniqueId())) {
|
||||
Log.w(TAG, "Ignoring invalid or outdated notifyRouteSelected call. "
|
||||
+ "selectingRoute=" + mSelectingRoute + " route=" + route);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Creates a controller and calls the {@link SessionCreationCallback#onSessionCreated}.
|
||||
* If session creation has failed, then it calls
|
||||
* {@link SessionCreationCallback#onSessionCreationFailed()}.
|
||||
* <p>
|
||||
* Pass {@code null} to sessionInfo for the failure case.
|
||||
*/
|
||||
void createControllerOnHandler(@Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
SessionCreationRequest matchingRequest = null;
|
||||
for (SessionCreationRequest request : mSessionCreationRequests) {
|
||||
if (request.mRequestId == requestId) {
|
||||
matchingRequest = request;
|
||||
break;
|
||||
}
|
||||
mSelectingRoute = null;
|
||||
}
|
||||
if (reason == SELECT_REASON_SYSTEM_SELECTED) {
|
||||
reason = SELECT_REASON_USER_SELECTED;
|
||||
|
||||
if (matchingRequest == null) {
|
||||
Log.w(TAG, "Ignoring session creation result for unknown request."
|
||||
+ " requestId=" + requestId + ", sessionInfo=" + sessionInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
mSessionCreationRequests.remove(matchingRequest);
|
||||
|
||||
final Executor executor = matchingRequest.mExecutor;
|
||||
final SessionCreationCallback callback = matchingRequest.mSessionCreationCallback;
|
||||
|
||||
if (sessionInfo == null) {
|
||||
// TODO: We may need to distinguish between failure and rejection.
|
||||
// One way can be introducing 'reason'.
|
||||
executor.execute(callback::onSessionCreationFailed);
|
||||
} else {
|
||||
// TODO: RouteSessionController should be created with full info (e.g. routes)
|
||||
// from RouteSessionInfo.
|
||||
RouteSessionController controller = new RouteSessionController(sessionInfo);
|
||||
executor.execute(() -> callback.onSessionCreated(controller, controlHints));
|
||||
}
|
||||
mSelectedRoute = route;
|
||||
notifyRouteSelected(route, reason, controlHints);
|
||||
}
|
||||
|
||||
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
for (RouteCallbackRecord record: mRouteCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRoutesAdded(routes));
|
||||
() -> record.mRouteCallback.onRoutesAdded(routes));
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
for (RouteCallbackRecord record: mRouteCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRoutesRemoved(routes));
|
||||
() -> record.mRouteCallback.onRoutesRemoved(routes));
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
for (RouteCallbackRecord record: mRouteCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRoutesChanged(routes));
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRouteSelected(route, reason, controlHints));
|
||||
() -> record.mRouteCallback.onRoutesChanged(routes));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for receiving events about media routing changes.
|
||||
* Callback for receiving events about media route discovery.
|
||||
*/
|
||||
public static class Callback {
|
||||
public static class RouteCallback {
|
||||
/**
|
||||
* Called when routes are added. Whenever you registers a callback, this will
|
||||
* be invoked with known routes.
|
||||
@@ -549,30 +578,33 @@ public class MediaRouter2 {
|
||||
* @param routes the list of routes that have been changed. It's never empty.
|
||||
*/
|
||||
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
|
||||
|
||||
/**
|
||||
* Called when a route is selected. Exactly one route can be selected at a time.
|
||||
*
|
||||
* @param route the selected route.
|
||||
* @param reason the reason why the route is selected.
|
||||
* @param controlHints An optional bundle of provider-specific arguments which may be
|
||||
* used to control the selected route. Can be empty.
|
||||
* @see #SELECT_REASON_UNKNOWN
|
||||
* @see #SELECT_REASON_USER_SELECTED
|
||||
* @see #SELECT_REASON_FALLBACK
|
||||
* @see #getSelectedRoute()
|
||||
*/
|
||||
public void onRouteSelected(@NonNull MediaRoute2Info route, @SelectReason int reason,
|
||||
@NonNull Bundle controlHints) {}
|
||||
}
|
||||
|
||||
final class CallbackRecord {
|
||||
public final Callback mCallback;
|
||||
public Executor mExecutor;
|
||||
public int mFlags;
|
||||
/**
|
||||
* Callback for receiving a result of session creation.
|
||||
*/
|
||||
public static class SessionCreationCallback {
|
||||
/**
|
||||
* Called when the route session is created by the route provider.
|
||||
*
|
||||
* @param controller the controller to control the created session
|
||||
*/
|
||||
public void onSessionCreated(RouteSessionController controller, Bundle controlHints) {}
|
||||
|
||||
CallbackRecord(@NonNull Callback callback, @Nullable Executor executor, int flags) {
|
||||
mCallback = callback;
|
||||
/**
|
||||
* Called when the session creation request failed.
|
||||
*/
|
||||
public void onSessionCreationFailed() {}
|
||||
}
|
||||
|
||||
final class RouteCallbackRecord {
|
||||
public final Executor mExecutor;
|
||||
public final RouteCallback mRouteCallback;
|
||||
public final int mFlags;
|
||||
|
||||
RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback,
|
||||
int flags) {
|
||||
mRouteCallback = routeCallback;
|
||||
mExecutor = executor;
|
||||
mFlags = flags;
|
||||
}
|
||||
@@ -582,15 +614,35 @@ public class MediaRouter2 {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof CallbackRecord)) {
|
||||
if (!(obj instanceof RouteCallbackRecord)) {
|
||||
return false;
|
||||
}
|
||||
return mCallback == ((CallbackRecord) obj).mCallback;
|
||||
return mRouteCallback
|
||||
== ((RouteCallbackRecord) obj).mRouteCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mCallback.hashCode();
|
||||
return mRouteCallback.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
final class SessionCreationRequest {
|
||||
public final MediaRoute2Info mRoute;
|
||||
public final String mControlCategory;
|
||||
public final Executor mExecutor;
|
||||
public final SessionCreationCallback mSessionCreationCallback;
|
||||
public final int mRequestId;
|
||||
|
||||
SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
|
||||
@NonNull String controlCategory,
|
||||
@Nullable Executor executor,
|
||||
@NonNull SessionCreationCallback sessionCreationCallback) {
|
||||
mRoute = route;
|
||||
mControlCategory = controlCategory;
|
||||
mExecutor = executor;
|
||||
mSessionCreationCallback = sessionCreationCallback;
|
||||
mRequestId = requestId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,10 +669,10 @@ public class MediaRouter2 {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyRouteSelected(MediaRoute2Info route, int reason,
|
||||
Bundle controlHints) {
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::selectRouteOnHandler,
|
||||
MediaRouter2.this, route, reason, controlHints));
|
||||
public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
|
||||
MediaRouter2.this, sessionInfo, controlHints, requestId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -39,6 +40,8 @@ public class RouteSessionController {
|
||||
private final String mCategory;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private List<String> mSelectedRoutes;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
|
||||
new CopyOnWriteArrayList<>();
|
||||
@@ -46,12 +49,13 @@ public class RouteSessionController {
|
||||
private volatile boolean mIsReleased;
|
||||
|
||||
/**
|
||||
* @param sessionId the ID of the session.
|
||||
* @param category The category of media routes that the session includes.
|
||||
* @param sessionInfo
|
||||
*/
|
||||
RouteSessionController(int sessionId, @NonNull String category) {
|
||||
mSessionId = sessionId;
|
||||
mCategory = category;
|
||||
RouteSessionController(@NonNull RouteSessionInfo sessionInfo) {
|
||||
mSessionId = sessionInfo.getSessionId();
|
||||
mCategory = sessionInfo.getControlCategory();
|
||||
mSelectedRoutes = sessionInfo.getSelectedRoutes();
|
||||
// TODO: Create getters for all other types of routes
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,12 +74,11 @@ public class RouteSessionController {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of currently connected routes
|
||||
* @return the list of currently selected routes
|
||||
*/
|
||||
@NonNull
|
||||
public List<MediaRoute2Info> getRoutes() {
|
||||
// TODO: Implement this when SessionInfo is introduced.
|
||||
return null;
|
||||
public List<String> getSelectedRoutes() {
|
||||
return Collections.unmodifiableList(mSelectedRoutes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +96,7 @@ public class RouteSessionController {
|
||||
/**
|
||||
* Add routes to the remote session.
|
||||
*
|
||||
* @see #getRoutes()
|
||||
* @see #getSelectedRoutes()
|
||||
* @see Callback#onSessionInfoChanged
|
||||
*/
|
||||
public void addRoutes(List<MediaRoute2Info> routes) {
|
||||
@@ -102,9 +105,10 @@ public class RouteSessionController {
|
||||
|
||||
/**
|
||||
* Remove routes from this session. Media may be stopped on those devices.
|
||||
* Route removal requests that are not currently in {@link #getRoutes()} will be ignored.
|
||||
* Route removal requests that are not currently in {@link #getSelectedRoutes()} will be
|
||||
* ignored.
|
||||
*
|
||||
* @see #getRoutes()
|
||||
* @see #getSelectedRoutes()
|
||||
* @see Callback#onSessionInfoChanged
|
||||
*/
|
||||
public void removeRoutes(List<MediaRoute2Info> routes) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.media.MediaRoute2ProviderService;
|
||||
import android.media.RouteSessionInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -37,6 +38,9 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
public static final String ROUTE_NAME1 = "Sample Route 1";
|
||||
public static final String ROUTE_ID2 = "route_id2";
|
||||
public static final String ROUTE_NAME2 = "Sample Route 2";
|
||||
public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
|
||||
"route_id3_session_creation_failed";
|
||||
public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
|
||||
|
||||
public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
|
||||
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
|
||||
@@ -55,6 +59,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
public static final String CATEGORY_SPECIAL =
|
||||
"com.android.mediarouteprovider.CATEGORY_SPECIAL";
|
||||
|
||||
public static final int SESSION_ID_1 = 1000;
|
||||
|
||||
Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
|
||||
|
||||
private void initializeRoutes() {
|
||||
@@ -66,6 +72,10 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
.addSupportedCategory(CATEGORY_SAMPLE)
|
||||
.setDeviceType(DEVICE_TYPE_SPEAKER)
|
||||
.build();
|
||||
MediaRoute2Info route3 = new MediaRoute2Info.Builder(
|
||||
ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
|
||||
.addSupportedCategory(CATEGORY_SAMPLE)
|
||||
.build();
|
||||
MediaRoute2Info routeSpecial =
|
||||
new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY)
|
||||
.addSupportedCategory(CATEGORY_SAMPLE)
|
||||
@@ -85,6 +95,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
|
||||
mRoutes.put(route1.getId(), route1);
|
||||
mRoutes.put(route2.getId(), route2);
|
||||
mRoutes.put(route3.getId(), route3);
|
||||
mRoutes.put(routeSpecial.getId(), routeSpecial);
|
||||
mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute);
|
||||
mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
|
||||
@@ -168,12 +179,18 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
|
||||
@Override
|
||||
public void onCreateSession(String packageName, String routeId, String controlCategory,
|
||||
int sessionId) {
|
||||
int requestId) {
|
||||
if (TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
|
||||
// Tell the router that session cannot be created by passing null as sessionInfo.
|
||||
notifySessionCreated(null /* sessionInfo */, null /* controlHitns */, requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
|
||||
sessionId, packageName, controlCategory)
|
||||
SESSION_ID_1, packageName, controlCategory)
|
||||
.addSelectedRoute(routeId)
|
||||
.build();
|
||||
notifySessionCreated(sessionId, sessionInfo, null);
|
||||
notifySessionCreated(sessionInfo, null /* controlHints */, requestId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ android_test {
|
||||
static_libs: [
|
||||
"android-support-test",
|
||||
"mockito-target-minus-junit4",
|
||||
"testng"
|
||||
],
|
||||
|
||||
platform_apis: true,
|
||||
|
||||
@@ -27,19 +27,26 @@ import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_ALL;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORIES_SPECIAL;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SAMPLE;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.CATEGORY_SPECIAL;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_CATEGORY;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME;
|
||||
import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2;
|
||||
import android.media.MediaRouter2.SessionCreationCallback;
|
||||
import android.media.RouteSessionController;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
@@ -51,6 +58,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -81,13 +89,6 @@ public class MediaRouter2Test {
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSelectedRoute_afterCreation() throws Exception {
|
||||
MediaRouter2 router = MediaRouter2.getInstance(mContext);
|
||||
MediaRoute2Info initiallySelectedRoute = router.getSelectedRoute();
|
||||
assertNotNull(initiallySelectedRoute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if we get proper routes for application that has special control category.
|
||||
*/
|
||||
@@ -204,6 +205,109 @@ public class MediaRouter2Test {
|
||||
(route -> route.getVolume() == originalVolume));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestCreateSessionWithInvalidArguments() {
|
||||
MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
|
||||
String controlCategory = "controlCategory";
|
||||
Executor executor = mExecutor;
|
||||
MediaRouter2.SessionCreationCallback callback = new MediaRouter2.SessionCreationCallback();
|
||||
|
||||
// Tests null route
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> mRouter2.requestCreateSession(null, controlCategory, executor, callback));
|
||||
|
||||
// Tests null or empty control category
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mRouter2.requestCreateSession(route, null, executor, callback));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mRouter2.requestCreateSession(route, "", executor, callback));
|
||||
|
||||
// Tests null executor
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> mRouter2.requestCreateSession(route, controlCategory, null, callback));
|
||||
|
||||
// Tests null callback
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> mRouter2.requestCreateSession(route, controlCategory, executor, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestCreateSessionSuccess() throws Exception {
|
||||
final List<String> sampleControlCategory = new ArrayList<>();
|
||||
sampleControlCategory.add(CATEGORY_SAMPLE);
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
|
||||
MediaRoute2Info route = routes.get(ROUTE_ID1);
|
||||
assertNotNull(route);
|
||||
|
||||
final CountDownLatch successLatch = new CountDownLatch(1);
|
||||
final CountDownLatch failureLatch = new CountDownLatch(1);
|
||||
|
||||
// Create session with this route
|
||||
SessionCreationCallback callback = new SessionCreationCallback() {
|
||||
@Override
|
||||
public void onSessionCreated(RouteSessionController controller, Bundle controlHints) {
|
||||
assertNotNull(controller);
|
||||
assertTrue(controller.getSelectedRoutes().contains(ROUTE_ID1));
|
||||
assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getCategory()));
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreationFailed() {
|
||||
failureLatch.countDown();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
|
||||
mRouter2.registerCallback(mExecutor, new MediaRouter2.RouteCallback());
|
||||
|
||||
mRouter2.requestCreateSession(route, CATEGORY_SAMPLE, mExecutor, callback);
|
||||
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
|
||||
// onSessionCreationFailed should not be called.
|
||||
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestCreateSessionFailure() throws Exception {
|
||||
final List<String> sampleControlCategory = new ArrayList<>();
|
||||
sampleControlCategory.add(CATEGORY_SAMPLE);
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
|
||||
MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED);
|
||||
assertNotNull(route);
|
||||
|
||||
final CountDownLatch successLatch = new CountDownLatch(1);
|
||||
final CountDownLatch failureLatch = new CountDownLatch(1);
|
||||
|
||||
// Create session with this route
|
||||
SessionCreationCallback callback = new SessionCreationCallback() {
|
||||
@Override
|
||||
public void onSessionCreated(RouteSessionController controller, Bundle controlHints) {
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreationFailed() {
|
||||
failureLatch.countDown();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
|
||||
mRouter2.registerCallback(mExecutor, new MediaRouter2.RouteCallback());
|
||||
|
||||
mRouter2.requestCreateSession(route, CATEGORY_SAMPLE, mExecutor, callback);
|
||||
assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
|
||||
// onSessionCreated should not be called.
|
||||
assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestCreateSessionMultipleSessions() throws Exception {
|
||||
// TODO: Test creating multiple sessions (Check the ID of each controller)
|
||||
}
|
||||
|
||||
// Helper for getting routes easily
|
||||
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
|
||||
@@ -220,7 +324,8 @@ public class MediaRouter2Test {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
// A dummy callback is required to send control category info.
|
||||
MediaRouter2.Callback routerCallback = new MediaRouter2.Callback() {
|
||||
MediaRouter2.RouteCallback
|
||||
routeCallback = new MediaRouter2.RouteCallback() {
|
||||
@Override
|
||||
public void onRoutesAdded(List<MediaRoute2Info> routes) {
|
||||
for (int i = 0; i < routes.size(); i++) {
|
||||
@@ -233,19 +338,20 @@ public class MediaRouter2Test {
|
||||
};
|
||||
|
||||
mRouter2.setControlCategories(controlCategories);
|
||||
mRouter2.registerCallback(mExecutor, routerCallback);
|
||||
mRouter2.registerCallback(mExecutor, routeCallback);
|
||||
try {
|
||||
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
return createRouteMap(mRouter2.getRoutes());
|
||||
} finally {
|
||||
mRouter2.unregisterCallback(routerCallback);
|
||||
mRouter2.unregisterCallback(routeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void awaitOnRouteChanged(Runnable task, String routeId,
|
||||
Predicate<MediaRoute2Info> predicate) throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
MediaRouter2.Callback callback = new MediaRouter2.Callback() {
|
||||
MediaRouter2.RouteCallback
|
||||
routeCallback = new MediaRouter2.RouteCallback() {
|
||||
@Override
|
||||
public void onRoutesChanged(List<MediaRoute2Info> changed) {
|
||||
MediaRoute2Info route = createRouteMap(changed).get(routeId);
|
||||
@@ -254,12 +360,12 @@ public class MediaRouter2Test {
|
||||
}
|
||||
}
|
||||
};
|
||||
mRouter2.registerCallback(mExecutor, callback);
|
||||
mRouter2.registerCallback(mExecutor, routeCallback);
|
||||
try {
|
||||
task.run();
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
} finally {
|
||||
mRouter2.unregisterCallback(callback);
|
||||
mRouter2.unregisterCallback(routeCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
|
||||
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -27,8 +28,8 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2;
|
||||
import android.media.MediaRouter2.RouteCallback;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.os.Bundle;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
@@ -59,6 +60,9 @@ public class MediaRouterManagerTest {
|
||||
public static final String ROUTE_NAME1 = "Sample Route 1";
|
||||
public static final String ROUTE_ID2 = "route_id2";
|
||||
public static final String ROUTE_NAME2 = "Sample Route 2";
|
||||
public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
|
||||
"route_id3_session_creation_failed";
|
||||
public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
|
||||
|
||||
public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
|
||||
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
|
||||
@@ -91,7 +95,8 @@ public class MediaRouterManagerTest {
|
||||
private String mPackageName;
|
||||
|
||||
private final List<MediaRouter2Manager.Callback> mManagerCallbacks = new ArrayList<>();
|
||||
private final List<MediaRouter2.Callback> mRouterCallbacks = new ArrayList<>();
|
||||
private final List<RouteCallback> mRouteCallbacks =
|
||||
new ArrayList<>();
|
||||
|
||||
public static final List<String> CATEGORIES_ALL = new ArrayList();
|
||||
public static final List<String> CATEGORIES_SPECIAL = new ArrayList();
|
||||
@@ -123,7 +128,21 @@ public class MediaRouterManagerTest {
|
||||
clearCallbacks();
|
||||
}
|
||||
|
||||
/**
|
||||
//TODO: Move to a separate file
|
||||
@Test
|
||||
public void testMediaRoute2Info() {
|
||||
MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name")
|
||||
.build();
|
||||
MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(routeInfo1).build();
|
||||
|
||||
MediaRoute2Info routeInfo3 = new MediaRoute2Info.Builder(routeInfo1)
|
||||
.setClientPackageName(mPackageName).build();
|
||||
|
||||
assertEquals(routeInfo1, routeInfo2);
|
||||
assertNotEquals(routeInfo1, routeInfo3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if routes are added correctly when a new callback is registered.
|
||||
*/
|
||||
@Test
|
||||
@@ -149,7 +168,7 @@ public class MediaRouterManagerTest {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
addRouterCallback(new MediaRouter2.Callback());
|
||||
addRouterCallback(new RouteCallback());
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRoutesRemoved(List<MediaRoute2Info> routes) {
|
||||
@@ -162,6 +181,8 @@ public class MediaRouterManagerTest {
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: Figure out a more proper way to test.
|
||||
// (Control requests shouldn't be used in this way.)
|
||||
mRouter2.sendControlRequest(routes.get(ROUTE_ID2), new Intent(ACTION_REMOVE_ROUTE));
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
@@ -179,29 +200,30 @@ public class MediaRouterManagerTest {
|
||||
|
||||
/**
|
||||
* Tests if MR2.Callback.onRouteSelected is called when a route is selected from MR2Manager.
|
||||
*
|
||||
* TODO: Change this test so that this test check whether the route is added in a session.
|
||||
* Until then, temporailiy removing @Test annotation.
|
||||
*/
|
||||
@Test
|
||||
public void testRouterOnRouteSelected() throws Exception {
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
addManagerCallback(new MediaRouter2Manager.Callback());
|
||||
addRouterCallback(new MediaRouter2.Callback() {
|
||||
@Override
|
||||
public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
|
||||
if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
// addRouterCallback(new RouteDiscoveryCallback() {
|
||||
// @Override
|
||||
// public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
|
||||
// if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
|
||||
// latch.countDown();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
|
||||
assertNotNull(routeToSelect);
|
||||
|
||||
try {
|
||||
mManager.selectRoute(mPackageName, routeToSelect);
|
||||
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
} finally {
|
||||
mManager.unselectRoute(mPackageName);
|
||||
@@ -217,7 +239,7 @@ public class MediaRouterManagerTest {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
addRouterCallback(new MediaRouter2.Callback());
|
||||
addRouterCallback(new RouteCallback());
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRouteSelected(String packageName, MediaRoute2Info route) {
|
||||
@@ -245,7 +267,7 @@ public class MediaRouterManagerTest {
|
||||
CountDownLatch latch2 = new CountDownLatch(1);
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
addRouterCallback(new MediaRouter2.Callback());
|
||||
addRouterCallback(new RouteCallback());
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRouteSelected(String packageName, MediaRoute2Info route) {
|
||||
@@ -276,7 +298,7 @@ public class MediaRouterManagerTest {
|
||||
@Test
|
||||
public void testSingleProviderSelect() throws Exception {
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
addRouterCallback(new MediaRouter2.Callback());
|
||||
addRouterCallback(new RouteCallback());
|
||||
|
||||
awaitOnRouteChangedManager(
|
||||
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
|
||||
@@ -330,7 +352,8 @@ public class MediaRouterManagerTest {
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
|
||||
// A dummy callback is required to send control category info.
|
||||
MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
|
||||
RouteCallback
|
||||
routeCallback = new RouteCallback();
|
||||
MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRoutesAdded(List<MediaRoute2Info> routes) {
|
||||
@@ -352,12 +375,12 @@ public class MediaRouterManagerTest {
|
||||
};
|
||||
mManager.registerCallback(mExecutor, managerCallback);
|
||||
mRouter2.setControlCategories(controlCategories);
|
||||
mRouter2.registerCallback(mExecutor, routerCallback);
|
||||
mRouter2.registerCallback(mExecutor, routeCallback);
|
||||
try {
|
||||
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
return createRouteMap(mManager.getAvailableRoutes(mPackageName));
|
||||
} finally {
|
||||
mRouter2.unregisterCallback(routerCallback);
|
||||
mRouter2.unregisterCallback(routeCallback);
|
||||
mManager.unregisterCallback(managerCallback);
|
||||
}
|
||||
}
|
||||
@@ -398,9 +421,9 @@ public class MediaRouterManagerTest {
|
||||
mManager.registerCallback(mExecutor, callback);
|
||||
}
|
||||
|
||||
private void addRouterCallback(MediaRouter2.Callback callback) {
|
||||
mRouterCallbacks.add(callback);
|
||||
mRouter2.registerCallback(mExecutor, callback);
|
||||
private void addRouterCallback(RouteCallback routeCallback) {
|
||||
mRouteCallbacks.add(routeCallback);
|
||||
mRouter2.registerCallback(mExecutor, routeCallback);
|
||||
}
|
||||
|
||||
private void clearCallbacks() {
|
||||
@@ -409,9 +432,9 @@ public class MediaRouterManagerTest {
|
||||
}
|
||||
mManagerCallbacks.clear();
|
||||
|
||||
for (MediaRouter2.Callback callback : mRouterCallbacks) {
|
||||
mRouter2.unregisterCallback(callback);
|
||||
for (RouteCallback routeCallback : mRouteCallbacks) {
|
||||
mRouter2.unregisterCallback(routeCallback);
|
||||
}
|
||||
mRouterCallbacks.clear();
|
||||
mRouteCallbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.RouteSessionInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -42,6 +43,8 @@ abstract class MediaRoute2Provider {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
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 sendControlRequest(MediaRoute2Info route, Intent request);
|
||||
@@ -82,5 +85,8 @@ abstract class MediaRoute2Provider {
|
||||
void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
|
||||
@NonNull String clientPackageName, @NonNull MediaRoute2Info route,
|
||||
@Nullable Bundle controlHints, int seq);
|
||||
void onSessionCreated(@NonNull MediaRoute2Provider provider,
|
||||
@Nullable RouteSessionInfo sessionInfo, @Nullable Bundle controlHints,
|
||||
int requestId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.media;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -26,6 +27,7 @@ import android.media.IMediaRoute2ProviderClient;
|
||||
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;
|
||||
@@ -73,6 +75,16 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
pw.println(prefix + " mConnectionReady=" + mConnectionReady);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestCreateSession(String packageName, String routeId, String controlCategory,
|
||||
int requestId) {
|
||||
if (mConnectionReady) {
|
||||
mActiveConnection.requestCreateSession(packageName, routeId, controlCategory,
|
||||
requestId);
|
||||
updateBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestSelectRoute(String packageName, String routeId, int seq) {
|
||||
if (mConnectionReady) {
|
||||
@@ -268,6 +280,14 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
mCallback.onRouteSelected(this, packageName, route, controlHints, seq);
|
||||
}
|
||||
|
||||
private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
if (mActiveConnection != connection) {
|
||||
return;
|
||||
}
|
||||
mCallback.onSessionCreated(this, sessionInfo, controlHints, requestId);
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
if (mActiveConnection != null) {
|
||||
mConnectionReady = false;
|
||||
@@ -308,6 +328,15 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
mClient.dispose();
|
||||
}
|
||||
|
||||
public void requestCreateSession(String packageName, String routeId, String controlCategory,
|
||||
int requestId) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
mProvider.requestSelectRoute(packageName, routeId, seq);
|
||||
@@ -361,6 +390,12 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
mHandler.post(() -> onRouteSelected(Connection.this,
|
||||
packageName, routeId, controlHints, seq));
|
||||
}
|
||||
|
||||
void postSessionCreated(@Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo, controlHints,
|
||||
requestId));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProviderClient extends IMediaRoute2ProviderClient.Stub {
|
||||
@@ -391,5 +426,13 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
Connection connection = mConnectionRef.get();
|
||||
if (connection != null) {
|
||||
connection.postSessionCreated(sessionInfo, controlHints, requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ 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;
|
||||
@@ -55,6 +56,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* TODO: Merge this to MediaRouterService once it's finished.
|
||||
@@ -166,6 +168,24 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
|
||||
String controlCategory, int requestId) {
|
||||
Objects.requireNonNull(client, "client must not be null");
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
if (TextUtils.isEmpty(controlCategory)) {
|
||||
throw new IllegalArgumentException("controlCategory must not be empty");
|
||||
}
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
requestCreateSessionLocked(client, route, controlCategory, requestId);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendControlRequest(@NonNull IMediaRouter2Client client,
|
||||
@NonNull MediaRoute2Info route, @NonNull Intent request) {
|
||||
Objects.requireNonNull(client, "client must not be null");
|
||||
@@ -198,18 +218,6 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public void requestSelectRoute2(@NonNull IMediaRouter2Client client,
|
||||
@Nullable MediaRoute2Info route) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
requestSelectRoute2Locked(mAllClientRecords.get(client.asBinder()), false, route);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
|
||||
Objects.requireNonNull(client, "client must not be null");
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
@@ -353,6 +361,19 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
|
||||
@NonNull MediaRoute2Info route, @NonNull String controlCategory, int requestId) {
|
||||
final IBinder binder = client.asBinder();
|
||||
final Client2Record clientRecord = mAllClientRecords.get(binder);
|
||||
|
||||
if (clientRecord != null) {
|
||||
clientRecord.mUserRecord.mHandler.sendMessage(
|
||||
obtainMessage(UserHandler::requestCreateSessionOnHandler,
|
||||
clientRecord.mUserRecord.mHandler,
|
||||
clientRecord, route, controlCategory, requestId));
|
||||
}
|
||||
}
|
||||
|
||||
private void requestSelectRoute2Locked(Client2Record clientRecord, boolean selectedByManager,
|
||||
MediaRoute2Info route) {
|
||||
if (clientRecord != null) {
|
||||
@@ -569,6 +590,7 @@ class MediaRouter2ServiceImpl {
|
||||
mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this);
|
||||
}
|
||||
|
||||
// TODO: This assumes that only one client exists in a package. Is it true?
|
||||
Client2Record findClientRecordLocked(String packageName) {
|
||||
for (Client2Record clientRecord : mClientRecords) {
|
||||
if (TextUtils.equals(clientRecord.mPackageName, packageName)) {
|
||||
@@ -667,10 +689,12 @@ class MediaRouter2ServiceImpl {
|
||||
private final SystemMediaRoute2Provider mSystemProvider;
|
||||
private final ArrayList<MediaRoute2Provider> mMediaProviders =
|
||||
new ArrayList<>();
|
||||
private final List<MediaRoute2ProviderInfo> mProviderInfos = new ArrayList<>();
|
||||
|
||||
private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>();
|
||||
private final CopyOnWriteArrayList<SessionCreationRequest> mSessionCreationRequests =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
private boolean mRunning;
|
||||
private boolean mProviderInfosUpdateScheduled;
|
||||
|
||||
UserHandler(MediaRouter2ServiceImpl service, UserRecord userRecord) {
|
||||
super(Looper.getMainLooper(), null, true);
|
||||
@@ -721,23 +745,31 @@ class MediaRouter2ServiceImpl {
|
||||
controlHints, seq));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreated(@NonNull MediaRoute2Provider provider,
|
||||
@Nullable RouteSessionInfo sessionInfo, @Nullable Bundle controlHints,
|
||||
int requestId) {
|
||||
sendMessage(PooledLambda.obtainMessage(UserHandler::handleCreateSessionResultOnHandler,
|
||||
this, provider, sessionInfo, controlHints, requestId));
|
||||
}
|
||||
|
||||
private void updateProvider(MediaRoute2Provider provider) {
|
||||
int providerIndex = getProviderInfoIndex(provider.getUniqueId());
|
||||
MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
|
||||
MediaRoute2ProviderInfo prevInfo =
|
||||
(providerIndex < 0) ? null : mProviderInfos.get(providerIndex);
|
||||
(providerIndex < 0) ? null : mLastProviderInfos.get(providerIndex);
|
||||
|
||||
if (Objects.equals(prevInfo, providerInfo)) return;
|
||||
|
||||
if (prevInfo == null) {
|
||||
mProviderInfos.add(providerInfo);
|
||||
mLastProviderInfos.add(providerInfo);
|
||||
Collection<MediaRoute2Info> addedRoutes = providerInfo.getRoutes();
|
||||
if (addedRoutes.size() > 0) {
|
||||
sendMessage(PooledLambda.obtainMessage(UserHandler::notifyRoutesAddedToClients,
|
||||
this, getClients(), new ArrayList<>(addedRoutes)));
|
||||
}
|
||||
} else if (providerInfo == null) {
|
||||
mProviderInfos.remove(prevInfo);
|
||||
mLastProviderInfos.remove(prevInfo);
|
||||
Collection<MediaRoute2Info> removedRoutes = prevInfo.getRoutes();
|
||||
if (removedRoutes.size() > 0) {
|
||||
sendMessage(PooledLambda.obtainMessage(
|
||||
@@ -745,7 +777,7 @@ class MediaRouter2ServiceImpl {
|
||||
this, getClients(), new ArrayList<>(removedRoutes)));
|
||||
}
|
||||
} else {
|
||||
mProviderInfos.set(providerIndex, providerInfo);
|
||||
mLastProviderInfos.set(providerIndex, providerInfo);
|
||||
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
|
||||
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
|
||||
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
|
||||
@@ -794,8 +826,8 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
private int getProviderInfoIndex(String providerId) {
|
||||
for (int i = 0; i < mProviderInfos.size(); i++) {
|
||||
MediaRoute2ProviderInfo providerInfo = mProviderInfos.get(i);
|
||||
for (int i = 0; i < mLastProviderInfos.size(); i++) {
|
||||
MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i);
|
||||
if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
|
||||
return i;
|
||||
}
|
||||
@@ -803,6 +835,96 @@ class MediaRouter2ServiceImpl {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void requestCreateSessionOnHandler(Client2Record clientRecord,
|
||||
MediaRoute2Info route, String controlCategory, int requestId) {
|
||||
|
||||
final MediaRoute2Provider provider = findProvider(route.getProviderId());
|
||||
if (provider == null) {
|
||||
Slog.w(TAG, "Ignoring session creation request since no provider found for"
|
||||
+ " given route=" + route);
|
||||
notifySessionCreationFailed(clientRecord, requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!route.getSupportedCategories().contains(controlCategory)) {
|
||||
Slog.w(TAG, "Ignoring session creation request since the given route=" + route
|
||||
+ " doesn't support the given category=" + controlCategory);
|
||||
notifySessionCreationFailed(clientRecord, requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Apply timeout for each request (How many seconds should we wait?)
|
||||
SessionCreationRequest request = new SessionCreationRequest(
|
||||
clientRecord, route, controlCategory, requestId);
|
||||
mSessionCreationRequests.add(request);
|
||||
|
||||
provider.requestCreateSession(clientRecord.mPackageName, route.getId(),
|
||||
controlCategory, requestId);
|
||||
}
|
||||
|
||||
private void handleCreateSessionResultOnHandler(
|
||||
@NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo,
|
||||
@Nullable Bundle controlHints, int requestId) {
|
||||
SessionCreationRequest matchingRequest = null;
|
||||
for (SessionCreationRequest request : mSessionCreationRequests) {
|
||||
if (request.mRequestId == requestId
|
||||
&& TextUtils.equals(
|
||||
request.mRoute.getProviderId(), provider.getUniqueId())) {
|
||||
matchingRequest = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingRequest == null) {
|
||||
Slog.w(TAG, "Ignoring session creation result for unknown request. "
|
||||
+ "requestId=" + requestId + ", sessionInfo=" + sessionInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionInfo == null) {
|
||||
// Failed
|
||||
notifySessionCreationFailed(matchingRequest.mClientRecord, requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
String originalRouteId = matchingRequest.mRoute.getId();
|
||||
String originalCategory = matchingRequest.mControlCategory;
|
||||
if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
|
||||
|| !TextUtils.equals(originalCategory, sessionInfo.getControlCategory())) {
|
||||
Slog.w(TAG, "Created session doesn't match the original request."
|
||||
+ " originalRouteId=" + originalRouteId
|
||||
+ ", originalCategory=" + originalCategory
|
||||
+ ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
|
||||
notifySessionCreationFailed(matchingRequest.mClientRecord, requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Succeeded
|
||||
notifySessionCreated(matchingRequest.mClientRecord, sessionInfo, controlHints,
|
||||
requestId);
|
||||
// TODO: Tell managers for the session creation
|
||||
}
|
||||
|
||||
private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo,
|
||||
Bundle controlHints, int requestId) {
|
||||
try {
|
||||
clientRecord.mClient.notifySessionCreated(sessionInfo, controlHints, requestId);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify client of the session creation."
|
||||
+ " Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySessionCreationFailed(Client2Record clientRecord, int requestId) {
|
||||
try {
|
||||
clientRecord.mClient.notifySessionCreated(
|
||||
null /* sessionInfo */, null /* controlHints */, requestId);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify client of the session creation failure."
|
||||
+ " Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSelectedRoute(MediaRoute2ProviderProxy provider,
|
||||
String clientPackageName, MediaRoute2Info selectedRoute, Bundle controlHints,
|
||||
int seq) {
|
||||
@@ -858,6 +980,12 @@ class MediaRouter2ServiceImpl {
|
||||
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. "
|
||||
@@ -951,7 +1079,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
private void notifyRoutesToClient(IMediaRouter2Client client) {
|
||||
List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) {
|
||||
for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
|
||||
routes.addAll(providerInfo.getRoutes());
|
||||
}
|
||||
if (routes.size() == 0) {
|
||||
@@ -964,13 +1092,9 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove notifyRouteSelected* methods
|
||||
private void notifyRouteSelectedToClient(IMediaRouter2Client client,
|
||||
MediaRoute2Info route, int reason, Bundle controlHints) {
|
||||
try {
|
||||
client.notifyRouteSelected(route, reason, controlHints);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify routes selected. Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients,
|
||||
@@ -1008,7 +1132,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
private void notifyRoutesToManager(IMediaRouter2Manager manager) {
|
||||
List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) {
|
||||
for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
|
||||
routes.addAll(providerInfo.getRoutes());
|
||||
}
|
||||
if (routes.size() == 0) {
|
||||
@@ -1085,5 +1209,21 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final class SessionCreationRequest {
|
||||
public final Client2Record mClientRecord;
|
||||
public final MediaRoute2Info mRoute;
|
||||
public final String mControlCategory;
|
||||
public final int mRequestId;
|
||||
|
||||
SessionCreationRequest(@NonNull Client2Record clientRecord,
|
||||
@NonNull MediaRoute2Info route,
|
||||
@NonNull String controlCategory, int requestId) {
|
||||
mClientRecord = clientRecord;
|
||||
mRoute = route;
|
||||
mControlCategory = controlCategory;
|
||||
mRequestId = requestId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,8 +457,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void requestSelectRoute2(IMediaRouter2Client client, MediaRoute2Info route) {
|
||||
mService2.requestSelectRoute2(client, route);
|
||||
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
|
||||
String controlCategory, int requestId) {
|
||||
mService2.requestCreateSession(client, route, controlCategory, requestId);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
|
||||
@@ -88,6 +88,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
initializeRoutes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestCreateSession(String packageName, String routeId, String controlCategory,
|
||||
int requestId) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
//TODO: implement method
|
||||
@Override
|
||||
public void requestSelectRoute(@NonNull String packageName, @NonNull String routeId, int seq) {
|
||||
|
||||
Reference in New Issue
Block a user