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

[Android MR] Migrate RemotingCastSession to CAF

This CL migrates RemotingCastSession into RemotingSessionController (CAF
version). Changes include:

* Abstract common logic from CastSessionController into BaseSessionController.
* Add onSessionStarted() and onSessionEnded() callback to BaseSessionController
  to handle session-specific logic regardless of session suspend.
* Return the correct namespaces in CastSessionController

Please diff BaseSessionController from Patchset 2 for easy review (other files
should be diff'ed against base)

Bug: 711860
Change-Id: I2bcb71b38758bb7f7b3a46f25cc4d55fe5c2956f
Reviewed-on: https://chromium-review.googlesource.com/1220300
Commit-Queue: Zhiqiang Zhang <zqzhang@chromium.org>
Reviewed-by: default avatarThomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590566}
parent 95ec6984
// Copyright 2018 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.caf;
import android.support.annotation.Nullable;
import android.support.v7.media.MediaRouter;
import com.google.android.gms.cast.CastDevice;
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.chrome.browser.media.router.CastSessionUtil;
import org.chromium.chrome.browser.media.router.FlingingController;
import org.chromium.chrome.browser.media.router.MediaSink;
import org.chromium.chrome.browser.media.router.MediaSource;
import java.util.ArrayList;
import java.util.List;
/**
* A base wrapper for {@link CastSession}, extending its functionality for Chrome MediaRouter.
*
* Has persistent lifecycle and always attaches itself to the current {@link CastSession}.
*/
public class BaseSessionController {
private static final String TAG = "BaseSessionCtrl";
private CastSession mCastSession;
private final CafBaseMediaRouteProvider mProvider;
private final MediaRouter.Callback mMediaRouterCallbackForSessionLaunch;
private CreateRouteRequestInfo mRouteCreationInfo;
private final CafNotificationController mNotificationController;
private final RemoteMediaClient.Callback mRemoteMediaClientCallback;
public BaseSessionController(CafBaseMediaRouteProvider provider) {
mProvider = provider;
mMediaRouterCallbackForSessionLaunch = new MediaRouterCallbackForSessionLaunch();
mNotificationController = new CafNotificationController(this);
mRemoteMediaClientCallback = new RemoteMediaClientCallback();
}
public void requestSessionLaunch() {
mRouteCreationInfo = mProvider.getPendingCreateRouteRequestInfo();
CastUtils.getCastContext().setReceiverApplicationId(
mRouteCreationInfo.source.getApplicationId());
if (mRouteCreationInfo.routeInfo.isSelected()) {
// If a route has just been selected, CAF might not be ready yet before setting the app
// ID. So unselect and select the route will let CAF be aware that the route has been
// selected thus it can start the session.
//
// 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(
mRouteCreationInfo.source.buildRouteSelector(),
mMediaRouterCallbackForSessionLaunch);
mProvider.getAndroidMediaRouter().unselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
} else {
mRouteCreationInfo.routeInfo.select();
}
}
public MediaSource getSource() {
return (mRouteCreationInfo != null) ? mRouteCreationInfo.source : null;
}
public MediaSink getSink() {
return (mRouteCreationInfo != null) ? mRouteCreationInfo.sink : null;
}
public CreateRouteRequestInfo getRouteCreationInfo() {
return mRouteCreationInfo;
}
public CastSession getSession() {
return mCastSession;
}
public RemoteMediaClient getRemoteMediaClient() {
return mCastSession.getRemoteMediaClient();
}
public CafNotificationController getNotificationController() {
return mNotificationController;
}
public void endSession() {
MediaRouter mediaRouter = mProvider.getAndroidMediaRouter();
mediaRouter.selectRoute(mediaRouter.getDefaultRoute());
}
public List<String> getCapabilities() {
List<String> capabilities = new ArrayList<>();
if (mCastSession == null || !mCastSession.isConnected()) return capabilities;
CastDevice device = mCastSession.getCastDevice();
if (device.hasCapability(CastDevice.CAPABILITY_AUDIO_IN)) {
capabilities.add("audio_in");
}
if (device.hasCapability(CastDevice.CAPABILITY_AUDIO_OUT)) {
capabilities.add("audio_out");
}
if (device.hasCapability(CastDevice.CAPABILITY_VIDEO_IN)) {
capabilities.add("video_in");
}
if (device.hasCapability(CastDevice.CAPABILITY_VIDEO_OUT)) {
capabilities.add("video_out");
}
return capabilities;
}
public boolean isConnected() {
return mCastSession != null && mCastSession.isConnected();
}
private void updateRemoteMediaClient(String message) {
if (!isConnected()) return;
mCastSession.getRemoteMediaClient().onMessageReceived(
mCastSession.getCastDevice(), CastSessionUtil.MEDIA_NAMESPACE, message);
}
/** Attaches the controller to the current {@link CastSession}. */
public void attachToCastSession(CastSession session) {
mCastSession = session;
getRemoteMediaClient().registerCallback(mRemoteMediaClientCallback);
}
/** Detaches the controller from any {@link CastSession}. */
public void detachFromCastSession() {
if (mCastSession == null) return;
getRemoteMediaClient().unregisterCallback(mRemoteMediaClientCallback);
mCastSession = null;
}
/** Called when session started. */
public void onSessionStarted() {
mNotificationController.onSessionStarted();
}
/** Called when session ended. */
public void onSessionEnded() {
mNotificationController.onSessionEnded();
mRouteCreationInfo = null;
}
protected final CafBaseMediaRouteProvider getProvider() {
return mProvider;
}
/**
* All sub-classes need to register this method to listen to messages of the namespaces they are
* interested in.
*/
protected void onMessageReceived(CastDevice castDevice, String namespace, String message) {
Log.d(TAG,
"Received message from Cast device: namespace=\"" + namespace + "\" message=\""
+ message + "\"");
if (CastSessionUtil.MEDIA_NAMESPACE.equals(namespace)) {
updateRemoteMediaClient(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);
}
}
}
private class RemoteMediaClientCallback extends RemoteMediaClient.Callback {
@Override
public void onStatusUpdated() {
mNotificationController.onStatusUpdated();
}
@Override
public void onMetadataUpdated() {
mNotificationController.onMetadataUpdated();
}
}
@Nullable
public FlingingController getFlingingController() {
return null;
}
}
...@@ -217,7 +217,7 @@ public abstract class CafBaseMediaRouteProvider ...@@ -217,7 +217,7 @@ public abstract class CafBaseMediaRouteProvider
public void onSessionStarted(CastSession session, String sessionId) { public void onSessionStarted(CastSession session, String sessionId) {
Log.d(TAG, "onSessionStarted"); Log.d(TAG, "onSessionStarted");
mSessionController.attachToCastSession(session); mSessionController.attachToCastSession(session);
sessionController().getNotificationController().onSessionStarted(); sessionController().onSessionStarted();
MediaSink sink = mPendingCreateRouteRequestInfo.sink; MediaSink sink = mPendingCreateRouteRequestInfo.sink;
MediaSource source = mPendingCreateRouteRequestInfo.source; MediaSource source = mPendingCreateRouteRequestInfo.source;
...@@ -269,7 +269,6 @@ public abstract class CafBaseMediaRouteProvider ...@@ -269,7 +269,6 @@ public abstract class CafBaseMediaRouteProvider
// sure the listener is not unregistered during a session relaunch. // sure the listener is not unregistered during a session relaunch.
return; return;
} }
mSessionController.getNotificationController().onSessionEnded();
mSessionController.detachFromCastSession(); mSessionController.detachFromCastSession();
mSessionController.onSessionEnded(); mSessionController.onSessionEnded();
getAndroidMediaRouter().selectRoute(getAndroidMediaRouter().getDefaultRoute()); getAndroidMediaRouter().selectRoute(getAndroidMediaRouter().getDefaultRoute());
......
...@@ -494,8 +494,6 @@ public class CafMessageHandler { ...@@ -494,8 +494,6 @@ public class CafMessageHandler {
*/ */
@VisibleForTesting @VisibleForTesting
void onMediaMessage(String message, RequestRecord request) { void onMediaMessage(String message, RequestRecord request) {
mSessionController.updateRemoteMediaClient(message);
if (isMediaStatusMessage(message)) { if (isMediaStatusMessage(message)) {
// MEDIA_STATUS needs to be sent to all the clients. // MEDIA_STATUS needs to be sent to all the clients.
for (String clientId : mRouteProvider.getClientIdToRecords().keySet()) { for (String clientId : mRouteProvider.getClientIdToRecords().keySet()) {
......
...@@ -21,9 +21,9 @@ import org.chromium.content_public.common.MediaMetadata; ...@@ -21,9 +21,9 @@ import org.chromium.content_public.common.MediaMetadata;
/** Controller for updating media notification for Casting and MediaFling. */ /** Controller for updating media notification for Casting and MediaFling. */
public class CafNotificationController implements MediaNotificationListener { public class CafNotificationController implements MediaNotificationListener {
private MediaNotificationInfo.Builder mNotificationBuilder; private MediaNotificationInfo.Builder mNotificationBuilder;
private final CastSessionController mSessionController; private final BaseSessionController mSessionController;
public CafNotificationController(CastSessionController sessionController) { public CafNotificationController(BaseSessionController sessionController) {
mSessionController = sessionController; mSessionController = sessionController;
} }
......
// Copyright 2018 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.caf.remoting;
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import org.chromium.chrome.browser.media.router.FlingingController;
import org.chromium.chrome.browser.media.router.MediaController;
import org.chromium.chrome.browser.media.router.MediaStatusObserver;
/** Adapter class for bridging {@link RemoteMediaClient} and {@link FlingController}. */
public class FlingingControllerAdapter implements FlingingController {
FlingingControllerAdapter(RemotingSessionController sessionController) {}
@Override
public MediaController getMediaController() {
// Not implemented.
return null;
}
@Override
public void setMediaStatusObserver(MediaStatusObserver observer) {
// Not implemented.
}
@Override
public void clearMediaStatusObserver() {
// Not implemented.
}
@Override
public long getApproximateCurrentTime() {
// Not implemented.
return 0;
}
}
// Copyright 2018 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.caf.remoting;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.framework.CastSession;
import org.chromium.base.Log;
import org.chromium.chrome.browser.media.router.CastSessionUtil;
import org.chromium.chrome.browser.media.router.FlingingController;
import org.chromium.chrome.browser.media.router.caf.BaseSessionController;
import org.chromium.chrome.browser.media.router.caf.CafBaseMediaRouteProvider;
import org.chromium.chrome.browser.media.router.cast.remoting.RemotingMediaSource;
/** Wrapper for {@link CastSession} for remoting. */
public class RemotingSessionController extends BaseSessionController {
private static final String TAG = "RmtSessionCtrl";
private FlingingControllerAdapter mFlingingControllerAdapter;
RemotingSessionController(CafBaseMediaRouteProvider provider) {
super(provider);
mFlingingControllerAdapter = new FlingingControllerAdapter(this);
}
@Override
public void attachToCastSession(CastSession session) {
super.attachToCastSession(session);
try {
getSession().setMessageReceivedCallbacks(
CastSessionUtil.MEDIA_NAMESPACE, this ::onMessageReceived);
} catch (Exception e) {
Log.e(TAG, "Failed to register namespace listener for %s",
CastSessionUtil.MEDIA_NAMESPACE, e);
}
}
@Override
public void onSessionStarted() {
getRemoteMediaClient().load(
new MediaInfo.Builder(((RemotingMediaSource) getSource()).getMediaUrl()).build());
}
@Override
public FlingingController getFlingingController() {
return mFlingingControllerAdapter;
}
}
...@@ -35,6 +35,7 @@ import java.util.Set; ...@@ -35,6 +35,7 @@ import java.util.Set;
/** /**
* A wrapper around a RemoteMediaPlayer, used in remote playback. * A wrapper around a RemoteMediaPlayer, used in remote playback.
*/ */
// Migrated to RemotingSessionController. See https://crbug.com/711860.
public class RemotingCastSession public class RemotingCastSession
implements MediaNotificationListener, CastSession, Cast.MessageReceivedCallback { implements MediaNotificationListener, CastSession, Cast.MessageReceivedCallback {
private static final String TAG = "MediaRouter"; private static final String TAG = "MediaRouter";
......
...@@ -792,6 +792,7 @@ chrome_java_sources = [ ...@@ -792,6 +792,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/media/router/MediaStatusBridge.java", "java/src/org/chromium/chrome/browser/media/router/MediaStatusBridge.java",
"java/src/org/chromium/chrome/browser/media/router/MediaStatusObserver.java", "java/src/org/chromium/chrome/browser/media/router/MediaStatusObserver.java",
"java/src/org/chromium/chrome/browser/media/router/MediaSink.java", "java/src/org/chromium/chrome/browser/media/router/MediaSink.java",
"java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java",
"java/src/org/chromium/chrome/browser/media/router/caf/CastOptionsProvider.java", "java/src/org/chromium/chrome/browser/media/router/caf/CastOptionsProvider.java",
"java/src/org/chromium/chrome/browser/media/router/caf/CastSessionController.java", "java/src/org/chromium/chrome/browser/media/router/caf/CastSessionController.java",
"java/src/org/chromium/chrome/browser/media/router/caf/CastUtils.java", "java/src/org/chromium/chrome/browser/media/router/caf/CastUtils.java",
...@@ -800,6 +801,8 @@ chrome_java_sources = [ ...@@ -800,6 +801,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java", "java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java",
"java/src/org/chromium/chrome/browser/media/router/caf/CafNotificationController.java", "java/src/org/chromium/chrome/browser/media/router/caf/CafNotificationController.java",
"java/src/org/chromium/chrome/browser/media/router/caf/CreateRouteRequestInfo.java", "java/src/org/chromium/chrome/browser/media/router/caf/CreateRouteRequestInfo.java",
"java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingSessionController.java",
"java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java",
"java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java", "java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java",
"java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.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/CastMediaSource.java",
......
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