Commit 949135e0 authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Extract MediaSource interface

Currently, MediaSource and RemotingMediaSource have no common ancestor.
This makes code reuse between CastMediaRouteProvider and
RemotingMediaRouteProvider difficult.

This CL renames MediaSource into CastMediaSource, adds a MediaSource
interface, and updates RemotingMediaSource and CastMediaSources to both
derive from this interface. This paves the way for the introduction of
a BaseMediaRouteProvider.

Bug: 790766
Change-Id: I3f8e46816a1e9f8f2f617a43b1099c8614204efc
Reviewed-on: https://chromium-review.googlesource.com/783836
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarAnton Vayvod <avayvod@chromium.org>
Reviewed-by: default avatarZhiqiang Zhang <zqzhang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521578}
parent fb4046a4
......@@ -10,6 +10,7 @@ import android.support.v7.media.MediaRouteSelector;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.media.router.cast.CastMediaSource;
import org.chromium.chrome.browser.media.router.cast.MediaSink;
import org.chromium.chrome.browser.media.router.cast.MediaSource;
import org.chromium.chrome.browser.media.router.cast.remoting.RemotingMediaSource;
......@@ -45,30 +46,23 @@ public class ChromeMediaRouterDialogController implements MediaRouteDialogDelega
public void openRouteChooserDialog(String[] sourceUrns) {
if (isShowingDialog()) return;
String sourceId = null;
MediaRouteSelector routeSelector = null;
MediaSource source = null;
for (String sourceUrn : sourceUrns) {
MediaSource source = MediaSource.from(sourceUrn);
if (source != null) {
sourceId = source.getUrn();
routeSelector = source.buildRouteSelector();
break;
}
RemotingMediaSource remotingSource = RemotingMediaSource.from(sourceUrn);
if (remotingSource != null) {
sourceId = remotingSource.getSourceId();
routeSelector = remotingSource.buildRouteSelector();
break;
}
source = CastMediaSource.from(sourceUrn);
if (source == null) source = RemotingMediaSource.from(sourceUrn);
if (source != null) break;
}
if (sourceId == null || routeSelector == null) {
MediaRouteSelector routeSelector = source == null ? null : source.buildRouteSelector();
if (routeSelector == null) {
nativeOnMediaSourceNotSupported(mNativeDialogController);
return;
}
mDialogManager = new MediaRouteChooserDialogManager(sourceId, routeSelector, this);
mDialogManager =
new MediaRouteChooserDialogManager(source.getSourceId(), routeSelector, this);
mDialogManager.openDialog();
}
......@@ -81,28 +75,18 @@ public class ChromeMediaRouterDialogController implements MediaRouteDialogDelega
public void openRouteControllerDialog(String sourceUrn, String mediaRouteId) {
if (isShowingDialog()) return;
String sourceId = null;
MediaRouteSelector routeSelector = null;
MediaSource source = MediaSource.from(sourceUrn);
if (source != null) {
sourceId = source.getUrn();
routeSelector = source.buildRouteSelector();
} else {
RemotingMediaSource remotingSource = RemotingMediaSource.from(sourceUrn);
if (remotingSource != null) {
sourceId = remotingSource.getSourceId();
routeSelector = remotingSource.buildRouteSelector();
}
}
MediaSource source = CastMediaSource.from(sourceUrn);
if (source == null) source = RemotingMediaSource.from(sourceUrn);
MediaRouteSelector routeSelector = source == null ? null : source.buildRouteSelector();
if (sourceId == null || routeSelector == null) {
if (routeSelector == null) {
nativeOnMediaSourceNotSupported(mNativeDialogController);
return;
}
mDialogManager =
new MediaRouteControllerDialogManager(sourceId, routeSelector, mediaRouteId, this);
mDialogManager = new MediaRouteControllerDialogManager(
source.getSourceId(), routeSelector, mediaRouteId, this);
mDialogManager.openDialog();
}
......
......@@ -166,7 +166,7 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
@Override
public boolean supportsSource(String sourceId) {
return MediaSource.from(sourceId) != null;
return CastMediaSource.from(sourceId) != null;
}
@Override
......@@ -178,7 +178,7 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
return;
}
MediaSource source = MediaSource.from(sourceId);
MediaSource source = CastMediaSource.from(sourceId);
if (source == null) {
// If the source is invalid, report no devices available.
onSinksReceived(sourceId, new ArrayList<MediaSink>());
......@@ -218,7 +218,7 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
public void stopObservingMediaSinks(String sourceId) {
if (mAndroidMediaRouter == null) return;
MediaSource source = MediaSource.from(sourceId);
MediaSource source = CastMediaSource.from(sourceId);
if (source == null) return;
String applicationId = source.getApplicationId();
......@@ -247,7 +247,7 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
return;
}
MediaSource source = MediaSource.from(sourceId);
MediaSource source = CastMediaSource.from(sourceId);
if (source == null) {
mManager.onRouteRequestError("Unsupported presentation URL", nativeRequestId);
return;
......@@ -270,15 +270,17 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
MediaSink sink = request.getSink();
MediaSource source = request.getSource();
MediaRoute route = new MediaRoute(
sink.getId(), source.getUrn(), request.getPresentationId());
MediaRoute route =
new MediaRoute(sink.getId(), source.getSourceId(), request.getPresentationId());
addRoute(route, request.getOrigin(), request.getTabId());
mManager.onRouteCreated(route.id, route.sinkId, request.getNativeRequestId(), this, true);
if (source.getClientId() != null) {
ClientRecord clientRecord = mClientRecords.get(source.getClientId());
String clientId = ((CastMediaSource) source).getClientId();
if (clientId != null) {
ClientRecord clientRecord = mClientRecords.get(clientId);
if (clientRecord != null) {
sendReceiverAction(clientRecord.routeId, sink, source.getClientId(), "cast");
sendReceiverAction(clientRecord.routeId, sink, clientId, "cast");
}
}
request.start();
......@@ -287,7 +289,7 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
@Override
public void joinRoute(String sourceId, String presentationId, String origin, int tabId,
int nativeRequestId) {
MediaSource source = MediaSource.from(sourceId);
CastMediaSource source = CastMediaSource.from(sourceId);
if (source == null || source.getClientId() == null) {
mManager.onRouteRequestError("Unsupported presentation URL", nativeRequestId);
return;
......@@ -420,10 +422,10 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
// Send a "disconnect_session" message to all the clients that match with the leaving
// client's auto join policy.
for (ClientRecord client : mClientRecords.values()) {
if ((MediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED.equals(leavingClient.autoJoinPolicy)
if ((CastMediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED.equals(leavingClient.autoJoinPolicy)
&& isSameOrigin(client.origin, leavingClient.origin)
&& client.tabId == leavingClient.tabId)
|| (MediaSource.AUTOJOIN_ORIGIN_SCOPED.equals(leavingClient.autoJoinPolicy)
|| (CastMediaSource.AUTOJOIN_ORIGIN_SCOPED.equals(leavingClient.autoJoinPolicy)
&& isSameOrigin(client.origin, leavingClient.origin))) {
onMessage(client.clientId,
buildInternalMessage("disconnect_session", -1, client.clientId, sessionId));
......@@ -451,10 +453,10 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
mMessageHandler = new CastMessageHandler(this);
}
private boolean canAutoJoin(MediaSource source, String origin, int tabId) {
if (source.getAutoJoinPolicy().equals(MediaSource.AUTOJOIN_PAGE_SCOPED)) return false;
private boolean canAutoJoin(CastMediaSource source, String origin, int tabId) {
if (source.getAutoJoinPolicy().equals(CastMediaSource.AUTOJOIN_PAGE_SCOPED)) return false;
MediaSource currentSource = MediaSource.from(mSession.getSourceId());
CastMediaSource currentSource = CastMediaSource.from(mSession.getSourceId());
if (!currentSource.getApplicationId().equals(source.getApplicationId())) return false;
ClientRecord client = null;
......@@ -467,17 +469,18 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
if (client == null) return false;
boolean sameOrigin = isSameOrigin(origin, client.origin);
if (source.getAutoJoinPolicy().equals(MediaSource.AUTOJOIN_ORIGIN_SCOPED)) {
if (source.getAutoJoinPolicy().equals(CastMediaSource.AUTOJOIN_ORIGIN_SCOPED)) {
return sameOrigin;
} else if (source.getAutoJoinPolicy().equals(MediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED)) {
} else if (source.getAutoJoinPolicy().equals(
CastMediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED)) {
return sameOrigin && tabId == client.tabId;
}
return false;
}
private boolean canJoinExistingSession(String presentationId, String origin, int tabId,
MediaSource source) {
private boolean canJoinExistingSession(
String presentationId, String origin, int tabId, CastMediaSource source) {
if (AUTO_JOIN_PRESENTATION_ID.equals(presentationId)) {
return canAutoJoin(source, origin, tabId);
} else if (presentationId.startsWith(PRESENTATION_ID_SESSION_ID_PREFIX)) {
......@@ -503,7 +506,7 @@ public class CastMediaRouteProvider implements MediaRouteProvider, DiscoveryDele
void addRoute(MediaRoute route, String origin, int tabId) {
mRoutes.put(route.id, route);
MediaSource source = MediaSource.from(route.sourceId);
CastMediaSource source = CastMediaSource.from(route.sourceId);
final String clientId = source.getClientId();
if (clientId == null || mClientRecords.get(clientId) != null) return;
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.media.router.cast;
import android.net.Uri;
import android.support.v7.media.MediaRouteSelector;
import com.google.android.gms.cast.CastMediaControlIntent;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
/**
* Abstracts parsing the Cast application id and other parameters from the source ID.
*/
public class CastMediaSource implements MediaSource {
public static final String AUTOJOIN_CUSTOM_CONTROLLER_SCOPED = "custom_controller_scoped";
public static final String AUTOJOIN_TAB_AND_ORIGIN_SCOPED = "tab_and_origin_scoped";
public static final String AUTOJOIN_ORIGIN_SCOPED = "origin_scoped";
public static final String AUTOJOIN_PAGE_SCOPED = "page_scoped";
private static final List<String> AUTOJOIN_POLICIES =
Arrays.asList(AUTOJOIN_CUSTOM_CONTROLLER_SCOPED, AUTOJOIN_TAB_AND_ORIGIN_SCOPED,
AUTOJOIN_ORIGIN_SCOPED, AUTOJOIN_PAGE_SCOPED);
private static final String CAST_SOURCE_ID_SEPARATOR = "/";
private static final String CAST_SOURCE_ID_APPLICATION_ID = "__castAppId__";
private static final String CAST_SOURCE_ID_CLIENT_ID = "__castClientId__";
private static final String CAST_SOURCE_ID_AUTOJOIN_POLICY = "__castAutoJoinPolicy__";
private static final String CAST_APP_CAPABILITIES_PREFIX = "(";
private static final String CAST_APP_CAPABILITIES_SUFFIX = ")";
private static final String CAST_APP_CAPABILITIES_SEPARATOR = ",";
private static final List<String> CAST_APP_CAPABILITIES =
Arrays.asList("video_out", "audio_out", "video_in", "audio_in", "multizone_group");
/**
* The protocol for Cast Presentation URLs.
*/
private static final String CAST_URL_PROTOCOL = "cast:";
/**
* The query parameter key for Cast Client ID in a Cast Presentation URL.
*/
private static final String CAST_URL_CLIENT_ID = "clientId";
/**
* The query parameter key for autojoin policy in a Cast Presentation URL.
*/
private static final String CAST_URL_AUTOJOIN_POLICY = "autoJoinPolicy";
/**
* The query parameter key for app capabilities in a Cast Presentation URL.
*/
private static final String CAST_URL_CAPABILITIES = "capabilities";
/**
* The original presentation URL that the {@link CastMediaSource} object was created from.
*/
private final String mSourceId;
/**
* The Cast application id, can be invalid in which case {@link CastMediaRouteProvider}
* will explicitly report no sinks available.
*/
private final String mApplicationId;
/**
* A numeric identifier for the Cast Web SDK, unique for the frame providing the
* presentation URL. Can be null.
*/
private final String mClientId;
/**
* Defines Cast-specific behavior for {@link CastMediaRouteProvider#joinRoute}. Defaults to
* {@link CastMediaSource#AUTOJOIN_TAB_AND_ORIGIN_SCOPED}.
*/
private final String mAutoJoinPolicy;
/**
* Defines the capabilities of the particular application id. Can be null.
*/
private final String[] mCapabilities;
/**
* Initializes the media source from the source id.
* @param sourceId the source id for the Cast media source (a presentation url).
* @return an initialized media source if the id is valid, null otherwise.
*/
@Nullable
public static CastMediaSource from(String sourceId) {
assert sourceId != null;
return sourceId.startsWith(CAST_URL_PROTOCOL) ? fromCastUrl(sourceId)
: fromLegacyUrl(sourceId);
}
/**
* Returns a new {@link MediaRouteSelector} to use for Cast device filtering for this
* particular media source or null if the application id is invalid.
*
* @return an initialized route selector or null.
*/
@Override
public MediaRouteSelector buildRouteSelector() {
try {
return new MediaRouteSelector.Builder()
.addControlCategory(CastMediaControlIntent.categoryForCast(mApplicationId))
.build();
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* @return the Cast application id corresponding to the source.
*/
@Override
public String getApplicationId() {
return mApplicationId;
}
/**
* @return the client id if passed in the source id. Can be null.
*/
@Nullable
public String getClientId() {
return mClientId;
}
/**
* @return the auto join policy which must be one of the AUTOJOIN constants defined above.
*/
public String getAutoJoinPolicy() {
return mAutoJoinPolicy;
}
/**
* @return the id identifying the media source
*/
@Override
public String getSourceId() {
return mSourceId;
}
/**
* @return application capabilities
*/
public String[] getCapabilities() {
return mCapabilities == null ? null : Arrays.copyOf(mCapabilities, mCapabilities.length);
}
private CastMediaSource(String sourceId, String applicationId, String clientId,
String autoJoinPolicy, String[] capabilities) {
mSourceId = sourceId;
mApplicationId = applicationId;
mClientId = clientId;
mAutoJoinPolicy = autoJoinPolicy == null ? AUTOJOIN_TAB_AND_ORIGIN_SCOPED : autoJoinPolicy;
mCapabilities = capabilities;
}
@Nullable
private static String extractParameter(String[] fragments, String key) {
String keyPrefix = key + "=";
for (String parameter : fragments) {
if (parameter.startsWith(keyPrefix)) return parameter.substring(keyPrefix.length());
}
return null;
}
@Nullable
private static String[] extractCapabilities(String capabilitiesParameter) {
if (capabilitiesParameter.length()
< CAST_APP_CAPABILITIES_PREFIX.length() + CAST_APP_CAPABILITIES_SUFFIX.length()) {
return null;
}
if (!capabilitiesParameter.startsWith(CAST_APP_CAPABILITIES_PREFIX)
|| !capabilitiesParameter.endsWith(CAST_APP_CAPABILITIES_SUFFIX)) {
return null;
}
String capabilitiesList =
capabilitiesParameter.substring(CAST_APP_CAPABILITIES_PREFIX.length(),
capabilitiesParameter.length() - CAST_APP_CAPABILITIES_SUFFIX.length());
String[] capabilities = capabilitiesList.split(CAST_APP_CAPABILITIES_SEPARATOR);
for (String capability : capabilities) {
if (!CAST_APP_CAPABILITIES.contains(capability)) return null;
}
return capabilities;
}
/**
* Helper method to create a MediaSource object from a Cast (cast:) presentation URL.
* @param sourceId the source id for the Cast media source.
* @return an initialized media source if the uri is a valid Cast presentation URL, null
* otherwise.
*/
@Nullable
private static CastMediaSource fromCastUrl(String sourceId) {
// Strip the scheme as the Uri parser works better without it.
Uri sourceUri = Uri.parse(sourceId.substring(CAST_URL_PROTOCOL.length()));
String applicationId = sourceUri.getPath();
if (applicationId == null) return null;
String clientId = sourceUri.getQueryParameter(CAST_URL_CLIENT_ID);
String autoJoinPolicy = sourceUri.getQueryParameter(CAST_URL_AUTOJOIN_POLICY);
if (autoJoinPolicy != null && !AUTOJOIN_POLICIES.contains(autoJoinPolicy)) {
return null;
}
String[] capabilities = null;
String capabilitiesParam = sourceUri.getQueryParameter(CAST_URL_CAPABILITIES);
if (capabilitiesParam != null) {
capabilities = capabilitiesParam.split(CAST_APP_CAPABILITIES_SEPARATOR);
for (String capability : capabilities) {
if (!CAST_APP_CAPABILITIES.contains(capability)) return null;
}
}
return new CastMediaSource(sourceId, applicationId, clientId, autoJoinPolicy, capabilities);
}
/**
* @deprecated Legacy Cast Presentation URLs are deprecated in favor of cast: URLs.
* TODO(crbug.com/757358): remove this method when we drop support for legacy URLs.
* Helper method to create a MediaSource object from a legacy (https:) presentation URL.
* @param sourceId the source id for the Cast media source.
* @return an initialized media source if the uri is a valid https presentation URL, null
* otherwise.
*/
@Deprecated
@Nullable
private static CastMediaSource fromLegacyUrl(String sourceId) {
Uri sourceUri = Uri.parse(sourceId);
String uriFragment = sourceUri.getFragment();
if (uriFragment == null) return null;
String[] parameters = uriFragment.split(CAST_SOURCE_ID_SEPARATOR);
String applicationId = extractParameter(parameters, CAST_SOURCE_ID_APPLICATION_ID);
if (applicationId == null) return null;
String[] capabilities = null;
int capabilitiesIndex = applicationId.indexOf(CAST_APP_CAPABILITIES_PREFIX);
if (capabilitiesIndex != -1) {
capabilities = extractCapabilities(applicationId.substring(capabilitiesIndex));
if (capabilities == null) return null;
applicationId = applicationId.substring(0, capabilitiesIndex);
}
String clientId = extractParameter(parameters, CAST_SOURCE_ID_CLIENT_ID);
String autoJoinPolicy = extractParameter(parameters, CAST_SOURCE_ID_AUTOJOIN_POLICY);
if (autoJoinPolicy != null && !AUTOJOIN_POLICIES.contains(autoJoinPolicy)) {
return null;
}
return new CastMediaSource(sourceId, applicationId, clientId, autoJoinPolicy, capabilities);
}
}
......@@ -268,7 +268,7 @@ public class CastSessionImpl implements MediaNotificationListener, CastSession {
@Override
public String getSourceId() {
return mSource.getUrn();
return mSource.getSourceId();
}
@Override
......
......@@ -4,259 +4,27 @@
package org.chromium.chrome.browser.media.router.cast;
import android.net.Uri;
import android.support.v7.media.MediaRouteSelector;
import com.google.android.gms.cast.CastMediaControlIntent;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
/**
* Abstracts parsing the Cast application id and other parameters from the source URN.
*/
public class MediaSource {
public static final String AUTOJOIN_CUSTOM_CONTROLLER_SCOPED = "custom_controller_scoped";
public static final String AUTOJOIN_TAB_AND_ORIGIN_SCOPED = "tab_and_origin_scoped";
public static final String AUTOJOIN_ORIGIN_SCOPED = "origin_scoped";
public static final String AUTOJOIN_PAGE_SCOPED = "page_scoped";
private static final List<String> AUTOJOIN_POLICIES =
Arrays.asList(AUTOJOIN_CUSTOM_CONTROLLER_SCOPED, AUTOJOIN_TAB_AND_ORIGIN_SCOPED,
AUTOJOIN_ORIGIN_SCOPED, AUTOJOIN_PAGE_SCOPED);
private static final String CAST_SOURCE_ID_SEPARATOR = "/";
private static final String CAST_SOURCE_ID_APPLICATION_ID = "__castAppId__";
private static final String CAST_SOURCE_ID_CLIENT_ID = "__castClientId__";
private static final String CAST_SOURCE_ID_AUTOJOIN_POLICY = "__castAutoJoinPolicy__";
private static final String CAST_APP_CAPABILITIES_PREFIX = "(";
private static final String CAST_APP_CAPABILITIES_SUFFIX = ")";
private static final String CAST_APP_CAPABILITIES_SEPARATOR = ",";
private static final List<String> CAST_APP_CAPABILITIES =
Arrays.asList("video_out", "audio_out", "video_in", "audio_in", "multizone_group");
/**
* The protocol for Cast Presentation URLs.
*/
private static final String CAST_URL_PROTOCOL = "cast:";
/**
* The query parameter key for Cast Client ID in a Cast Presentation URL.
*/
private static final String CAST_URL_CLIENT_ID = "clientId";
/**
* The query parameter key for autojoin policy in a Cast Presentation URL.
*/
private static final String CAST_URL_AUTOJOIN_POLICY = "autoJoinPolicy";
/**
* The query parameter key for app capabilities in a Cast Presentation URL.
*/
private static final String CAST_URL_CAPABILITIES = "capabilities";
/**
* The original presentation URL that the {@link MediaSource} object was created from.
*/
private final String mSourceId;
/**
* The Cast application id, can be invalid in which case {@link CastMediaRouteProvider}
* will explicitly report no sinks available.
*/
private final String mApplicationId;
/**
* A numeric identifier for the Cast Web SDK, unique for the frame providing the
* presentation URL. Can be null.
*/
private final String mClientId;
/**
* Defines Cast-specific behavior for {@link CastMediaRouteProvider#joinRoute}. Defaults to
* {@link MediaSource#AUTOJOIN_TAB_AND_ORIGIN_SCOPED}.
*/
private final String mAutoJoinPolicy;
/**
* Defines the capabilities of the particular application id. Can be null.
*/
private final String[] mCapabilities;
/**
* Initializes the media source from the source id.
* @param sourceId the source id for the Cast media source (a presentation url).
* @return an initialized media source if the id is valid, null otherwise.
*/
@Nullable
public static MediaSource from(String sourceId) {
assert sourceId != null;
return sourceId.startsWith(CAST_URL_PROTOCOL) ? fromCastUrl(sourceId)
: fromLegacyUrl(sourceId);
}
public interface MediaSource {
/**
* Returns a new {@link MediaRouteSelector} to use for Cast device filtering for this
* particular media source or null if the application id is invalid.
*
* @return an initialized route selector or null.
*/
public MediaRouteSelector buildRouteSelector() {
try {
return new MediaRouteSelector.Builder()
.addControlCategory(CastMediaControlIntent.categoryForCast(mApplicationId))
.build();
} catch (IllegalArgumentException e) {
return null;
}
}
public MediaRouteSelector buildRouteSelector();
/**
* @return the Cast application id corresponding to the source.
*/
public String getApplicationId() {
return mApplicationId;
}
/**
* @return the client id if passed in the source id. Can be null.
*/
@Nullable
public String getClientId() {
return mClientId;
}
/**
* @return the auto join policy which must be one of the AUTOJOIN constants defined above.
*/
public String getAutoJoinPolicy() {
return mAutoJoinPolicy;
}
public String getApplicationId();
/**
* @return the id identifying the media source
*/
public String getUrn() {
return mSourceId;
}
/**
* @return application capabilities
*/
public String[] getCapabilities() {
return mCapabilities == null ? null : Arrays.copyOf(mCapabilities, mCapabilities.length);
}
private MediaSource(
String sourceId,
String applicationId,
String clientId,
String autoJoinPolicy,
String[] capabilities) {
mSourceId = sourceId;
mApplicationId = applicationId;
mClientId = clientId;
mAutoJoinPolicy = autoJoinPolicy == null ? AUTOJOIN_TAB_AND_ORIGIN_SCOPED : autoJoinPolicy;
mCapabilities = capabilities;
}
@Nullable
private static String extractParameter(String[] fragments, String key) {
String keyPrefix = key + "=";
for (String parameter : fragments) {
if (parameter.startsWith(keyPrefix)) return parameter.substring(keyPrefix.length());
}
return null;
}
@Nullable
private static String[] extractCapabilities(String capabilitiesParameter) {
if (capabilitiesParameter.length()
< CAST_APP_CAPABILITIES_PREFIX.length() + CAST_APP_CAPABILITIES_SUFFIX.length()) {
return null;
}
if (!capabilitiesParameter.startsWith(CAST_APP_CAPABILITIES_PREFIX)
|| !capabilitiesParameter.endsWith(CAST_APP_CAPABILITIES_SUFFIX)) {
return null;
}
String capabilitiesList = capabilitiesParameter.substring(
CAST_APP_CAPABILITIES_PREFIX.length(),
capabilitiesParameter.length() - CAST_APP_CAPABILITIES_SUFFIX.length());
String[] capabilities = capabilitiesList.split(CAST_APP_CAPABILITIES_SEPARATOR);
for (String capability : capabilities) {
if (!CAST_APP_CAPABILITIES.contains(capability)) return null;
}
return capabilities;
}
/**
* Helper method to create a MediaSource object from a Cast (cast:) presentation URL.
* @param sourceId the source id for the Cast media source.
* @return an initialized media source if the uri is a valid Cast presentation URL, null
* otherwise.
*/
@Nullable
private static MediaSource fromCastUrl(String sourceId) {
// Strip the scheme as the Uri parser works better without it.
Uri sourceUri = Uri.parse(sourceId.substring(CAST_URL_PROTOCOL.length()));
String applicationId = sourceUri.getPath();
if (applicationId == null) return null;
String clientId = sourceUri.getQueryParameter(CAST_URL_CLIENT_ID);
String autoJoinPolicy = sourceUri.getQueryParameter(CAST_URL_AUTOJOIN_POLICY);
if (autoJoinPolicy != null && !AUTOJOIN_POLICIES.contains(autoJoinPolicy)) {
return null;
}
String[] capabilities = null;
String capabilitiesParam = sourceUri.getQueryParameter(CAST_URL_CAPABILITIES);
if (capabilitiesParam != null) {
capabilities = capabilitiesParam.split(CAST_APP_CAPABILITIES_SEPARATOR);
for (String capability : capabilities) {
if (!CAST_APP_CAPABILITIES.contains(capability)) return null;
}
}
return new MediaSource(sourceId, applicationId, clientId, autoJoinPolicy, capabilities);
}
/**
* @deprecated Legacy Cast Presentation URLs are deprecated in favor of cast: URLs.
* TODO(crbug.com/757358): remove this method when we drop support for legacy URLs.
* Helper method to create a MediaSource object from a legacy (https:) presentation URL.
* @param sourceId the source id for the Cast media source.
* @return an initialized media source if the uri is a valid https presentation URL, null
* otherwise.
*/
@Deprecated
@Nullable
private static MediaSource fromLegacyUrl(String sourceId) {
Uri sourceUri = Uri.parse(sourceId);
String uriFragment = sourceUri.getFragment();
if (uriFragment == null) return null;
String[] parameters = uriFragment.split(CAST_SOURCE_ID_SEPARATOR);
String applicationId = extractParameter(parameters, CAST_SOURCE_ID_APPLICATION_ID);
if (applicationId == null) return null;
String[] capabilities = null;
int capabilitiesIndex = applicationId.indexOf(CAST_APP_CAPABILITIES_PREFIX);
if (capabilitiesIndex != -1) {
capabilities = extractCapabilities(applicationId.substring(capabilitiesIndex));
if (capabilities == null) return null;
applicationId = applicationId.substring(0, capabilitiesIndex);
}
String clientId = extractParameter(parameters, CAST_SOURCE_ID_CLIENT_ID);
String autoJoinPolicy = extractParameter(parameters, CAST_SOURCE_ID_AUTOJOIN_POLICY);
if (autoJoinPolicy != null && !AUTOJOIN_POLICIES.contains(autoJoinPolicy)) {
return null;
}
return new MediaSource(sourceId, applicationId, clientId, autoJoinPolicy, capabilities);
}
public String getSourceId();
}
......@@ -9,6 +9,7 @@ import android.util.Base64;
import com.google.android.gms.cast.CastMediaControlIntent;
import org.chromium.base.Log;
import org.chromium.chrome.browser.media.router.cast.MediaSource;
import java.io.UnsupportedEncodingException;
......@@ -17,7 +18,7 @@ import javax.annotation.Nullable;
/**
* Abstracts parsing the Cast application id and other parameters from the source id.
*/
public class RemotingMediaSource {
public class RemotingMediaSource implements MediaSource {
private static final String TAG = "MediaRemoting";
// Need to be in sync with third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp.
......@@ -71,6 +72,7 @@ public class RemotingMediaSource {
*
* @return an initialized route selector or null.
*/
@Override
public MediaRouteSelector buildRouteSelector() {
return new MediaRouteSelector.Builder()
.addControlCategory(CastMediaControlIntent.categoryForCast(mApplicationId))
......@@ -80,6 +82,7 @@ public class RemotingMediaSource {
/**
* @return the Cast application id corresponding to the source. Can be overridden downstream.
*/
@Override
public String getApplicationId() {
return mApplicationId;
}
......@@ -87,6 +90,7 @@ public class RemotingMediaSource {
/**
* @return the id identifying the media source
*/
@Override
public String getSourceId() {
return mSourceId;
}
......@@ -103,4 +107,4 @@ public class RemotingMediaSource {
mApplicationId = applicationId;
mMediaUrl = mediaUrl;
}
}
\ No newline at end of file
}
......@@ -594,6 +594,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/media/router/MediaRouteManager.java",
"java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java",
"java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java",
"java/src/org/chromium/chrome/browser/media/router/cast/CastMediaSource.java",
"java/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandler.java",
"java/src/org/chromium/chrome/browser/media/router/cast/CastRequestIdGenerator.java",
"java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java",
......@@ -1893,10 +1894,10 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterSinkObservationTest.java",
"junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterTestBase.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaSourceTest.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandlerTest.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/DiscoveryCallbackTest.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/JSONTestUtils.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/MediaSourceTest.java",
"junit/src/org/chromium/chrome/browser/media/router/cast/TestUtils.java",
"junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java",
"junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationActionsUpdatedTest.java",
......
......@@ -34,7 +34,7 @@ public class CastMediaRouteProviderTest {
private static final String SUPPORTED_SOURCE = "cast:DEADBEEF";
private static final String SUPPORTED_AUTOJOIN_SOURCE = "cast:DEADBEEF"
+ "?clientId=12345&autoJoinPolicy=" + MediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED;
+ "?clientId=12345&autoJoinPolicy=" + CastMediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED;
// TODO(crbug.com/672704): Android does not currently support 1-UA mode.
private static final String UNSUPPORTED_SOURCE = "https://example.com";
......
......@@ -16,22 +16,20 @@ import org.chromium.base.test.util.Feature;
import org.chromium.testing.local.LocalRobolectricTestRunner;
/**
* Robolectric tests for {@link MediaSource} class.
* Robolectric tests for {@link CastMediaSource} class.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MediaSourceTest {
public class CastMediaSourceTest {
@Test
@Feature({"MediaRouter"})
public void testCorrectSourceId() {
final String sourceId =
"https://example.com/path?query"
final String sourceId = "https://example.com/path?query"
+ "#__castAppId__=ABCD1234(video_out,audio_out)"
+ "/__castClientId__=1234567890"
+ "/__castAutoJoinPolicy__=tab_and_origin_scoped";
MediaSource source = MediaSource.from(sourceId);
CastMediaSource source = CastMediaSource.from(sourceId);
assertNotNull(source);
assertEquals("ABCD1234", source.getApplicationId());
......@@ -43,38 +41,39 @@ public class MediaSourceTest {
assertEquals("1234567890", source.getClientId());
assertEquals("tab_and_origin_scoped", source.getAutoJoinPolicy());
assertEquals(sourceId, source.getUrn());
assertEquals(sourceId, source.getSourceId());
}
@Test
@Feature({"MediaRouter"})
public void testNoFragment() {
assertNull(MediaSource.from("https://example.com/path?query"));
assertNull(CastMediaSource.from("https://example.com/path?query"));
}
@Test
@Feature({"MediaRouter"})
public void testEmptyFragment() {
assertNull(MediaSource.from("https://example.com/path?query#"));
assertNull(CastMediaSource.from("https://example.com/path?query#"));
}
@Test
@Feature({"MediaRouter"})
public void testNoAppId() {
assertNull(MediaSource.from("https://example.com/path?query#fragment"));
assertNull(CastMediaSource.from("https://example.com/path?query#fragment"));
}
@Test
@Feature({"MediaRouter"})
public void testNoValidAppId() {
// Invalid app id needs to indicate no availability so {@link MediaSource} needs to be
// Invalid app id needs to indicate no availability so {@link CastMediaSource} needs to be
// created.
MediaSource empty = MediaSource.from("https://example.com/path?query#__castAppId__=");
CastMediaSource empty =
CastMediaSource.from("https://example.com/path?query#__castAppId__=");
assertNotNull(empty);
assertEquals("", empty.getApplicationId());
MediaSource invalid = MediaSource.from(
"https://example.com/path?query#__castAppId__=INVALID-APP-ID");
CastMediaSource invalid =
CastMediaSource.from("https://example.com/path?query#__castAppId__=INVALID-APP-ID");
assertNotNull(invalid);
assertEquals("INVALID-APP-ID", invalid.getApplicationId());
}
......@@ -82,33 +81,35 @@ public class MediaSourceTest {
@Test
@Feature({"MediaRouter"})
public void testNoCapabilitiesSuffix() {
assertNull(MediaSource.from("https://example.com/path?query#__castAppId__=A("));
assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A("));
}
@Test
@Feature({"MediaRouter"})
public void testCapabilitiesEmpty() {
assertNull(MediaSource.from("https://example.com/path?query#__castAppId__=A()"));
assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A()"));
}
@Test
@Feature({"MediaRouter"})
public void testInvalidCapability() {
assertNull(MediaSource.from("https://example.com/path?query#__castAppId__=A(a)"));
assertNull(MediaSource.from("https://example.com/path?query#__castAppId__=A(video_in,b)"));
assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A(a)"));
assertNull(
CastMediaSource.from("https://example.com/path?query#__castAppId__=A(video_in,b)"));
}
@Test
@Feature({"MediaRouter"})
public void testInvalidAutoJoinPolicy() {
assertNull(MediaSource.from("https://example.com/path?query#__castAppId__=A"
assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A"
+ "/__castAutoJoinPolicy__=invalidPolicy"));
}
@Test
@Feature({"MediaRouter"})
public void testOptionalParameters() {
MediaSource source = MediaSource.from("https://example.com/path?query#__castAppId__=A");
CastMediaSource source =
CastMediaSource.from("https://example.com/path?query#__castAppId__=A");
assertNotNull(source);
assertEquals("A", source.getApplicationId());
......@@ -120,7 +121,7 @@ public class MediaSourceTest {
@Test
@Feature({"MediaRouter"})
public void testBasicCastPresentationUrl() {
MediaSource source = MediaSource.from("cast:ABCD1234");
CastMediaSource source = CastMediaSource.from("cast:ABCD1234");
assertNotNull(source);
assertEquals("ABCD1234", source.getApplicationId());
assertNull(source.getCapabilities());
......@@ -131,7 +132,7 @@ public class MediaSourceTest {
@Test
@Feature({"MediaRouter"})
public void testCastPresentationUrlWithParameters() {
MediaSource source = MediaSource.from("cast:ABCD1234?clientId=1234"
CastMediaSource source = CastMediaSource.from("cast:ABCD1234?clientId=1234"
+ "&capabilities=video_out,audio_out"
+ "&autoJoinPolicy=tab_and_origin_scoped");
assertNotNull(source);
......@@ -147,14 +148,14 @@ public class MediaSourceTest {
@Test
@Feature({"MediaRouter"})
public void testCastPresentationUrlInvalidCapability() {
assertNull(MediaSource.from("cast:ABCD1234?clientId=1234"
assertNull(CastMediaSource.from("cast:ABCD1234?clientId=1234"
+ "&capabilities=invalidCapability"));
}
@Test
@Feature({"MediaRouter"})
public void testCastPresentationUrlInvalidAutoJoinPolicy() {
assertNull(MediaSource.from("cast:ABCD1234?clientId=1234"
assertNull(CastMediaSource.from("cast:ABCD1234?clientId=1234"
+ "&autoJoinPolicy=invalidPolicy"));
}
}
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