Commit d9ca8389 authored by Zhiqiang Zhang's avatar Zhiqiang Zhang Committed by Commit Bot

[Android MR] Misc fixes for getting Cast to work

This CL includes the following changes:

* Register SessionManagerListener properly so CafMRP and the page can
  get the correct state.
* Store the pending request only in CafBaseMRP and CastSessionController
  will get it from the provider.
* When starting a session, only re-select a route after the route has
  been unselected. This is to avoid rapid selection/unselection to cause
  Android MediaRouter to work improperly.
* Several minor fixes to avoid crashing in the code flow.

Bug: 711860
Change-Id: I8e4e46abe9421908a5d6c7e221a68feb891bbfa3
Reviewed-on: https://chromium-review.googlesource.com/1196166
Commit-Queue: Zhiqiang Zhang <zqzhang@chromium.org>
Reviewed-by: default avatarThomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#587914}
parent 366cb525
...@@ -10,6 +10,7 @@ import android.support.v7.media.MediaRouteSelector; ...@@ -10,6 +10,7 @@ import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter; import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo; import android.support.v7.media.MediaRouter.RouteInfo;
import com.google.android.gms.cast.CastStatusCodes;
import com.google.android.gms.cast.framework.CastSession; import com.google.android.gms.cast.framework.CastSession;
import com.google.android.gms.cast.framework.SessionManagerListener; import com.google.android.gms.cast.framework.SessionManagerListener;
...@@ -149,6 +150,8 @@ public abstract class CafBaseMediaRouteProvider ...@@ -149,6 +150,8 @@ public abstract class CafBaseMediaRouteProvider
public final void createRoute(String sourceId, String sinkId, String presentationId, public final void createRoute(String sourceId, String sinkId, String presentationId,
String origin, int tabId, boolean isIncognito, int nativeRequestId) { String origin, int tabId, boolean isIncognito, int nativeRequestId) {
Log.d(TAG, "createRoute"); Log.d(TAG, "createRoute");
CastUtils.getCastContext().getSessionManager().addSessionManagerListener(
this, CastSession.class);
if (mPendingCreateRouteRequestInfo != null) { if (mPendingCreateRouteRequestInfo != null) {
// TODO(zqzhang): do something. // TODO(zqzhang): do something.
} }
...@@ -165,10 +168,21 @@ public abstract class CafBaseMediaRouteProvider ...@@ -165,10 +168,21 @@ public abstract class CafBaseMediaRouteProvider
return; return;
} }
mPendingCreateRouteRequestInfo = new CreateRouteRequestInfo( MediaRouter.RouteInfo targetRouteInfo = null;
source, sink, presentationId, origin, tabId, isIncognito, nativeRequestId); for (MediaRouter.RouteInfo routeInfo : getAndroidMediaRouter().getRoutes()) {
if (routeInfo.getId().equals(sink.getId())) {
targetRouteInfo = routeInfo;
break;
}
}
if (targetRouteInfo == null) {
mManager.onRouteRequestError("The sink does not exist", nativeRequestId);
}
mSessionController.requestSessionLaunch(mPendingCreateRouteRequestInfo); mPendingCreateRouteRequestInfo = new CreateRouteRequestInfo(source, sink, presentationId,
origin, tabId, isIncognito, nativeRequestId, targetRouteInfo);
mSessionController.requestSessionLaunch();
} }
@Override @Override
...@@ -226,23 +240,33 @@ public abstract class CafBaseMediaRouteProvider ...@@ -226,23 +240,33 @@ public abstract class CafBaseMediaRouteProvider
@Override @Override
public final void onSessionEnding(CastSession session) { public final void onSessionEnding(CastSession session) {
mSessionController.detachFromCastSession(session); handleSessionEnd(/* error= */ null);
getAndroidMediaRouter().selectRoute(getAndroidMediaRouter().getDefaultRoute());
removeAllRoutesWithError(/* error= */ null);
} }
@Override @Override
public final void onSessionEnded(CastSession session, int error) {} public final void onSessionEnded(CastSession session, int error) {
handleSessionEnd((error == CastStatusCodes.SUCCESS)
? null
: ("Session ended with error code " + error));
}
@Override @Override
public final void onSessionSuspended(CastSession session, int reason) { public final void onSessionSuspended(CastSession session, int reason) {
mSessionController.detachFromCastSession(session); mSessionController.detachFromCastSession();
} }
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// SessionManagerListener implementation end // SessionManagerListener implementation end
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
private void handleSessionEnd(String error) {
mSessionController.detachFromCastSession();
getAndroidMediaRouter().selectRoute(getAndroidMediaRouter().getDefaultRoute());
removeAllRoutesWithError(error);
CastUtils.getCastContext().getSessionManager().removeSessionManagerListener(
this, CastSession.class);
}
public @NonNull MediaRouter getAndroidMediaRouter() { public @NonNull MediaRouter getAndroidMediaRouter() {
return mAndroidMediaRouter; return mAndroidMediaRouter;
} }
...@@ -290,4 +314,8 @@ public abstract class CafBaseMediaRouteProvider ...@@ -290,4 +314,8 @@ public abstract class CafBaseMediaRouteProvider
public FlingingController getFlingingController(String routeId) { public FlingingController getFlingingController(String routeId) {
return null; return null;
} }
public CreateRouteRequestInfo getPendingCreateRouteRequestInfo() {
return mPendingCreateRouteRequestInfo;
}
} }
...@@ -157,7 +157,7 @@ public class CafMediaRouteProvider extends CafBaseMediaRouteProvider { ...@@ -157,7 +157,7 @@ public class CafMediaRouteProvider extends CafBaseMediaRouteProvider {
clientRecord.routeId, sink, clientRecord.clientId, "cast"); clientRecord.routeId, sink, clientRecord.clientId, "cast");
} }
mMessageHandler.onSessionStarted(sessionController()); mMessageHandler.onSessionStarted();
sessionController().getSession().getRemoteMediaClient().requestStatus(); sessionController().getSession().getRemoteMediaClient().requestStatus();
} }
...@@ -194,7 +194,7 @@ public class CafMediaRouteProvider extends CafBaseMediaRouteProvider { ...@@ -194,7 +194,7 @@ public class CafMediaRouteProvider extends CafBaseMediaRouteProvider {
private CafMediaRouteProvider(MediaRouter androidMediaRouter, MediaRouteManager manager) { private CafMediaRouteProvider(MediaRouter androidMediaRouter, MediaRouteManager manager) {
super(androidMediaRouter, manager); super(androidMediaRouter, manager);
mMessageHandler = new CafMessageHandler(this); mMessageHandler = new CafMessageHandler(this, sessionController());
} }
private boolean canJoinExistingSession( private boolean canJoinExistingSession(
......
...@@ -72,7 +72,7 @@ public class CafMessageHandler { ...@@ -72,7 +72,7 @@ public class CafMessageHandler {
// The reference to CastSession, only valid after calling {@link onSessionCreated}, and will be // The reference to CastSession, only valid after calling {@link onSessionCreated}, and will be
// reset to null when calling {@link onApplicationStopped}. // reset to null when calling {@link onApplicationStopped}.
private CastSessionController mSessionController; private final CastSessionController mSessionController;
private final CafMediaRouteProvider mRouteProvider; private final CafMediaRouteProvider mRouteProvider;
private Handler mHandler; private Handler mHandler;
...@@ -95,10 +95,12 @@ public class CafMessageHandler { ...@@ -95,10 +95,12 @@ public class CafMessageHandler {
* @param session The {@link CastSession} for communicating with the Cast SDK. * @param session The {@link CastSession} for communicating with the Cast SDK.
* @param provider The {@link CafMediaRouteProvider} for communicating with the page. * @param provider The {@link CafMediaRouteProvider} for communicating with the page.
*/ */
public CafMessageHandler(CafMediaRouteProvider provider) { public CafMessageHandler(
CafMediaRouteProvider provider, CastSessionController sessionController) {
mRouteProvider = provider; mRouteProvider = provider;
mRequests = new SparseArray<RequestRecord>(); mRequests = new SparseArray<RequestRecord>();
mStopRequests = new ArrayMap<String, Queue<Integer>>(); mStopRequests = new ArrayMap<String, Queue<Integer>>();
mSessionController = sessionController;
mVolumeRequests = new ArrayDeque<RequestRecord>(); mVolumeRequests = new ArrayDeque<RequestRecord>();
mHandler = new Handler(); mHandler = new Handler();
...@@ -141,15 +143,12 @@ public class CafMessageHandler { ...@@ -141,15 +143,12 @@ public class CafMessageHandler {
* Set the session when a session is started, and notify all clients that are not connected. * Set the session when a session is started, and notify all clients that are not connected.
* @param session The newly created session. * @param session The newly created session.
*/ */
public void onSessionStarted(CastSessionController sessionController) { public void onSessionStarted() {
mSessionController = sessionController;
for (ClientRecord client : mRouteProvider.getClientIdToRecords().values()) { for (ClientRecord client : mRouteProvider.getClientIdToRecords().values()) {
if (!client.isConnected) continue; if (!client.isConnected) continue;
notifySessionConnectedToClient(client.clientId); notifySessionConnectedToClient(client.clientId);
} }
// TODO(zqzhang): Register namespaces.
// TODO(zqzhang): Request media status.
} }
/** Notify a client that a session has connected. */ /** Notify a client that a session has connected. */
...@@ -438,7 +437,7 @@ public class CafMessageHandler { ...@@ -438,7 +437,7 @@ public class CafMessageHandler {
@VisibleForTesting @VisibleForTesting
boolean sendJsonCastMessage(JSONObject message, final String namespace, final String clientId, boolean sendJsonCastMessage(JSONObject message, final String namespace, final String clientId,
final int sequenceNumber) throws JSONException { final int sequenceNumber) throws JSONException {
if (mSessionController == null || !mSessionController.isConnected()) return false; if (!mSessionController.isConnected()) return false;
removeNullFields(message); removeNullFields(message);
...@@ -553,7 +552,6 @@ public class CafMessageHandler { ...@@ -553,7 +552,6 @@ public class CafMessageHandler {
} }
mStopRequests.remove(clientId); mStopRequests.remove(clientId);
} }
mSessionController = null;
} }
/** /**
...@@ -659,7 +657,7 @@ public class CafMessageHandler { ...@@ -659,7 +657,7 @@ public class CafMessageHandler {
* @return A message containing the information of the {@link CastSession}. * @return A message containing the information of the {@link CastSession}.
*/ */
public String buildSessionMessage() { public String buildSessionMessage() {
if (mSessionController == null || !mSessionController.isConnected()) return "{}"; if (!mSessionController.isConnected()) return "{}";
try { try {
// "volume" is a part of "receiver" initialized below. // "volume" is a part of "receiver" initialized below.
...@@ -778,7 +776,7 @@ public class CafMessageHandler { ...@@ -778,7 +776,7 @@ public class CafMessageHandler {
private boolean sendStringCastMessage( private boolean sendStringCastMessage(
String message, String namespace, String clientId, int sequenceNumber) { String message, String namespace, String clientId, int sequenceNumber) {
if (mSessionController == null || !mSessionController.isConnected()) return false; if (!mSessionController.isConnected()) return false;
PendingResult<Status> pendingResult = PendingResult<Status> pendingResult =
mSessionController.getSession().sendMessage(namespace, message); mSessionController.getSession().sendMessage(namespace, message);
......
...@@ -19,6 +19,7 @@ public class CastOptionsProvider implements OptionsProvider { ...@@ -19,6 +19,7 @@ public class CastOptionsProvider implements OptionsProvider {
return new CastOptions.Builder() return new CastOptions.Builder()
.setCastMediaOptions(null) .setCastMediaOptions(null)
.setEnableReconnectionService(false) .setEnableReconnectionService(false)
.setResumeSavedSession(false)
.build(); .build();
} }
......
...@@ -10,6 +10,7 @@ import com.google.android.gms.cast.ApplicationMetadata; ...@@ -10,6 +10,7 @@ import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast; import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.CastDevice; import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.framework.CastSession; import com.google.android.gms.cast.framework.CastSession;
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.chrome.browser.media.router.CastSessionUtil; import org.chromium.chrome.browser.media.router.CastSessionUtil;
...@@ -33,26 +34,35 @@ public class CastSessionController { ...@@ -33,26 +34,35 @@ public class CastSessionController {
private final CafBaseMediaRouteProvider mProvider; private final CafBaseMediaRouteProvider mProvider;
private MediaSink mSink; private MediaSink mSink;
private MediaSource mSource; private MediaSource mSource;
private List<String> mNamespaces; private List<String> mNamespaces = new ArrayList<String>();
private final CastListener mCastListener; private final CastListener mCastListener;
private final MediaRouter.Callback mMediaRouterCallbackForSessionLaunch;
public CastSessionController(CafBaseMediaRouteProvider provider) { public CastSessionController(CafBaseMediaRouteProvider provider) {
mProvider = provider; mProvider = provider;
mCastListener = new CastListener(); mCastListener = new CastListener();
mMediaRouterCallbackForSessionLaunch = new MediaRouterCallbackForSessionLaunch();
} }
public void requestSessionLaunch(CreateRouteRequestInfo request) { public void requestSessionLaunch() {
CreateRouteRequestInfo request = mProvider.getPendingCreateRouteRequestInfo();
mSource = request.source; mSource = request.source;
mSink = request.sink; mSink = request.sink;
CastUtils.getCastContext().setReceiverApplicationId(request.source.getApplicationId()); CastUtils.getCastContext().setReceiverApplicationId(request.source.getApplicationId());
for (MediaRouter.RouteInfo routeInfo : mProvider.getAndroidMediaRouter().getRoutes()) { if (request.routeInfo.isSelected()) {
if (routeInfo.getId().equals(request.sink.getId())) { // If a route has just been selected, CAF might not be ready yet before setting the app
// Unselect and then select so that CAF will get notified of the selection. // ID. So unselect and select the route will let CAF be aware that the route has been
mProvider.getAndroidMediaRouter().unselect(MediaRouter.UNSELECT_REASON_UNKNOWN); // selected thus it can start the session.
routeInfo.select(); //
break; // An issue of this workaround is that if a route is unselected and selected in a very
} // short time, the selection might be ignored by MediaRouter, so put the reselection in
// a callback.
mProvider.getAndroidMediaRouter().addCallback(
mSource.buildRouteSelector(), mMediaRouterCallbackForSessionLaunch);
mProvider.getAndroidMediaRouter().unselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
} else {
request.routeInfo.select();
} }
} }
...@@ -68,6 +78,10 @@ public class CastSessionController { ...@@ -68,6 +78,10 @@ public class CastSessionController {
return mCastSession; return mCastSession;
} }
public RemoteMediaClient getRemoteMediaClient() {
return mCastSession.getRemoteMediaClient();
}
public void endSession() { public void endSession() {
MediaRouter mediaRouter = mProvider.getAndroidMediaRouter(); MediaRouter mediaRouter = mProvider.getAndroidMediaRouter();
mediaRouter.selectRoute(mediaRouter.getDefaultRoute()); mediaRouter.selectRoute(mediaRouter.getDefaultRoute());
...@@ -111,9 +125,13 @@ public class CastSessionController { ...@@ -111,9 +125,13 @@ public class CastSessionController {
public void attachToCastSession(CastSession session) { public void attachToCastSession(CastSession session) {
mCastSession = session; mCastSession = session;
mCastSession.addCastListener(mCastListener); mCastSession.addCastListener(mCastListener);
updateNamespaces();
} }
public void detachFromCastSession(CastSession session) { public void detachFromCastSession() {
if (mCastSession == null) return;
mNamespaces.clear();
mCastSession.removeCastListener(mCastListener); mCastSession.removeCastListener(mCastListener);
mCastSession = null; mCastSession = null;
} }
...@@ -194,4 +212,18 @@ public class CastSessionController { ...@@ -194,4 +212,18 @@ public class CastSessionController {
CafMessageHandler messageHandler = mProvider.getMessageHandler(); CafMessageHandler messageHandler = mProvider.getMessageHandler();
messageHandler.onMessageReceived(namespace, message); messageHandler.onMessageReceived(namespace, message);
} }
private class MediaRouterCallbackForSessionLaunch extends MediaRouter.Callback {
@Override
public void onRouteUnselected(MediaRouter mediaRouter, MediaRouter.RouteInfo routeInfo) {
if (mProvider.getPendingCreateRouteRequestInfo() == null) return;
if (routeInfo.getId().equals(
mProvider.getPendingCreateRouteRequestInfo().routeInfo.getId())) {
routeInfo.select();
mProvider.getAndroidMediaRouter().removeCallback(
mMediaRouterCallbackForSessionLaunch);
}
}
}
} }
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.media.router.caf; package org.chromium.chrome.browser.media.router.caf;
import android.support.v7.media.MediaRouter;
import org.chromium.chrome.browser.media.router.MediaSink; import org.chromium.chrome.browser.media.router.MediaSink;
import org.chromium.chrome.browser.media.router.MediaSource; import org.chromium.chrome.browser.media.router.MediaSource;
...@@ -16,9 +18,11 @@ public class CreateRouteRequestInfo { ...@@ -16,9 +18,11 @@ public class CreateRouteRequestInfo {
public final int tabId; public final int tabId;
public final boolean isIncognito; public final boolean isIncognito;
public final int nativeRequestId; public final int nativeRequestId;
public final MediaRouter.RouteInfo routeInfo;
public CreateRouteRequestInfo(MediaSource source, MediaSink sink, String presentationId, public CreateRouteRequestInfo(MediaSource source, MediaSink sink, String presentationId,
String origin, int tabId, boolean isIncognito, int nativeRequestId) { String origin, int tabId, boolean isIncognito, int nativeRequestId,
MediaRouter.RouteInfo routeInfo) {
this.source = source; this.source = source;
this.sink = sink; this.sink = sink;
this.presentationId = presentationId; this.presentationId = presentationId;
...@@ -26,5 +30,6 @@ public class CreateRouteRequestInfo { ...@@ -26,5 +30,6 @@ public class CreateRouteRequestInfo {
this.tabId = tabId; this.tabId = tabId;
this.isIncognito = isIncognito; this.isIncognito = isIncognito;
this.nativeRequestId = nativeRequestId; this.nativeRequestId = nativeRequestId;
this.routeInfo = routeInfo;
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment