Merge "MediaRouter: Defer releasing routing controller when transfer" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
82ff3f1472
@@ -34,7 +34,8 @@ oneway interface IMediaRouter2 {
|
||||
void notifySessionReleased(in RoutingSessionInfo sessionInfo);
|
||||
/**
|
||||
* Gets hints of the new session for the given route.
|
||||
* Call MediaRouterService#notifySessionHintsForCreatingSession to pass the result.
|
||||
* Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result.
|
||||
*/
|
||||
void getSessionHintsForCreatingSession(long uniqueRequestId, in MediaRoute2Info route);
|
||||
void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
|
||||
in MediaRoute2Info route);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ interface IMediaRouterService {
|
||||
void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
|
||||
void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
|
||||
|
||||
// Note: When changing this file, match the order of methods below with
|
||||
// Note: When changing this file, match the order of methods below with
|
||||
// MediaRouterService.java for readability.
|
||||
|
||||
// Methods for MediaRouter2
|
||||
@@ -57,10 +57,9 @@ interface IMediaRouterService {
|
||||
in RouteDiscoveryPreference preference);
|
||||
void setRouteVolumeWithRouter2(IMediaRouter2 router, in MediaRoute2Info route, int volume);
|
||||
|
||||
void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
|
||||
in MediaRoute2Info route, in @nullable Bundle sessionHints);
|
||||
void notifySessionHintsForCreatingSession(IMediaRouter2 router, long uniqueRequestId,
|
||||
in MediaRoute2Info route, in @nullable Bundle sessionHints);
|
||||
void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
|
||||
in RoutingSessionInfo oldSession, in MediaRoute2Info route,
|
||||
in @nullable Bundle sessionHints);
|
||||
void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
|
||||
void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
|
||||
void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
|
||||
@@ -76,7 +75,7 @@ interface IMediaRouterService {
|
||||
in MediaRoute2Info route, int volume);
|
||||
|
||||
void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
|
||||
String packageName, in @nullable MediaRoute2Info route);
|
||||
in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
|
||||
void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
|
||||
String sessionId, in MediaRoute2Info route);
|
||||
void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -62,6 +61,11 @@ public final class MediaRouter2 {
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final Object sRouterLock = new Object();
|
||||
|
||||
// The maximum time for the old routing controller available after transfer.
|
||||
private static final int TRANSFER_TIMEOUT_MS = 30_000;
|
||||
// The manager request ID representing that no manager is involved.
|
||||
private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE;
|
||||
|
||||
@GuardedBy("sRouterLock")
|
||||
private static MediaRouter2 sInstance;
|
||||
|
||||
@@ -80,7 +84,7 @@ public final class MediaRouter2 {
|
||||
|
||||
private final String mPackageName;
|
||||
@GuardedBy("sRouterLock")
|
||||
final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
|
||||
final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>();
|
||||
|
||||
final RoutingController mSystemController;
|
||||
|
||||
@@ -94,7 +98,7 @@ public final class MediaRouter2 {
|
||||
@GuardedBy("sRouterLock")
|
||||
private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>();
|
||||
|
||||
private final AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
|
||||
private final AtomicInteger mNextRequestId = new AtomicInteger(1);
|
||||
|
||||
final Handler mHandler;
|
||||
@GuardedBy("sRouterLock")
|
||||
@@ -412,9 +416,16 @@ public final class MediaRouter2 {
|
||||
return;
|
||||
}
|
||||
|
||||
final int requestId = mControllerCreationRequestCnt.getAndIncrement();
|
||||
requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
|
||||
}
|
||||
|
||||
ControllerCreationRequest request = new ControllerCreationRequest(requestId, route);
|
||||
void requestCreateController(@NonNull RoutingController controller,
|
||||
@NonNull MediaRoute2Info route, long managerRequestId) {
|
||||
|
||||
final int requestId = mNextRequestId.getAndIncrement();
|
||||
|
||||
ControllerCreationRequest request = new ControllerCreationRequest(requestId,
|
||||
managerRequestId, route, controller);
|
||||
mControllerCreationRequests.add(request);
|
||||
|
||||
OnGetControllerHintsListener listener = mOnGetControllerHintsListener;
|
||||
@@ -433,11 +444,15 @@ public final class MediaRouter2 {
|
||||
if (stub != null) {
|
||||
try {
|
||||
mMediaRouterService.requestCreateSessionWithRouter2(
|
||||
stub, requestId, route, controllerHints);
|
||||
stub, requestId, managerRequestId,
|
||||
controller.getRoutingSessionInfo(), route, controllerHints);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "transfer: Unable to request to create controller.", ex);
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
|
||||
MediaRouter2.this, requestId, null));
|
||||
Log.e(TAG, "createControllerForTransfer: "
|
||||
+ "Failed to request for creating a controller.", ex);
|
||||
mControllerCreationRequests.remove(request);
|
||||
if (managerRequestId == MANAGER_REQUEST_ID_NONE) {
|
||||
notifyTransferFailure(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +478,8 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of currently non-released {@link RoutingController routing controllers}.
|
||||
* Gets the list of currently active {@link RoutingController routing controllers} on which
|
||||
* media can be played.
|
||||
* <p>
|
||||
* Note: The list returned here will never be empty. The first element in the list is
|
||||
* always the {@link #getSystemController() system controller}.
|
||||
@@ -554,13 +570,13 @@ public final class MediaRouter2 {
|
||||
mShouldUpdateRoutes = true;
|
||||
}
|
||||
|
||||
if (addedRoutes.size() > 0) {
|
||||
if (!addedRoutes.isEmpty()) {
|
||||
notifyRoutesAdded(addedRoutes);
|
||||
}
|
||||
if (removedRoutes.size() > 0) {
|
||||
if (!removedRoutes.isEmpty()) {
|
||||
notifyRoutesRemoved(removedRoutes);
|
||||
}
|
||||
if (changedRoutes.size() > 0) {
|
||||
if (!changedRoutes.isEmpty()) {
|
||||
notifyRoutesChanged(changedRoutes);
|
||||
}
|
||||
|
||||
@@ -582,7 +598,7 @@ public final class MediaRouter2 {
|
||||
}
|
||||
mShouldUpdateRoutes = true;
|
||||
}
|
||||
if (addedRoutes.size() > 0) {
|
||||
if (!addedRoutes.isEmpty()) {
|
||||
notifyRoutesAdded(addedRoutes);
|
||||
}
|
||||
}
|
||||
@@ -598,7 +614,7 @@ public final class MediaRouter2 {
|
||||
}
|
||||
mShouldUpdateRoutes = true;
|
||||
}
|
||||
if (removedRoutes.size() > 0) {
|
||||
if (!removedRoutes.isEmpty()) {
|
||||
notifyRoutesRemoved(removedRoutes);
|
||||
}
|
||||
}
|
||||
@@ -614,7 +630,7 @@ public final class MediaRouter2 {
|
||||
}
|
||||
mShouldUpdateRoutes = true;
|
||||
}
|
||||
if (changedRoutes.size() > 0) {
|
||||
if (!changedRoutes.isEmpty()) {
|
||||
notifyRoutesChanged(changedRoutes);
|
||||
}
|
||||
}
|
||||
@@ -635,44 +651,47 @@ public final class MediaRouter2 {
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingRequest != null) {
|
||||
mControllerCreationRequests.remove(matchingRequest);
|
||||
|
||||
MediaRoute2Info requestedRoute = matchingRequest.mRoute;
|
||||
|
||||
if (sessionInfo == null) {
|
||||
// TODO: We may need to distinguish between failure and rejection.
|
||||
// One way can be introducing 'reason'.
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
} else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
|
||||
Log.w(TAG, "The session does not contain the requested route. "
|
||||
+ "(requestedRouteId=" + requestedRoute.getId()
|
||||
+ ", actualRoutes=" + sessionInfo.getSelectedRoutes()
|
||||
+ ")");
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
} else if (!TextUtils.equals(requestedRoute.getProviderId(),
|
||||
sessionInfo.getProviderId())) {
|
||||
Log.w(TAG, "The session's provider ID does not match the requested route's. "
|
||||
+ "(requested route's providerId=" + requestedRoute.getProviderId()
|
||||
+ ", actual providerId=" + sessionInfo.getProviderId()
|
||||
+ ")");
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sessionInfo == null) {
|
||||
if (matchingRequest == null) {
|
||||
Log.w(TAG, "createControllerOnHandler: Ignoring an unknown request.");
|
||||
return;
|
||||
}
|
||||
|
||||
RoutingController oldController = getCurrentController();
|
||||
if (!oldController.releaseInternal(
|
||||
/* shouldReleaseSession= */ matchingRequest != null,
|
||||
/* shouldNotifyStop= */ false)) {
|
||||
// Could not release the controller since it was just released by other thread.
|
||||
oldController = getSystemController();
|
||||
mControllerCreationRequests.remove(matchingRequest);
|
||||
MediaRoute2Info requestedRoute = matchingRequest.mRoute;
|
||||
|
||||
// TODO: Notify the reason for failure.
|
||||
if (sessionInfo == null) {
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
} else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
|
||||
Log.w(TAG, "The session does not contain the requested route. "
|
||||
+ "(requestedRouteId=" + requestedRoute.getId()
|
||||
+ ", actualRoutes=" + sessionInfo.getSelectedRoutes()
|
||||
+ ")");
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
} else if (!TextUtils.equals(requestedRoute.getProviderId(),
|
||||
sessionInfo.getProviderId())) {
|
||||
Log.w(TAG, "The session's provider ID does not match the requested route's. "
|
||||
+ "(requested route's providerId=" + requestedRoute.getProviderId()
|
||||
+ ", actual providerId=" + sessionInfo.getProviderId()
|
||||
+ ")");
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
RoutingController oldController = matchingRequest.mOldController;
|
||||
// When the old controller is released before transferred, treat it as a failure.
|
||||
// This could also happen when transfer is requested twice or more.
|
||||
if (!oldController.scheduleRelease()) {
|
||||
Log.w(TAG, "createControllerOnHandler: "
|
||||
+ "Ignoring controller creation for released old controller. "
|
||||
+ "oldController=" + oldController);
|
||||
if (!sessionInfo.isSystemSession()) {
|
||||
new RoutingController(sessionInfo).release();
|
||||
}
|
||||
notifyTransferFailure(requestedRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
RoutingController newController;
|
||||
@@ -686,12 +705,7 @@ public final class MediaRouter2 {
|
||||
}
|
||||
}
|
||||
|
||||
// Two controller can be same if stop() is called before the result of Cast -> Phone comes.
|
||||
if (oldController != newController) {
|
||||
notifyTransfer(oldController, newController);
|
||||
} else if (matchingRequest != null) {
|
||||
notifyTransferFailure(matchingRequest.mRoute);
|
||||
}
|
||||
notifyTransfer(oldController, newController);
|
||||
}
|
||||
|
||||
void updateControllerOnHandler(RoutingSessionInfo sessionInfo) {
|
||||
@@ -736,10 +750,9 @@ public final class MediaRouter2 {
|
||||
return;
|
||||
}
|
||||
|
||||
final String uniqueSessionId = sessionInfo.getId();
|
||||
RoutingController matchingController;
|
||||
synchronized (sRouterLock) {
|
||||
matchingController = mNonSystemRoutingControllers.get(uniqueSessionId);
|
||||
matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
|
||||
}
|
||||
|
||||
if (matchingController == null) {
|
||||
@@ -757,34 +770,23 @@ public final class MediaRouter2 {
|
||||
return;
|
||||
}
|
||||
|
||||
matchingController.releaseInternal(
|
||||
/* shouldReleaseSession= */ false, /* shouldNotifyStop= */ true);
|
||||
matchingController.releaseInternal(/* shouldReleaseSession= */ false);
|
||||
}
|
||||
|
||||
void onGetControllerHintsForCreatingSessionOnHandler(long uniqueRequestId,
|
||||
MediaRoute2Info route) {
|
||||
OnGetControllerHintsListener listener = mOnGetControllerHintsListener;
|
||||
Bundle controllerHints = null;
|
||||
if (listener != null) {
|
||||
controllerHints = listener.onGetControllerHints(route);
|
||||
if (controllerHints != null) {
|
||||
controllerHints = new Bundle(controllerHints);
|
||||
void onRequestCreateControllerByManagerOnHandler(RoutingSessionInfo oldSession,
|
||||
MediaRoute2Info route, long managerRequestId) {
|
||||
RoutingController controller;
|
||||
if (oldSession.isSystemSession()) {
|
||||
controller = getSystemController();
|
||||
} else {
|
||||
synchronized (sRouterLock) {
|
||||
controller = mNonSystemRoutingControllers.get(oldSession.getId());
|
||||
}
|
||||
}
|
||||
|
||||
MediaRouter2Stub stub;
|
||||
synchronized (sRouterLock) {
|
||||
stub = mStub;
|
||||
}
|
||||
if (stub != null) {
|
||||
try {
|
||||
mMediaRouterService.notifySessionHintsForCreatingSession(
|
||||
stub, uniqueRequestId, route, controllerHints);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "onGetControllerHintsForCreatingSessionOnHandler: Unable to notify "
|
||||
+ " session hints for creating session.", ex);
|
||||
}
|
||||
if (controller == null) {
|
||||
return;
|
||||
}
|
||||
requestCreateController(controller, route, managerRequestId);
|
||||
}
|
||||
|
||||
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
|
||||
@@ -886,8 +888,13 @@ public final class MediaRouter2 {
|
||||
/**
|
||||
* Called when a media is transferred between two different routing controllers.
|
||||
* This can happen by calling {@link #transferTo(MediaRoute2Info)}.
|
||||
* The {@code oldController} is released before this method is called, except for the
|
||||
* {@link #getSystemController() system controller}.
|
||||
* <p> Override this to start playback with {@code newController}. You may want to get
|
||||
* the status of the media that is being played with {@code oldController} and resume it
|
||||
* continuously with {@code newController}.
|
||||
* After this is called, any callbacks with {@code oldController} will not be invoked
|
||||
* unless {@code oldController} is the {@link #getSystemController() system controller}.
|
||||
* You need to {@link RoutingController#release() release} {@code oldController} before
|
||||
* playing the media with {@code newController}.
|
||||
*
|
||||
* @param oldController the previous controller that controlled routing
|
||||
* @param newController the new controller to control routing
|
||||
@@ -906,16 +913,15 @@ public final class MediaRouter2 {
|
||||
/**
|
||||
* Called when a media routing stops. It can be stopped by a user or a provider.
|
||||
* App should not continue playing media locally when this method is called.
|
||||
* The {@code oldController} is released before this method is called, except for the
|
||||
* {@link #getSystemController() system controller}.
|
||||
* The {@code controller} is released before this method is called.
|
||||
*
|
||||
* @param controller the controller that controlled the stopped media routing.
|
||||
* @param controller the controller that controlled the stopped media routing
|
||||
*/
|
||||
public void onStop(@NonNull RoutingController controller) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener interface to send an optional app-specific hints when creating the
|
||||
* A listener interface to send optional app-specific hints when creating a
|
||||
* {@link RoutingController}.
|
||||
*/
|
||||
public interface OnGetControllerHintsListener {
|
||||
@@ -929,9 +935,9 @@ public final class MediaRouter2 {
|
||||
* The method will be called on the same thread that calls
|
||||
* {@link #transferTo(MediaRoute2Info)} or the main thread if it is requested by the system.
|
||||
*
|
||||
* @param route The route to create controller with
|
||||
* @param route the route to create a controller with
|
||||
* @return An optional bundle of app-specific arguments to send to the provider,
|
||||
* or null if none. The contents of this bundle may affect the result of
|
||||
* or {@code null} if none. The contents of this bundle may affect the result of
|
||||
* controller creation.
|
||||
* @see MediaRoute2ProviderService#onCreateSession(long, String, String, Bundle)
|
||||
*/
|
||||
@@ -944,10 +950,11 @@ public final class MediaRouter2 {
|
||||
*/
|
||||
public abstract static class ControllerCallback {
|
||||
/**
|
||||
* Called when a controller is updated. (e.g., the selected routes of the
|
||||
* controller is changed or the volume of the controller is changed.)
|
||||
* Called when a controller is updated. (e.g., when the selected routes of the
|
||||
* controller is changed or when the volume of the controller is changed.)
|
||||
*
|
||||
* @param controller the updated controller. Can be the system controller.
|
||||
* @param controller the updated controller. It may be the
|
||||
* {@link #getSystemController() system controller}.
|
||||
* @see #getSystemController()
|
||||
*/
|
||||
public void onControllerUpdated(@NonNull RoutingController controller) { }
|
||||
@@ -955,20 +962,28 @@ public final class MediaRouter2 {
|
||||
|
||||
/**
|
||||
* A class to control media routing session in media route provider.
|
||||
* For example, selecting/deselecting/transferring routes to a session can be done through this
|
||||
* class. Instances are created by {@link #transferTo(MediaRoute2Info)}.
|
||||
* For example, selecting/deselecting/transferring to routes of a session can be done through
|
||||
* this. Instances are created when
|
||||
* {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is called,
|
||||
* which is invoked after {@link #transferTo(MediaRoute2Info)} is called.
|
||||
*/
|
||||
public class RoutingController {
|
||||
private final Object mControllerLock = new Object();
|
||||
|
||||
private static final int CONTROLLER_STATE_UNKNOWN = 0;
|
||||
private static final int CONTROLLER_STATE_ACTIVE = 1;
|
||||
private static final int CONTROLLER_STATE_RELEASING = 2;
|
||||
private static final int CONTROLLER_STATE_RELEASED = 3;
|
||||
|
||||
@GuardedBy("mControllerLock")
|
||||
private RoutingSessionInfo mSessionInfo;
|
||||
|
||||
@GuardedBy("mControllerLock")
|
||||
private volatile boolean mIsReleased;
|
||||
private int mState;
|
||||
|
||||
RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
|
||||
mSessionInfo = sessionInfo;
|
||||
mState = CONTROLLER_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -982,7 +997,7 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original session id set by
|
||||
* Gets the original session ID set by
|
||||
* {@link RoutingSessionInfo.Builder#Builder(String, String)}.
|
||||
*
|
||||
* @hide
|
||||
@@ -996,7 +1011,8 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the control hints used to control routing session if available.
|
||||
* Gets the control hints used to control routing session if available.
|
||||
* It is set by the media route provider.
|
||||
*/
|
||||
@Nullable
|
||||
public Bundle getControlHints() {
|
||||
@@ -1042,7 +1058,9 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about how volume is handled on the session.
|
||||
* Gets the information about how volume is handled on the session.
|
||||
* <p>Please note that you may not control the volume of the session even when
|
||||
* you can control the volume of each selected route in the session.
|
||||
*
|
||||
* @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
|
||||
* {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}
|
||||
@@ -1067,8 +1085,8 @@ public final class MediaRouter2 {
|
||||
* Gets the current volume of the session.
|
||||
* <p>
|
||||
* When it's available, it represents the volume of routing session, which is a group
|
||||
* of selected routes. To get the volume of a route,
|
||||
* use {@link MediaRoute2Info#getVolume()}.
|
||||
* of selected routes. Use {@link MediaRoute2Info#getVolume()}
|
||||
* to get the volume of a route,
|
||||
* </p>
|
||||
* @see MediaRoute2Info#getVolume()
|
||||
*/
|
||||
@@ -1087,7 +1105,7 @@ public final class MediaRouter2 {
|
||||
*/
|
||||
public boolean isReleased() {
|
||||
synchronized (mControllerLock) {
|
||||
return mIsReleased;
|
||||
return mState == CONTROLLER_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,8 +1117,8 @@ public final class MediaRouter2 {
|
||||
* <p>
|
||||
* The given route must satisfy all of the following conditions:
|
||||
* <ul>
|
||||
* <li>ID should not be included in {@link #getSelectedRoutes()}</li>
|
||||
* <li>ID should be included in {@link #getSelectableRoutes()}</li>
|
||||
* <li>It should not be included in {@link #getSelectedRoutes()}</li>
|
||||
* <li>It should be included in {@link #getSelectableRoutes()}</li>
|
||||
* </ul>
|
||||
* If the route doesn't meet any of above conditions, it will be ignored.
|
||||
*
|
||||
@@ -1111,11 +1129,9 @@ public final class MediaRouter2 {
|
||||
*/
|
||||
public void selectRoute(@NonNull MediaRoute2Info route) {
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
synchronized (mControllerLock) {
|
||||
if (mIsReleased) {
|
||||
Log.w(TAG, "selectRoute: Called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
if (isReleased()) {
|
||||
Log.w(TAG, "selectRoute: Called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
|
||||
@@ -1145,12 +1161,12 @@ public final class MediaRouter2 {
|
||||
|
||||
/**
|
||||
* Deselects a route from the remote session. After a route is deselected, the media is
|
||||
* expected to be stopped on the deselected routes.
|
||||
* expected to be stopped on the deselected route.
|
||||
* <p>
|
||||
* The given route must satisfy all of the following conditions:
|
||||
* <ul>
|
||||
* <li>ID should be included in {@link #getSelectedRoutes()}</li>
|
||||
* <li>ID should be included in {@link #getDeselectableRoutes()}</li>
|
||||
* <li>It should be included in {@link #getSelectedRoutes()}</li>
|
||||
* <li>It should be included in {@link #getDeselectableRoutes()}</li>
|
||||
* </ul>
|
||||
* If the route doesn't meet any of above conditions, it will be ignored.
|
||||
*
|
||||
@@ -1160,11 +1176,9 @@ public final class MediaRouter2 {
|
||||
*/
|
||||
public void deselectRoute(@NonNull MediaRoute2Info route) {
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
synchronized (mControllerLock) {
|
||||
if (mIsReleased) {
|
||||
Log.w(TAG, "deselectRoute: called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
if (isReleased()) {
|
||||
Log.w(TAG, "deselectRoute: called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
|
||||
@@ -1193,13 +1207,8 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers to a given route for the remote session. The given route must satisfy
|
||||
* all of the following conditions:
|
||||
* <ul>
|
||||
* <li>ID should not be included in {@link RoutingSessionInfo#getSelectedRoutes()}</li>
|
||||
* <li>ID should be included in {@link RoutingSessionInfo#getTransferableRoutes()}</li>
|
||||
* </ul>
|
||||
* If the route doesn't meet any of above conditions, it will be ignored.
|
||||
* Transfers to a given route for the remote session. The given route must be included
|
||||
* in {@link RoutingSessionInfo#getTransferableRoutes()}.
|
||||
*
|
||||
* @see RoutingSessionInfo#getSelectedRoutes()
|
||||
* @see RoutingSessionInfo#getTransferableRoutes()
|
||||
@@ -1208,19 +1217,13 @@ public final class MediaRouter2 {
|
||||
void transferToRoute(@NonNull MediaRoute2Info route) {
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
synchronized (mControllerLock) {
|
||||
if (mIsReleased) {
|
||||
if (isReleased()) {
|
||||
Log.w(TAG, "transferToRoute: Called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSessionInfo.getSelectedRoutes().contains(route.getId())) {
|
||||
Log.w(TAG, "Ignoring transferring to a route that is already added. "
|
||||
+ "route=" + route);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSessionInfo.getTransferableRoutes().contains(route.getId())) {
|
||||
Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
|
||||
Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1255,11 +1258,9 @@ public final class MediaRouter2 {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mControllerLock) {
|
||||
if (mIsReleased) {
|
||||
Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
if (isReleased()) {
|
||||
Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
|
||||
return;
|
||||
}
|
||||
MediaRouter2Stub stub;
|
||||
synchronized (sRouterLock) {
|
||||
@@ -1275,33 +1276,58 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
/**
|
||||
* Release this controller and corresponding session.
|
||||
* Releases this controller and the corresponding session.
|
||||
* Any operations on this controller after calling this method will be ignored.
|
||||
* The devices that are playing media will stop playing it.
|
||||
*/
|
||||
// TODO(b/157872573): Add tests using {@link MediaRouter2Manager#getActiveSessions()}.
|
||||
public void release() {
|
||||
releaseInternal(/* shouldReleaseSession= */ true, /* shouldNotifyStop= */ true);
|
||||
releaseInternal(/* shouldReleaseSession= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} when succeeded to release, {@code false} if the controller is
|
||||
* already released.
|
||||
* Schedules release of the controller.
|
||||
* @return {@code true} if it's successfully scheduled, {@code false} if it's already
|
||||
* scheduled to be released or released.
|
||||
*/
|
||||
boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) {
|
||||
boolean scheduleRelease() {
|
||||
synchronized (mControllerLock) {
|
||||
if (mIsReleased) {
|
||||
Log.w(TAG, "releaseInternal: Called on released controller. Ignoring.");
|
||||
if (mState != CONTROLLER_STATE_ACTIVE) {
|
||||
return false;
|
||||
}
|
||||
mIsReleased = true;
|
||||
mState = CONTROLLER_STATE_RELEASING;
|
||||
}
|
||||
|
||||
synchronized (sRouterLock) {
|
||||
// It could happen if the controller is released by the another thread
|
||||
// in between two locks
|
||||
if (!mNonSystemRoutingControllers.remove(getId(), this)) {
|
||||
Log.w(TAG, "releaseInternal: Ignoring unknown controller.");
|
||||
return false;
|
||||
// In that case, onStop isn't called so we return true to call onTransfer.
|
||||
// It's also consistent with that the another thread acquires the lock later.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mHandler.postDelayed(this::release, TRANSFER_TIMEOUT_MS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void releaseInternal(boolean shouldReleaseSession) {
|
||||
boolean shouldNotifyStop;
|
||||
|
||||
synchronized (mControllerLock) {
|
||||
if (mState == CONTROLLER_STATE_RELEASED) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "releaseInternal: Called on released controller. Ignoring.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
shouldNotifyStop = (mState == CONTROLLER_STATE_ACTIVE);
|
||||
mState = CONTROLLER_STATE_RELEASED;
|
||||
}
|
||||
|
||||
synchronized (sRouterLock) {
|
||||
mNonSystemRoutingControllers.remove(getId(), this);
|
||||
|
||||
if (shouldReleaseSession && mStub != null) {
|
||||
try {
|
||||
@@ -1326,7 +1352,6 @@ public final class MediaRouter2 {
|
||||
mStub = null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1389,9 +1414,14 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) {
|
||||
boolean scheduleRelease() {
|
||||
// SystemRoutingController can be always transferred
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void releaseInternal(boolean shouldReleaseSession) {
|
||||
// Do nothing. SystemRoutingController will never be released
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1442,8 +1472,7 @@ public final class MediaRouter2 {
|
||||
if (!(obj instanceof TransferCallbackRecord)) {
|
||||
return false;
|
||||
}
|
||||
return mTransferCallback
|
||||
== ((TransferCallbackRecord) obj).mTransferCallback;
|
||||
return mTransferCallback == ((TransferCallbackRecord) obj).mTransferCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1481,11 +1510,17 @@ public final class MediaRouter2 {
|
||||
|
||||
static final class ControllerCreationRequest {
|
||||
public final int mRequestId;
|
||||
public final long mManagerRequestId;
|
||||
public final MediaRoute2Info mRoute;
|
||||
public final RoutingController mOldController;
|
||||
|
||||
ControllerCreationRequest(int requestId, @NonNull MediaRoute2Info route) {
|
||||
ControllerCreationRequest(int requestId, long managerRequestId,
|
||||
@NonNull MediaRoute2Info route, @NonNull RoutingController oldController) {
|
||||
mRequestId = requestId;
|
||||
mRoute = route;
|
||||
mManagerRequestId = managerRequestId;
|
||||
mRoute = Objects.requireNonNull(route, "route must not be null");
|
||||
mOldController = Objects.requireNonNull(oldController,
|
||||
"oldController must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1534,11 +1569,11 @@ public final class MediaRouter2 {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSessionHintsForCreatingSession(long uniqueRequestId,
|
||||
@NonNull MediaRoute2Info route) {
|
||||
public void requestCreateSessionByManager(long managerRequestId,
|
||||
RoutingSessionInfo oldSession, MediaRoute2Info route) {
|
||||
mHandler.sendMessage(obtainMessage(
|
||||
MediaRouter2::onGetControllerHintsForCreatingSessionOnHandler,
|
||||
MediaRouter2.this, uniqueRequestId, route));
|
||||
MediaRouter2::onRequestCreateControllerByManagerOnHandler,
|
||||
MediaRouter2.this, oldSession, route, managerRequestId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,12 @@ import java.util.stream.Collectors;
|
||||
public final class MediaRouter2Manager {
|
||||
private static final String TAG = "MR2Manager";
|
||||
private static final Object sLock = new Object();
|
||||
/**
|
||||
* The request ID for requests not asked by this instance.
|
||||
* Shouldn't be used for a valid request.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REQUEST_ID_NONE = 0;
|
||||
/** @hide */
|
||||
@VisibleForTesting
|
||||
public static final int TRANSFER_TIMEOUT_MS = 30_000;
|
||||
@@ -480,7 +486,6 @@ public final class MediaRouter2Manager {
|
||||
notifyTransferFailed(matchingRequest.mOldSessionInfo, requestedRoute);
|
||||
return;
|
||||
}
|
||||
releaseSession(matchingRequest.mOldSessionInfo);
|
||||
notifyTransferred(matchingRequest.mOldSessionInfo, sessionInfo);
|
||||
}
|
||||
|
||||
@@ -777,7 +782,7 @@ public final class MediaRouter2Manager {
|
||||
if (client != null) {
|
||||
try {
|
||||
mMediaRouterService.requestCreateSessionWithManager(
|
||||
client, requestId, oldSession.getClientPackageName(), route);
|
||||
client, requestId, oldSession, route);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "requestCreateSession: Failed to send a request", ex);
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ public final class RoutingSessionInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about how volume is handled on the session.
|
||||
* Gets the information about how volume is handled on the session.
|
||||
*
|
||||
* @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
|
||||
* {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
|
||||
|
||||
@@ -126,6 +126,7 @@ public class MediaRouter2ManagerTest {
|
||||
StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
|
||||
if (instance != null) {
|
||||
instance.setProxy(null);
|
||||
instance.setSpy(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,6 +424,79 @@ public class MediaRouter2ManagerTest {
|
||||
route -> TextUtils.equals(route.getClientPackageName(), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@LargeTest
|
||||
public void testTransferTwice() throws Exception {
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
|
||||
addRouterCallback(new RouteCallback() { });
|
||||
|
||||
CountDownLatch successLatch1 = new CountDownLatch(1);
|
||||
CountDownLatch successLatch2 = new CountDownLatch(1);
|
||||
CountDownLatch failureLatch = new CountDownLatch(1);
|
||||
CountDownLatch managerOnSessionReleasedLatch = new CountDownLatch(1);
|
||||
CountDownLatch serviceOnReleaseSessionLatch = new CountDownLatch(1);
|
||||
List<RoutingSessionInfo> sessions = new ArrayList<>();
|
||||
|
||||
StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
|
||||
assertNotNull(instance);
|
||||
instance.setSpy(new StubMediaRoute2ProviderService.Spy() {
|
||||
@Override
|
||||
public void onReleaseSession(long requestId, String sessionId) {
|
||||
serviceOnReleaseSessionLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onTransferred(RoutingSessionInfo oldSession,
|
||||
RoutingSessionInfo newSession) {
|
||||
sessions.add(newSession);
|
||||
if (successLatch1.getCount() > 0) {
|
||||
successLatch1.countDown();
|
||||
} else {
|
||||
successLatch2.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
|
||||
failureLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionReleased(RoutingSessionInfo session) {
|
||||
managerOnSessionReleasedLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
MediaRoute2Info route1 = routes.get(ROUTE_ID1);
|
||||
MediaRoute2Info route2 = routes.get(ROUTE_ID2);
|
||||
assertNotNull(route1);
|
||||
assertNotNull(route2);
|
||||
|
||||
mManager.selectRoute(mPackageName, route1);
|
||||
assertTrue(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
mManager.selectRoute(mPackageName, route2);
|
||||
assertTrue(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
|
||||
// onTransferFailed/onSessionReleased should not be called.
|
||||
assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
|
||||
assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
|
||||
|
||||
assertEquals(2, sessions.size());
|
||||
List<String> activeSessionIds = mManager.getActiveSessions().stream()
|
||||
.map(RoutingSessionInfo::getId)
|
||||
.collect(Collectors.toList());
|
||||
// The old session shouldn't appear on the active session list.
|
||||
assertFalse(activeSessionIds.contains(sessions.get(0).getId()));
|
||||
assertTrue(activeSessionIds.contains(sessions.get(1).getId()));
|
||||
|
||||
assertFalse(serviceOnReleaseSessionLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
|
||||
mManager.releaseSession(sessions.get(0));
|
||||
assertTrue(serviceOnReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@LargeTest
|
||||
public void testTransfer_ignored_fails() throws Exception {
|
||||
|
||||
@@ -79,6 +79,7 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
|
||||
@GuardedBy("sLock")
|
||||
private static StubMediaRoute2ProviderService sInstance;
|
||||
private Proxy mProxy;
|
||||
private Spy mSpy;
|
||||
|
||||
private void initializeRoutes() {
|
||||
MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
|
||||
@@ -256,6 +257,11 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
|
||||
|
||||
@Override
|
||||
public void onReleaseSession(long requestId, String sessionId) {
|
||||
Spy spy = mSpy;
|
||||
if (spy != null) {
|
||||
spy.onReleaseSession(requestId, sessionId);
|
||||
}
|
||||
|
||||
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
|
||||
if (sessionInfo == null) {
|
||||
return;
|
||||
@@ -375,7 +381,21 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
|
||||
mProxy = proxy;
|
||||
}
|
||||
|
||||
public void setSpy(@Nullable Spy spy) {
|
||||
mSpy = spy;
|
||||
}
|
||||
|
||||
/**
|
||||
* It overrides the original service
|
||||
*/
|
||||
public static class Proxy {
|
||||
public void onSetRouteVolume(String routeId, int volume, long requestId) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* It gets notified but doesn't prevent the original methods to be called.
|
||||
*/
|
||||
public static class Spy {
|
||||
public void onReleaseSession(long requestId, String sessionId) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ abstract class MediaRoute2Provider {
|
||||
|
||||
public abstract void setRouteVolume(long requestId, String routeId, int volume);
|
||||
public abstract void setSessionVolume(long requestId, String sessionId, int volume);
|
||||
public abstract void prepareReleaseSession(@NonNull String sessionId);
|
||||
|
||||
@NonNull
|
||||
public String getUniqueId() {
|
||||
|
||||
@@ -34,11 +34,16 @@ import android.os.IBinder.DeathRecipient;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -61,6 +66,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
|
||||
|
||||
private RouteDiscoveryPreference mLastDiscoveryPreference = null;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
|
||||
|
||||
MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName,
|
||||
int userId) {
|
||||
super(componentName);
|
||||
@@ -141,6 +149,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareReleaseSession(@NonNull String sessionId) {
|
||||
synchronized (mLock) {
|
||||
for (RoutingSessionInfo session : mSessionInfos) {
|
||||
if (TextUtils.equals(session.getId(), sessionId)) {
|
||||
mSessionInfos.remove(session);
|
||||
mReleasingSessions.add(session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasComponentName(String packageName, String className) {
|
||||
return mComponentName.getPackageName().equals(packageName)
|
||||
&& mComponentName.getClassName().equals(className);
|
||||
@@ -300,88 +321,97 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
|
||||
}
|
||||
|
||||
private void onSessionCreated(Connection connection, long requestId,
|
||||
RoutingSessionInfo sessionInfo) {
|
||||
RoutingSessionInfo newSession) {
|
||||
if (mActiveConnection != connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionInfo == null) {
|
||||
Slog.w(TAG, "onSessionCreated: Ignoring null sessionInfo sent from " + mComponentName);
|
||||
if (newSession == null) {
|
||||
Slog.w(TAG, "onSessionCreated: Ignoring null session sent from " + mComponentName);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionInfo = updateSessionInfo(sessionInfo);
|
||||
newSession = assignProviderIdForSession(newSession);
|
||||
String newSessionId = newSession.getId();
|
||||
|
||||
boolean duplicateSessionAlreadyExists = false;
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mSessionInfos.size(); i++) {
|
||||
if (mSessionInfos.get(i).getId().equals(sessionInfo.getId())) {
|
||||
duplicateSessionAlreadyExists = true;
|
||||
break;
|
||||
}
|
||||
if (mSessionInfos.stream()
|
||||
.anyMatch(session -> TextUtils.equals(session.getId(), newSessionId))
|
||||
|| mReleasingSessions.stream()
|
||||
.anyMatch(session -> TextUtils.equals(session.getId(), newSessionId))) {
|
||||
Slog.w(TAG, "onSessionCreated: Duplicate session already exists. Ignoring.");
|
||||
return;
|
||||
}
|
||||
mSessionInfos.add(sessionInfo);
|
||||
mSessionInfos.add(newSession);
|
||||
}
|
||||
|
||||
if (duplicateSessionAlreadyExists) {
|
||||
Slog.w(TAG, "onSessionCreated: Duplicate session already exists. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback.onSessionCreated(this, requestId, sessionInfo);
|
||||
mCallback.onSessionCreated(this, requestId, newSession);
|
||||
}
|
||||
|
||||
private void onSessionUpdated(Connection connection, RoutingSessionInfo sessionInfo) {
|
||||
private void onSessionUpdated(Connection connection, RoutingSessionInfo updatedSession) {
|
||||
if (mActiveConnection != connection) {
|
||||
return;
|
||||
}
|
||||
if (sessionInfo == null) {
|
||||
Slog.w(TAG, "onSessionUpdated: Ignoring null sessionInfo sent from "
|
||||
if (updatedSession == null) {
|
||||
Slog.w(TAG, "onSessionUpdated: Ignoring null session sent from "
|
||||
+ mComponentName);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionInfo = updateSessionInfo(sessionInfo);
|
||||
updatedSession = assignProviderIdForSession(updatedSession);
|
||||
|
||||
boolean found = false;
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mSessionInfos.size(); i++) {
|
||||
if (mSessionInfos.get(i).getId().equals(sessionInfo.getId())) {
|
||||
mSessionInfos.set(i, sessionInfo);
|
||||
if (mSessionInfos.get(i).getId().equals(updatedSession.getId())) {
|
||||
mSessionInfos.set(i, updatedSession);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
for (RoutingSessionInfo releasingSession : mReleasingSessions) {
|
||||
if (TextUtils.equals(releasingSession.getId(), updatedSession.getId())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Slog.w(TAG, "onSessionUpdated: Matching session info not found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Slog.w(TAG, "onSessionUpdated: Matching session info not found");
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback.onSessionUpdated(this, sessionInfo);
|
||||
mCallback.onSessionUpdated(this, updatedSession);
|
||||
}
|
||||
|
||||
private void onSessionReleased(Connection connection, RoutingSessionInfo sessionInfo) {
|
||||
private void onSessionReleased(Connection connection, RoutingSessionInfo releaedSession) {
|
||||
if (mActiveConnection != connection) {
|
||||
return;
|
||||
}
|
||||
if (sessionInfo == null) {
|
||||
Slog.w(TAG, "onSessionReleased: Ignoring null sessionInfo sent from " + mComponentName);
|
||||
if (releaedSession == null) {
|
||||
Slog.w(TAG, "onSessionReleased: Ignoring null session sent from " + mComponentName);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionInfo = updateSessionInfo(sessionInfo);
|
||||
releaedSession = assignProviderIdForSession(releaedSession);
|
||||
|
||||
boolean found = false;
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mSessionInfos.size(); i++) {
|
||||
if (mSessionInfos.get(i).getId().equals(sessionInfo.getId())) {
|
||||
mSessionInfos.remove(i);
|
||||
for (RoutingSessionInfo session : mSessionInfos) {
|
||||
if (TextUtils.equals(session.getId(), releaedSession.getId())) {
|
||||
mSessionInfos.remove(session);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
for (RoutingSessionInfo session : mReleasingSessions) {
|
||||
if (TextUtils.equals(session.getId(), releaedSession.getId())) {
|
||||
mReleasingSessions.remove(session);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@@ -389,10 +419,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback.onSessionReleased(this, sessionInfo);
|
||||
mCallback.onSessionReleased(this, releaedSession);
|
||||
}
|
||||
|
||||
private RoutingSessionInfo updateSessionInfo(RoutingSessionInfo sessionInfo) {
|
||||
private RoutingSessionInfo assignProviderIdForSession(RoutingSessionInfo sessionInfo) {
|
||||
return new RoutingSessionInfo.Builder(sessionInfo)
|
||||
.setOwnerPackageName(mComponentName.getPackageName())
|
||||
.setProviderId(getUniqueId())
|
||||
@@ -423,6 +453,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
|
||||
mCallback.onSessionReleased(this, sessionInfo);
|
||||
}
|
||||
mSessionInfos.clear();
|
||||
mReleasingSessions.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
package com.android.server.media;
|
||||
|
||||
import static android.media.MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE;
|
||||
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
|
||||
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
|
||||
import static android.media.MediaRouter2Utils.getOriginalId;
|
||||
import static android.media.MediaRouter2Utils.getProviderId;
|
||||
|
||||
@@ -33,6 +31,8 @@ import android.media.IMediaRouter2;
|
||||
import android.media.IMediaRouter2Manager;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.MediaRoute2ProviderService;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RouteDiscoveryPreference;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.os.Binder;
|
||||
@@ -235,30 +235,17 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
|
||||
long managerRequestId, RoutingSessionInfo oldSession,
|
||||
MediaRoute2Info route, Bundle sessionHints) {
|
||||
Objects.requireNonNull(router, "router must not be null");
|
||||
Objects.requireNonNull(oldSession, "oldSession must not be null");
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
requestCreateSessionWithRouter2Locked(requestId, router, route, sessionHints);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifySessionHintsForCreatingSession(IMediaRouter2 router,
|
||||
long uniqueRequestId, MediaRoute2Info route, Bundle sessionHints) {
|
||||
Objects.requireNonNull(router, "router must not be null");
|
||||
Objects.requireNonNull(route, "route must not be null");
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
notifySessionHintsForCreatingSessionLocked(uniqueRequestId,
|
||||
router, route, sessionHints);
|
||||
requestCreateSessionWithRouter2Locked(requestId, managerRequestId,
|
||||
router, oldSession, route, sessionHints);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
@@ -417,16 +404,14 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
public void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
|
||||
String packageName, MediaRoute2Info route) {
|
||||
RoutingSessionInfo oldSession, MediaRoute2Info route) {
|
||||
Objects.requireNonNull(manager, "manager must not be null");
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
throw new IllegalArgumentException("packageName must not be empty");
|
||||
}
|
||||
Objects.requireNonNull(oldSession, "oldSession must not be null");
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
requestCreateSessionWithManagerLocked(requestId, manager, packageName, route);
|
||||
requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
@@ -638,7 +623,8 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestCreateSessionWithRouter2Locked(int requestId, @NonNull IMediaRouter2 router,
|
||||
private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId,
|
||||
@NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession,
|
||||
@NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
|
||||
final IBinder binder = router.asBinder();
|
||||
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
|
||||
@@ -647,41 +633,60 @@ class MediaRouter2ServiceImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
|
||||
&& !TextUtils.equals(route.getId(),
|
||||
routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId())) {
|
||||
Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
|
||||
+ route);
|
||||
routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
|
||||
routerRecord, requestId);
|
||||
return;
|
||||
if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
|
||||
ManagerRecord manager = routerRecord.mUserRecord.mHandler.findManagerWithId(
|
||||
toRequesterId(managerRequestId));
|
||||
if (manager == null || manager.mLastSessionCreationRequest == null) {
|
||||
Slog.w(TAG, "requestCreateSessionWithRouter2Locked: "
|
||||
+ "Ignoring unknown request.");
|
||||
routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
|
||||
routerRecord, requestId);
|
||||
return;
|
||||
}
|
||||
if (!TextUtils.equals(manager.mLastSessionCreationRequest.mOldSession.getId(),
|
||||
oldSession.getId())) {
|
||||
Slog.w(TAG, "requestCreateSessionWithRouter2Locked: "
|
||||
+ "Ignoring unmatched routing session.");
|
||||
routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
|
||||
routerRecord, requestId);
|
||||
return;
|
||||
}
|
||||
if (!TextUtils.equals(manager.mLastSessionCreationRequest.mRoute.getId(),
|
||||
route.getId())) {
|
||||
// When media router has no permission
|
||||
if (!routerRecord.mHasModifyAudioRoutingPermission
|
||||
&& manager.mLastSessionCreationRequest.mRoute.isSystemRoute()
|
||||
&& route.isSystemRoute()) {
|
||||
route = manager.mLastSessionCreationRequest.mRoute;
|
||||
} else {
|
||||
Slog.w(TAG, "requestCreateSessionWithRouter2Locked: "
|
||||
+ "Ignoring unmatched route.");
|
||||
routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
|
||||
routerRecord, requestId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
manager.mLastSessionCreationRequest = null;
|
||||
} else {
|
||||
if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
|
||||
&& !TextUtils.equals(route.getId(),
|
||||
routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId())) {
|
||||
Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
|
||||
+ route);
|
||||
routerRecord.mUserRecord.mHandler.notifySessionCreationFailedToRouter(
|
||||
routerRecord, requestId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId);
|
||||
routerRecord.mUserRecord.mHandler.sendMessage(
|
||||
obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler,
|
||||
routerRecord.mUserRecord.mHandler,
|
||||
uniqueRequestId, routerRecord, route,
|
||||
uniqueRequestId, managerRequestId, routerRecord, oldSession, route,
|
||||
sessionHints));
|
||||
}
|
||||
|
||||
private void notifySessionHintsForCreatingSessionLocked(long uniqueRequestId,
|
||||
@NonNull IMediaRouter2 router,
|
||||
@NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
|
||||
final IBinder binder = router.asBinder();
|
||||
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
|
||||
|
||||
if (routerRecord == null) {
|
||||
Slog.w(TAG, "notifySessionHintsForCreatingSessionLocked: Ignoring unknown router.");
|
||||
return;
|
||||
}
|
||||
|
||||
routerRecord.mUserRecord.mHandler.sendMessage(
|
||||
obtainMessage(UserHandler::requestCreateSessionWithManagerOnHandler,
|
||||
routerRecord.mUserRecord.mHandler,
|
||||
uniqueRequestId, routerRecord, route, sessionHints));
|
||||
}
|
||||
|
||||
private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
|
||||
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
|
||||
final IBinder binder = router.asBinder();
|
||||
@@ -853,27 +858,46 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
private void requestCreateSessionWithManagerLocked(int requestId,
|
||||
@NonNull IMediaRouter2Manager manager,
|
||||
@NonNull String packageName, @NonNull MediaRoute2Info route) {
|
||||
@NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
|
||||
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
|
||||
if (managerRecord == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String packageName = oldSession.getClientPackageName();
|
||||
|
||||
RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName);
|
||||
if (routerRecord == null) {
|
||||
Slog.w(TAG, "requestCreateSessionWithManagerLocked: Ignoring session creation for "
|
||||
+ "unknown router.");
|
||||
try {
|
||||
managerRecord.mManager.notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "requestCreateSessionWithManagerLocked: Failed to notify failure. "
|
||||
+ "Manager probably died.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
|
||||
if (managerRecord.mLastSessionCreationRequest != null) {
|
||||
managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager(
|
||||
managerRecord.mManager,
|
||||
toOriginalRequestId(managerRecord.mLastSessionCreationRequest
|
||||
.mManagerRequestId),
|
||||
REASON_UNKNOWN_ERROR);
|
||||
managerRecord.mLastSessionCreationRequest = null;
|
||||
}
|
||||
managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord,
|
||||
MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId,
|
||||
oldSession, route);
|
||||
|
||||
// Before requesting to the provider, get session hints from the media router.
|
||||
// As a return, media router will request to create a session.
|
||||
routerRecord.mUserRecord.mHandler.sendMessage(
|
||||
obtainMessage(UserHandler::getSessionHintsForCreatingSessionOnHandler,
|
||||
obtainMessage(UserHandler::requestRouterCreateSessionOnHandler,
|
||||
routerRecord.mUserRecord.mHandler,
|
||||
uniqueRequestId, routerRecord, managerRecord, route));
|
||||
uniqueRequestId, routerRecord, managerRecord, oldSession, route));
|
||||
}
|
||||
|
||||
private void selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager,
|
||||
@@ -887,7 +911,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
// Can be null if the session is system's or RCN.
|
||||
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
|
||||
.findRouterforSessionLocked(uniqueSessionId);
|
||||
.findRouterWithSessionLocked(uniqueSessionId);
|
||||
|
||||
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
|
||||
managerRecord.mUserRecord.mHandler.sendMessage(
|
||||
@@ -908,7 +932,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
// Can be null if the session is system's or RCN.
|
||||
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
|
||||
.findRouterforSessionLocked(uniqueSessionId);
|
||||
.findRouterWithSessionLocked(uniqueSessionId);
|
||||
|
||||
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
|
||||
managerRecord.mUserRecord.mHandler.sendMessage(
|
||||
@@ -929,7 +953,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
// Can be null if the session is system's or RCN.
|
||||
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
|
||||
.findRouterforSessionLocked(uniqueSessionId);
|
||||
.findRouterWithSessionLocked(uniqueSessionId);
|
||||
|
||||
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
|
||||
managerRecord.mUserRecord.mHandler.sendMessage(
|
||||
@@ -966,7 +990,7 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
|
||||
.findRouterforSessionLocked(uniqueSessionId);
|
||||
.findRouterWithSessionLocked(uniqueSessionId);
|
||||
|
||||
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
|
||||
managerRecord.mUserRecord.mHandler.sendMessage(
|
||||
@@ -1097,6 +1121,7 @@ class MediaRouter2ServiceImpl {
|
||||
public final int mPid;
|
||||
public final String mPackageName;
|
||||
public final int mManagerId;
|
||||
public SessionCreationRequest mLastSessionCreationRequest;
|
||||
|
||||
ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager,
|
||||
int uid, int pid, String packageName) {
|
||||
@@ -1222,10 +1247,20 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RouterRecord findRouterforSessionLocked(@NonNull String uniqueSessionId) {
|
||||
public RouterRecord findRouterWithSessionLocked(@NonNull String uniqueSessionId) {
|
||||
return mSessionToRouterMap.get(uniqueSessionId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ManagerRecord findManagerWithId(int managerId) {
|
||||
for (ManagerRecord manager : getManagerRecords()) {
|
||||
if (manager.mManagerId == managerId) {
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) {
|
||||
int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId());
|
||||
MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo();
|
||||
@@ -1318,26 +1353,28 @@ class MediaRouter2ServiceImpl {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void getSessionHintsForCreatingSessionOnHandler(long uniqueRequestId,
|
||||
private void requestRouterCreateSessionOnHandler(long uniqueRequestId,
|
||||
@NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord,
|
||||
@NonNull MediaRoute2Info route) {
|
||||
SessionCreationRequest request =
|
||||
new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord);
|
||||
mSessionCreationRequests.add(request);
|
||||
|
||||
@NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
|
||||
try {
|
||||
routerRecord.mRouter.getSessionHintsForCreatingSession(uniqueRequestId, route);
|
||||
if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission) {
|
||||
routerRecord.mRouter.requestCreateSessionByManager(uniqueRequestId,
|
||||
oldSession, mSystemProvider.getDefaultRoute());
|
||||
} else {
|
||||
routerRecord.mRouter.requestCreateSessionByManager(uniqueRequestId,
|
||||
oldSession, route);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
|
||||
+ "Failed to request. Router probably died.", ex);
|
||||
mSessionCreationRequests.remove(request);
|
||||
notifyRequestFailedToManager(managerRecord.mManager,
|
||||
toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId,
|
||||
@NonNull RouterRecord routerRecord,
|
||||
long managerRequestId, @NonNull RouterRecord routerRecord,
|
||||
@NonNull RoutingSessionInfo oldSession,
|
||||
@NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
|
||||
|
||||
final MediaRoute2Provider provider = findProvider(route.getProviderId());
|
||||
@@ -1350,49 +1387,14 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
SessionCreationRequest request =
|
||||
new SessionCreationRequest(routerRecord, uniqueRequestId, route, null);
|
||||
new SessionCreationRequest(routerRecord, uniqueRequestId,
|
||||
managerRequestId, oldSession, route);
|
||||
mSessionCreationRequests.add(request);
|
||||
|
||||
provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
|
||||
route.getOriginalId(), sessionHints);
|
||||
}
|
||||
|
||||
private void requestCreateSessionWithManagerOnHandler(long uniqueRequestId,
|
||||
@NonNull RouterRecord routerRecord,
|
||||
@NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
|
||||
SessionCreationRequest matchingRequest = null;
|
||||
for (SessionCreationRequest request : mSessionCreationRequests) {
|
||||
if (request.mUniqueRequestId == uniqueRequestId) {
|
||||
matchingRequest = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchingRequest == null) {
|
||||
Slog.w(TAG, "requestCreateSessionWithManagerOnHandler: "
|
||||
+ "Ignoring an unknown session creation request.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TextUtils.equals(matchingRequest.mRoute.getId(), route.getId())) {
|
||||
Slog.w(TAG, "requestCreateSessionWithManagerOnHandler: "
|
||||
+ "The given route is different from the requested route.");
|
||||
return;
|
||||
}
|
||||
|
||||
final MediaRoute2Provider provider = findProvider(route.getProviderId());
|
||||
if (provider == null) {
|
||||
Slog.w(TAG, "requestCreateSessionWithManagerOnHandler: Ignoring session "
|
||||
+ "creation request since no provider found for given route=" + route);
|
||||
mSessionCreationRequests.remove(matchingRequest);
|
||||
notifyRequestFailedToManager(matchingRequest.mRequestedManagerRecord.mManager,
|
||||
toOriginalRequestId(uniqueRequestId), REASON_ROUTE_NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
|
||||
route.getOriginalId(), sessionHints);
|
||||
}
|
||||
|
||||
// routerRecord can be null if the session is system's or RCN.
|
||||
private void selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord,
|
||||
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
|
||||
@@ -1539,25 +1541,23 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
|
||||
long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo) {
|
||||
notifySessionCreatedToManagers(getManagers(),
|
||||
toOriginalRequestId(uniqueRequestId), sessionInfo);
|
||||
|
||||
if (uniqueRequestId == REQUEST_ID_NONE) {
|
||||
// The session is created without any matching request.
|
||||
return;
|
||||
}
|
||||
|
||||
SessionCreationRequest matchingRequest = null;
|
||||
|
||||
for (SessionCreationRequest request : mSessionCreationRequests) {
|
||||
if (request.mUniqueRequestId == uniqueRequestId
|
||||
&& TextUtils.equals(
|
||||
request.mRoute.getProviderId(), provider.getUniqueId())) {
|
||||
request.mRoute.getProviderId(), provider.getUniqueId())) {
|
||||
matchingRequest = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
long managerRequestId = (matchingRequest == null)
|
||||
? MediaRoute2ProviderService.REQUEST_ID_NONE
|
||||
: matchingRequest.mManagerRequestId;
|
||||
// Managers should know created session even if it's not requested.
|
||||
notifySessionCreatedToManagers(managerRequestId, sessionInfo);
|
||||
|
||||
if (matchingRequest == null) {
|
||||
Slog.w(TAG, "Ignoring session creation result for unknown request. "
|
||||
+ "uniqueRequestId=" + uniqueRequestId + ", sessionInfo=" + sessionInfo);
|
||||
@@ -1565,12 +1565,14 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
mSessionCreationRequests.remove(matchingRequest);
|
||||
|
||||
if (sessionInfo == null) {
|
||||
// Failed
|
||||
notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord,
|
||||
toOriginalRequestId(uniqueRequestId));
|
||||
return;
|
||||
// Not to show old session
|
||||
MediaRoute2Provider oldProvider =
|
||||
findProvider(matchingRequest.mOldSession.getProviderId());
|
||||
if (oldProvider != null) {
|
||||
oldProvider.prepareReleaseSession(matchingRequest.mOldSession.getId());
|
||||
} else {
|
||||
Slog.w(TAG, "onSessionCreatedOnHandler: Can't find provider for an old session. "
|
||||
+ "session=" + matchingRequest.mOldSession);
|
||||
}
|
||||
|
||||
String originalRouteId = matchingRequest.mRoute.getId();
|
||||
@@ -1645,23 +1647,17 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
final int requesterId = toRequesterId(uniqueRequestId);
|
||||
for (ManagerRecord manager : getManagerRecords()) {
|
||||
if (manager.mManagerId == requesterId) {
|
||||
notifyRequestFailedToManager(
|
||||
manager.mManager, toOriginalRequestId(uniqueRequestId), reason);
|
||||
return;
|
||||
}
|
||||
ManagerRecord manager = findManagerWithId(requesterId);
|
||||
if (manager != null) {
|
||||
notifyRequestFailedToManager(
|
||||
manager.mManager, toOriginalRequestId(uniqueRequestId), reason);
|
||||
return;
|
||||
}
|
||||
|
||||
// Currently, only the manager can get notified of failures.
|
||||
// TODO: Notify router too when the related callback is introduced.
|
||||
}
|
||||
|
||||
// TODO(b/157873556): Find a way to prevent providers from notifying error on random reqID.
|
||||
// Possible solutions can be:
|
||||
// 1) Record the other type of requests too (not only session creation request)
|
||||
// 2) Throw exception on providers when they try to notify error on
|
||||
// random uniqueRequestId.
|
||||
private boolean handleSessionCreationRequestFailed(@NonNull MediaRoute2Provider provider,
|
||||
long uniqueRequestId, int reason) {
|
||||
// Check whether the failure is about creating a session
|
||||
@@ -1683,12 +1679,16 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
// Notify the requester about the failure.
|
||||
// The call should be made by either MediaRouter2 or MediaRouter2Manager.
|
||||
if (matchingRequest.mRequestedManagerRecord == null) {
|
||||
if (matchingRequest.mManagerRequestId == MediaRouter2Manager.REQUEST_ID_NONE) {
|
||||
notifySessionCreationFailedToRouter(
|
||||
matchingRequest.mRouterRecord, toOriginalRequestId(uniqueRequestId));
|
||||
} else {
|
||||
notifyRequestFailedToManager(matchingRequest.mRequestedManagerRecord.mManager,
|
||||
toOriginalRequestId(uniqueRequestId), reason);
|
||||
final int requesterId = toRequesterId(matchingRequest.mManagerRequestId);
|
||||
ManagerRecord manager = findManagerWithId(requesterId);
|
||||
if (manager != null) {
|
||||
notifyRequestFailedToManager(manager.mManager,
|
||||
toOriginalRequestId(matchingRequest.mManagerRequestId), reason);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1921,14 +1921,19 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySessionCreatedToManagers(@NonNull List<IMediaRouter2Manager> managers,
|
||||
int requestId, @NonNull RoutingSessionInfo sessionInfo) {
|
||||
for (IMediaRouter2Manager manager : managers) {
|
||||
private void notifySessionCreatedToManagers(long managerRequestId,
|
||||
@NonNull RoutingSessionInfo session) {
|
||||
int requesterId = toRequesterId(managerRequestId);
|
||||
int originalRequestId = toOriginalRequestId(managerRequestId);
|
||||
|
||||
for (ManagerRecord manager : getManagerRecords()) {
|
||||
try {
|
||||
manager.notifySessionCreated(requestId, sessionInfo);
|
||||
manager.mManager.notifySessionCreated(
|
||||
((manager.mManagerId == requesterId) ? originalRequestId :
|
||||
MediaRouter2Manager.REQUEST_ID_NONE), session);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "notifySessionCreatedToManagers: "
|
||||
+ "failed to notify. Manager probably died.", ex);
|
||||
+ "Failed to notify. Manager probably died.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2014,7 +2019,7 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
mUserRecord.mCompositeDiscoveryPreference =
|
||||
new RouteDiscoveryPreference.Builder(discoveryPreferences)
|
||||
.build();
|
||||
.build();
|
||||
}
|
||||
for (MediaRoute2Provider provider : mRouteProviders) {
|
||||
provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
|
||||
@@ -2030,21 +2035,22 @@ class MediaRouter2ServiceImpl {
|
||||
return null;
|
||||
}
|
||||
|
||||
final class SessionCreationRequest {
|
||||
public final RouterRecord mRouterRecord;
|
||||
public final long mUniqueRequestId;
|
||||
public final MediaRoute2Info mRoute;
|
||||
public final ManagerRecord mRequestedManagerRecord;
|
||||
}
|
||||
static final class SessionCreationRequest {
|
||||
public final RouterRecord mRouterRecord;
|
||||
public final long mUniqueRequestId;
|
||||
public final long mManagerRequestId;
|
||||
public final RoutingSessionInfo mOldSession;
|
||||
public final MediaRoute2Info mRoute;
|
||||
|
||||
// requestedManagerRecord is not null only when the request is made by manager.
|
||||
SessionCreationRequest(@NonNull RouterRecord routerRecord, long uniqueRequestId,
|
||||
@NonNull MediaRoute2Info route,
|
||||
@Nullable ManagerRecord requestedManagerRecord) {
|
||||
mRouterRecord = routerRecord;
|
||||
mUniqueRequestId = uniqueRequestId;
|
||||
mRoute = route;
|
||||
mRequestedManagerRecord = requestedManagerRecord;
|
||||
}
|
||||
SessionCreationRequest(@NonNull RouterRecord routerRecord, long uniqueRequestId,
|
||||
long managerRequestId, @NonNull RoutingSessionInfo oldSession,
|
||||
@NonNull MediaRoute2Info route) {
|
||||
mRouterRecord = routerRecord;
|
||||
mUniqueRequestId = uniqueRequestId;
|
||||
mManagerRequestId = managerRequestId;
|
||||
mOldSession = oldSession;
|
||||
mRoute = route;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,16 +481,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
// Binder call
|
||||
@Override
|
||||
public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
|
||||
long managerRequestId, RoutingSessionInfo oldSession,
|
||||
MediaRoute2Info route, Bundle sessionHints) {
|
||||
mService2.requestCreateSessionWithRouter2(router, requestId, route, sessionHints);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void notifySessionHintsForCreatingSession(IMediaRouter2 router,
|
||||
long uniqueRequestId, MediaRoute2Info route, Bundle sessionHints) {
|
||||
mService2.notifySessionHintsForCreatingSession(router,
|
||||
uniqueRequestId, route, sessionHints);
|
||||
mService2.requestCreateSessionWithRouter2(router, requestId, managerRequestId,
|
||||
oldSession, route, sessionHints);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@@ -558,8 +552,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
// Binder call
|
||||
@Override
|
||||
public void requestCreateSessionWithManager(IMediaRouter2Manager manager,
|
||||
int requestId, String packageName, MediaRoute2Info route) {
|
||||
mService2.requestCreateSessionWithManager(manager, requestId, packageName, route);
|
||||
int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
|
||||
mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
|
||||
@@ -222,6 +222,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
// Do nothing since we don't support grouping volume yet.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareReleaseSession(String sessionId) {
|
||||
// Do nothing since the system session persists.
|
||||
}
|
||||
|
||||
public MediaRoute2Info getDefaultRoute() {
|
||||
return mDefaultRoute;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user