Index: chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java |
index df8e65d4d43b54643c1ec6e632cc9cc87d35ca95..d4cc4db8c07307cb8885a90d8cb5e540e66aa653 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java |
@@ -12,6 +12,7 @@ import android.support.v7.media.MediaRouter.RouteInfo; |
import org.chromium.chrome.browser.media.router.ChromeMediaRouter; |
import org.chromium.chrome.browser.media.router.DiscoveryDelegate; |
+import org.chromium.chrome.browser.media.router.MediaRoute; |
import org.chromium.chrome.browser.media.router.MediaRouteManager; |
import org.chromium.chrome.browser.media.router.MediaRouteProvider; |
import org.chromium.chrome.browser.media.router.RouteController; |
@@ -31,8 +32,6 @@ import javax.annotation.Nullable; |
public class CastMediaRouteProvider |
implements MediaRouteProvider, DiscoveryDelegate, RouteDelegate { |
- private static final String TAG = "MediaRouter"; |
- |
private static final String AUTO_JOIN_PRESENTATION_ID = "auto-join"; |
private static final String PRESENTATION_ID_SESSION_ID_PREFIX = "cast-session_"; |
@@ -41,10 +40,12 @@ public class CastMediaRouteProvider |
private final MediaRouteManager mManager; |
private final Map<String, DiscoveryCallback> mDiscoveryCallbacks = |
new HashMap<String, DiscoveryCallback>(); |
- private final Map<String, CastRouteController> mRoutes = |
- new HashMap<String, CastRouteController>(); |
- private final Map<String, String> mClientIdsToRouteIds = new HashMap<String, String>(); |
+ private final Map<String, MediaRoute> mRoutes = new HashMap<String, MediaRoute>(); |
+ private ClientRecord mLastRemovedRouteRecord; |
+ private final List<ClientRecord> mClientRecords = new ArrayList<ClientRecord>(); |
+ // There can be only one Cast session at the same time on Android. |
+ private SessionRecord mSession; |
private CreateRouteRequest mPendingCreateRouteRequest; |
private Handler mHandler = new Handler(); |
@@ -76,15 +77,33 @@ public class CastMediaRouteProvider |
} |
@Override |
- public void onRouteCreated(int requestId, RouteController route, boolean wasLaunched) { |
- assert route instanceof CastRouteController; |
+ public void onRouteCreated(int requestId, MediaRoute route, RouteController routeController) { |
+ String routeId = route.id; |
+ |
+ MediaSource source = MediaSource.from(route.sourceId); |
+ final String clientId = source.getClientId(); |
+ if (clientId != null && getClientRecordByClientId(clientId) == null) { |
+ mClientRecords.add(new ClientRecord( |
+ routeId, |
+ clientId, |
+ source.getApplicationId(), |
+ source.getAutoJoinPolicy(), |
+ routeController.getOrigin(), |
+ routeController.getTabId())); |
+ } |
+ |
+ if (mSession == null) { |
+ mSession = new SessionRecord(route.sinkId, (CastRouteController) routeController); |
+ } |
+ mSession.routeIds.add(routeId); |
- String routeId = route.getRouteId(); |
+ if (clientId != null && !mSession.clientIds.contains(clientId)) { |
+ mSession.clientIds.add(clientId); |
+ } |
- mRoutes.put(routeId, (CastRouteController) route); |
- mClientIdsToRouteIds.put(MediaSource.from(route.getSourceId()).getClientId(), routeId); |
+ mRoutes.put(routeId, route); |
- mManager.onRouteCreated(routeId, route.getSinkId(), requestId, this, wasLaunched); |
+ mManager.onRouteCreated(routeId, route.sinkId, requestId, this, true); |
} |
@Override |
@@ -93,8 +112,20 @@ public class CastMediaRouteProvider |
} |
@Override |
- public void onRouteClosed(RouteController route) { |
- mClientIdsToRouteIds.remove(MediaSource.from(route.getSourceId()).getClientId()); |
+ public void onRouteClosed(String routeId) { |
+ mLastRemovedRouteRecord = getClientRecordByRouteId(routeId); |
+ mClientRecords.remove(mLastRemovedRouteRecord); |
+ |
+ mManager.onRouteClosed(routeId); |
+ if (mSession != null) { |
+ for (String sessionRouteId : mSession.routeIds) { |
+ if (sessionRouteId.equals(routeId)) continue; |
+ |
+ mManager.onRouteClosed(routeId); |
+ } |
+ } |
+ |
+ mSession = null; |
if (mPendingCreateRouteRequest != null) { |
mPendingCreateRouteRequest.start(mApplicationContext); |
@@ -102,7 +133,6 @@ public class CastMediaRouteProvider |
} else if (mAndroidMediaRouter != null) { |
mAndroidMediaRouter.selectRoute(mAndroidMediaRouter.getDefaultRoute()); |
} |
- mManager.onRouteClosed(route.getRouteId()); |
} |
@Override |
@@ -194,111 +224,113 @@ public class CastMediaRouteProvider |
} |
@Override |
- public void createRoute(String sourceId, String sinkId, String routeId, String origin, |
+ public void createRoute(String sourceId, String sinkId, String presentationId, String origin, |
int tabId, int nativeRequestId) { |
if (mAndroidMediaRouter == null) { |
mManager.onRouteRequestError("Not supported", nativeRequestId); |
return; |
} |
- MediaSource source = MediaSource.from(sourceId); |
- if (source == null || source.getClientId() == null) { |
- mManager.onRouteRequestError("Unsupported presentation URL", nativeRequestId); |
- return; |
- } |
- |
MediaSink sink = MediaSink.fromSinkId(sinkId, mAndroidMediaRouter); |
if (sink == null) { |
mManager.onRouteRequestError("No sink", nativeRequestId); |
return; |
} |
+ MediaSource source = MediaSource.from(sourceId); |
+ if (source == null) { |
+ mManager.onRouteRequestError("Unsupported presentation URL", nativeRequestId); |
+ return; |
+ } |
+ |
CreateRouteRequest createRouteRequest = new CreateRouteRequest( |
- source, sink, routeId, origin, tabId, nativeRequestId, this); |
- String existingRouteId = mClientIdsToRouteIds.get(source.getClientId()); |
- if (existingRouteId == null) { |
- createRouteRequest.start(mApplicationContext); |
+ source, sink, presentationId, origin, tabId, nativeRequestId, this); |
+ |
+ // TODO(avayvod): Implement ReceiverAction.CAST, https://crbug.com/561470. |
+ |
+ // Since we only have one session, close it before starting a new one. |
+ if (mSession != null && !mSession.isStopping) { |
+ mPendingCreateRouteRequest = createRouteRequest; |
+ mSession.isStopping = true; |
+ mSession.session.close(); |
return; |
} |
- mPendingCreateRouteRequest = createRouteRequest; |
- closeRoute(existingRouteId); |
+ createRouteRequest.start(mApplicationContext); |
} |
@Override |
public void joinRoute(String sourceId, String presentationId, String origin, int tabId, |
int nativeRequestId) { |
+ if (mSession == null) { |
+ mManager.onRouteRequestError("No presentation", nativeRequestId); |
+ return; |
+ } |
+ |
MediaSource source = MediaSource.from(sourceId); |
if (source == null || source.getClientId() == null) { |
mManager.onRouteRequestError("Unsupported presentation URL", nativeRequestId); |
return; |
} |
- CastRouteController routeToJoin = null; |
- if (AUTO_JOIN_PRESENTATION_ID.equals(presentationId)) { |
- routeToJoin = autoJoinRoute(source, origin, tabId); |
- } else if (presentationId.startsWith(PRESENTATION_ID_SESSION_ID_PREFIX)) { |
- String sessionId = presentationId.substring(PRESENTATION_ID_SESSION_ID_PREFIX.length()); |
- for (CastRouteController route : mRoutes.values()) { |
- if (sessionId.equals(route.getSessionId())) { |
- routeToJoin = route; |
- break; |
- } |
- } |
- } else { |
- for (CastRouteController route : mRoutes.values()) { |
- String[] routeIdComponents = ChromeMediaRouter |
- .parseMediaRouteId(route.getRouteId()); |
- assert routeIdComponents != null; |
- |
- if (presentationId.equals(routeIdComponents[0])) { |
- routeToJoin = route; |
- break; |
- } |
- } |
- } |
+ // TODO(avayvod): Implement _receiver-action route for ReceiverAction messages, |
+ // https://crbug.com/561470. |
- if (routeToJoin == null) { |
+ if (!canJoinExistingSession(presentationId, origin, tabId, source)) { |
mManager.onRouteRequestError("No matching route", nativeRequestId); |
return; |
} |
- String mediaRouteId = ChromeMediaRouter.createMediaRouteId( |
- presentationId, routeToJoin.getSinkId(), sourceId); |
- CastRouteController joinedController = routeToJoin.createJoinedController(mediaRouteId, |
- origin, tabId, MediaSource.from(sourceId)); |
- mRoutes.put(mediaRouteId, joinedController); |
- |
- this.onRouteCreated(nativeRequestId, joinedController, false); |
+ MediaRoute route = new MediaRoute(mSession.session.getSinkId(), sourceId, presentationId); |
+ mRoutes.put(route.id, route); |
- if (routeToJoin.isDetached()) mManager.onRouteClosed(routeToJoin.getRouteId()); |
+ this.onRouteCreated(nativeRequestId, route, mSession.session); |
} |
@Override |
public void closeRoute(String routeId) { |
- RouteController route = mRoutes.remove(routeId); |
- if (route == null) return; |
+ MediaRoute route = mRoutes.get(routeId); |
+ |
+ if (route == null) { |
+ onRouteClosed(routeId); |
+ return; |
+ } |
+ |
+ if (mSession == null || !mSession.routeIds.contains(routeId)) { |
+ mRoutes.remove(routeId); |
+ |
+ onRouteClosed(routeId); |
+ return; |
+ } |
+ |
+ // TODO(avayvod): Implement ReceiverAction.STOP, https://crbug.com/561470. |
+ |
+ if (mSession.isStopping) return; |
- route.close(); |
+ mSession.isStopping = true; |
+ mSession.session.close(); |
} |
@Override |
public void detachRoute(String routeId) { |
- RouteController route = mRoutes.get(routeId); |
- if (route == null) return; |
+ mRoutes.remove(routeId); |
+ if (mSession != null) mSession.routeIds.remove(routeId); |
- route.markDetached(); |
+ for (int i = mClientRecords.size() - 1; i >= 0; --i) { |
+ ClientRecord client = mClientRecords.get(i); |
+ if (client.routeId.equals(routeId)) mClientRecords.remove(i); |
+ if (mSession != null) mSession.clientIds.remove(client.clientId); |
+ } |
} |
@Override |
public void sendStringMessage(String routeId, String message, int nativeCallbackId) { |
- RouteController route = mRoutes.get(routeId); |
- if (route == null) { |
+ if (mSession == null || !mSession.routeIds.contains(routeId)) { |
mManager.onMessageSentResult(false, nativeCallbackId); |
return; |
} |
- route.sendStringMessage(message, nativeCallbackId); |
+ mSession.session.sendStringMessage(message, nativeCallbackId); |
} |
@Override |
@@ -316,30 +348,64 @@ public class CastMediaRouteProvider |
mAndroidMediaRouter = androidMediaRouter; |
mManager = manager; |
} |
+ |
@Nullable |
- private CastRouteController autoJoinRoute(MediaSource source, String origin, int tabId) { |
- CastRouteController matchingRoute = null; |
- for (CastRouteController route : mRoutes.values()) { |
- MediaSource routeSource = MediaSource.from(route.getSourceId()); |
- if (routeSource.getApplicationId().equals(source.getApplicationId())) { |
- matchingRoute = route; |
- break; |
- } |
+ private boolean canAutoJoin(MediaSource source, String origin, int tabId) { |
+ MediaSource currentSource = MediaSource.from(mSession.session.getSourceId()); |
+ if (!currentSource.getApplicationId().equals(source.getApplicationId())) return false; |
+ |
+ ClientRecord client = null; |
+ if (!mSession.clientIds.isEmpty()) { |
+ String clientId = mSession.clientIds.iterator().next(); |
+ client = getClientRecordByClientId(clientId); |
+ } else if (mLastRemovedRouteRecord != null) { |
+ client = mLastRemovedRouteRecord; |
+ return origin.equals(client.origin) && tabId == client.tabId; |
} |
- if (matchingRoute == null) return null; |
+ if (client == null) return false; |
- String autoJoinPolicy = source.getAutoJoinPolicy(); |
+ if (source.getAutoJoinPolicy().equals(MediaSource.AUTOJOIN_PAGE_SCOPED)) { |
+ return false; |
+ } else if (source.getAutoJoinPolicy().equals(MediaSource.AUTOJOIN_ORIGIN_SCOPED)) { |
+ return origin.equals(client.origin); |
+ } else if (source.getAutoJoinPolicy().equals(MediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED)) { |
+ return origin.equals(client.origin) && tabId == client.tabId; |
+ } |
+ |
+ return false; |
+ } |
+ |
+ private boolean canJoinExistingSession(String presentationId, String origin, int tabId, |
+ MediaSource source) { |
+ if (AUTO_JOIN_PRESENTATION_ID.equals(presentationId)) { |
+ return canAutoJoin(source, origin, tabId); |
+ } else if (presentationId.startsWith(PRESENTATION_ID_SESSION_ID_PREFIX)) { |
+ String sessionId = presentationId.substring(PRESENTATION_ID_SESSION_ID_PREFIX.length()); |
- if (MediaSource.AUTOJOIN_ORIGIN_SCOPED.equals(autoJoinPolicy)) { |
- if (!matchingRoute.getOrigin().equals(origin)) return null; |
- } else if (MediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED.equals(autoJoinPolicy)) { |
- if (!matchingRoute.getOrigin().equals(origin) |
- || matchingRoute.getTabId() != tabId) { |
- return null; |
+ if (mSession.session.getSessionId().equals(sessionId)) return true; |
+ } else { |
+ for (String routeId : mSession.routeIds) { |
+ MediaRoute route = mRoutes.get(routeId); |
+ if (route != null && route.presentationId.equals(presentationId)) return true; |
} |
} |
+ return false; |
+ } |
- return matchingRoute; |
+ @Nullable |
+ private ClientRecord getClientRecordByClientId(String clientId) { |
+ for (ClientRecord record : mClientRecords) { |
+ if (record.clientId.equals(clientId)) return record; |
+ } |
+ return null; |
+ } |
+ |
+ @Nullable |
+ private ClientRecord getClientRecordByRouteId(String routeId) { |
+ for (ClientRecord record : mClientRecords) { |
+ if (record.routeId.equals(routeId)) return record; |
+ } |
+ return null; |
} |
} |