Skip to content

Commit

Permalink
[Cast, Android, Presentation API] Implement ReceiverAction messages.
Browse files Browse the repository at this point in the history
BUG=561470
TEST=manual with the YT experiment

1. Allow a route with a special presentation id to join a non-existing
session to pass the CAST message back through.
2. Send the CAST message back via the special route before the route is being created with the
selected device info.
3. Send the STOP message via the normal route when the session is about to
close via the CloseRoute() or the notification controls.

Review URL: https://codereview.chromium.org/1534783002

Cr-Commit-Position: refs/heads/master@{#365900}
  • Loading branch information
avayvod authored and Commit bot committed Dec 17, 2015
1 parent 8adff4e commit e96721f
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public interface RouteDelegate {
*/
void onRouteRequestError(String message, int requestId);

/**
* Called when the user wants to stop the session via the UI (e.g. notification).
*/
void onSessionStopAction();

/**
* Called when the session is closed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo;

import org.chromium.base.Log;
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;
import org.chromium.chrome.browser.media.router.RouteDelegate;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
Expand All @@ -32,8 +35,11 @@
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_";
private static final String RECEIVER_ACTION_PRESENTATION_ID = "_receiver-action";

private final Context mApplicationContext;
private final MediaRouter mAndroidMediaRouter;
Expand Down Expand Up @@ -78,39 +84,26 @@ public void onSinksReceived(String sourceId, List<MediaSink> sinks) {

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

if (clientId != null && !mSession.clientIds.contains(clientId)) {
mSession.clientIds.add(clientId);
}

mRoutes.put(routeId, route);

mManager.onRouteCreated(routeId, route.sinkId, requestId, this, true);
addRoute(route, routeController.getOrigin(), routeController.getTabId());
mManager.onRouteCreated(route.id, route.sinkId, requestId, this, true);
}

@Override
public void onRouteRequestError(String message, int requestId) {
mManager.onRouteRequestError(message, requestId);
}

@Override
public void onSessionStopAction() {
if (mSession == null) return;

for (String routeId : mRoutes.keySet()) closeRoute(routeId);
}

@Override
public void onSessionClosed() {
if (mSession == null) return;
Expand Down Expand Up @@ -252,11 +245,19 @@ public void createRoute(String sourceId, String sinkId, String presentationId, S
return;
}

if (source.getClientId() != null) {
String receiverActionClientId = source.getClientId() + RECEIVER_ACTION_PRESENTATION_ID;
ClientRecord clientRecord = getClientRecordByClientId(receiverActionClientId);
if (clientRecord != null) {
sendReceiverAction(clientRecord.routeId, sink, receiverActionClientId, "cast");
detachRoute(clientRecord.routeId);
mManager.onRouteClosed(clientRecord.routeId);
}
}

CreateRouteRequest createRouteRequest = new CreateRouteRequest(
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;
Expand All @@ -271,27 +272,33 @@ public void createRoute(String sourceId, String sinkId, String presentationId, S
@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;
}

// TODO(avayvod): Implement _receiver-action route for ReceiverAction messages,
// https://crbug.com/561470.
// For the ReceiverAction presentation id there's no need to have a session or a route.
if (RECEIVER_ACTION_PRESENTATION_ID.equals(presentationId)) {
MediaRoute route = new MediaRoute("", sourceId, presentationId);
addRoute(route, origin, tabId);
mManager.onRouteCreated(route.id, route.sinkId, nativeRequestId, this, true);
return;
}

if (mSession == null) {
mManager.onRouteRequestError("No presentation", nativeRequestId);
return;
}

if (!canJoinExistingSession(presentationId, origin, tabId, source)) {
mManager.onRouteRequestError("No matching route", nativeRequestId);
return;
}

MediaRoute route = new MediaRoute(mSession.session.getSinkId(), sourceId, presentationId);
this.onRouteCreated(nativeRequestId, route, mSession.session);
addRoute(route, origin, tabId);
mManager.onRouteCreated(route.id, route.sinkId, nativeRequestId, this, false);
}

@Override
Expand All @@ -305,7 +312,11 @@ public void closeRoute(String routeId) {
return;
}

// TODO(avayvod): Implement ReceiverAction.STOP, https://crbug.com/561470.
ClientRecord client = getClientRecordByRouteId(routeId);
if (client != null) {
MediaSink sink = MediaSink.fromSinkId(mSession.sinkId, mAndroidMediaRouter);
if (sink != null) sendReceiverAction(routeId, sink, client.clientId, "stop");
}

if (mSession.isStopping) return;

Expand All @@ -331,6 +342,13 @@ public void detachRoute(String routeId) {

@Override
public void sendStringMessage(String routeId, String message, int nativeCallbackId) {
ClientRecord clientRecord = getClientRecordByRouteId(routeId);
if (clientRecord != null
&& clientRecord.clientId.endsWith(RECEIVER_ACTION_PRESENTATION_ID)) {
mManager.onMessageSentResult(true, nativeCallbackId);
return;
}

if (mSession == null || !mSession.routeIds.contains(routeId)) {
mManager.onMessageSentResult(false, nativeCallbackId);
return;
Expand Down Expand Up @@ -399,11 +417,6 @@ private boolean canJoinExistingSession(String presentationId, String origin, int
return false;
}

private void onRouteClosed(String routeId) {
mManager.onRouteClosed(routeId);
detachRoute(routeId);
}

@Nullable
private ClientRecord getClientRecordByClientId(String clientId) {
for (ClientRecord record : mClientRecords) {
Expand All @@ -419,4 +432,53 @@ private ClientRecord getClientRecordByRouteId(String routeId) {
}
return null;
}

private void addRoute(MediaRoute route, String origin, int tabId) {
mRoutes.put(route.id, route);
if (mSession != null) mSession.routeIds.add(route.id);

MediaSource source = MediaSource.from(route.sourceId);
final String clientId = source.getClientId();

if (clientId == null || getClientRecordByClientId(clientId) != null) return;

mClientRecords.add(new ClientRecord(
route.id,
clientId,
source.getApplicationId(),
source.getAutoJoinPolicy(),
origin,
tabId));
if (mSession != null) mSession.clientIds.add(clientId);
}

private void sendReceiverAction(
String routeId, MediaSink sink, String clientId, String action) {
try {
JSONObject jsonReceiver = new JSONObject();
jsonReceiver.put("label", sink.getId());
jsonReceiver.put("friendlyName", sink.getName());
jsonReceiver.put("capabilities", CastRouteController.getCapabilities(sink.getDevice()));
jsonReceiver.put("volume", null);
jsonReceiver.put("isActiveInput", null);
jsonReceiver.put("displayStatus", null);
jsonReceiver.put("receiverType", "cast");

JSONObject jsonReceiverAction = new JSONObject();
jsonReceiverAction.put("receiver", jsonReceiver);
jsonReceiverAction.put("action", action);

JSONObject json = new JSONObject();
json.put("type", "receiver_action");
json.put("sequenceNumber", -1);
json.put("timeoutMillis", 0);
json.put("clientId", clientId);
json.put("message", jsonReceiverAction);

Log.d(TAG, "Sending receiver action to %s: %s", routeId, json.toString());
mManager.onMessage(routeId, json.toString());
} catch (JSONException e) {
Log.e(TAG, "Failed to send receiver action message", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ public void onPause(int actionSource) {

@Override
public void onStop(int actionSource) {
stopApplication();
mRouteDelegate.onSessionStopAction();
}


Expand Down Expand Up @@ -869,7 +869,7 @@ private void sendClientMessageTo(
buildInternalMessage(type, message, clientId, sequenceNumber));
}

private JSONArray getCapabilities(CastDevice device) {
static JSONArray getCapabilities(CastDevice device) {
JSONArray jsonCapabilities = new JSONArray();
if (device.hasCapability(CastDevice.CAPABILITY_AUDIO_IN)) {
jsonCapabilities.put("audio_in");
Expand Down

0 comments on commit e96721f

Please sign in to comment.