Loading media/java/android/media/IMediaRoute2ProviderClient.aidl +6 −4 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ import android.os.Bundle; * @hide */ oneway interface IMediaRoute2ProviderClient { void updateState(in MediaRoute2ProviderInfo providerInfo, in List<RoutingSessionInfo> sessionInfos); void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, long requestId); void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo); // TODO: Change it to updateRoutes? void updateState(in MediaRoute2ProviderInfo providerInfo); void notifySessionCreated(in RoutingSessionInfo sessionInfo, long requestId); void notifySessionCreationFailed(long requestId); void notifySessionUpdated(in RoutingSessionInfo sessionInfo); void notifySessionReleased(in RoutingSessionInfo sessionInfo); } media/java/android/media/MediaRoute2ProviderService.java +114 −86 Original line number Diff line number Diff line Loading @@ -58,6 +58,16 @@ public abstract class MediaRoute2ProviderService extends Service { public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; /** * The request ID to pass {@link #notifySessionCreated(RoutingSessionInfo, long)} * when {@link MediaRoute2ProviderService} created a session although there was no creation * request. * * @see #notifySessionCreated(RoutingSessionInfo, long) * @hide */ public static final long REQUEST_ID_UNKNOWN = 0; private final Handler mHandler; private final Object mSessionLock = new Object(); private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false); Loading Loading @@ -118,7 +128,7 @@ public abstract class MediaRoute2ProviderService extends Service { * * @param sessionId id of the session * @return information of the session with the given id. * null if the session is destroyed or id is not valid. * null if the session is released or ID is not valid. * @hide */ @Nullable Loading @@ -143,161 +153,178 @@ public abstract class MediaRoute2ProviderService extends Service { } /** * Updates the information of a session. * If the session is destroyed or not created before, it will be ignored. * Call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify clients of * session info changes. * Notifies clients of that the session is created and ready for use. * <p> * If this session is created without any creation request, use {@link #REQUEST_ID_UNKNOWN} * as the request ID. * * @param sessionInfo new session information * @see #notifySessionCreated(RoutingSessionInfo, long) * @param sessionInfo information of the new session. * The {@link RoutingSessionInfo#getId() id} of the session must be unique. * @param requestId id of the previous request to create this session provided in * {@link #onCreateSession(String, String, String, long)} * @see #onCreateSession(String, String, String, long) * @hide */ public final void updateSessionInfo(@NonNull RoutingSessionInfo sessionInfo) { public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo, long requestId) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); Log.w(TAG, "Ignoring duplicate session id."); return; } mSessionInfo.put(sessionInfo.getId(), sessionInfo); } schedulePublishState(); } else { Log.w(TAG, "Ignoring unknown session info."); if (mClient == null) { return; } try { // TODO: Calling binder calls in multiple thread may cause timing issue. // Consider to change implementations to avoid the problems. // For example, post binder calls, always send all sessions at once, etc. mClient.notifySessionCreated(sessionInfo, requestId); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session created."); } } /** * Notifies the session is changed. * Notifies clients of that the session could not be created. * * TODO: This method is temporary, only created for tests. Remove when the alternative is ready. * @param requestId id of the previous request to create the session provided in * {@link #onCreateSession(String, String, String, long)}. * @see #onCreateSession(String, String, String, long) * @hide */ public final void notifySessionInfoChanged(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); } else { Log.w(TAG, "Ignoring unknown session info."); return; } } public final void notifySessionCreationFailed(long requestId) { if (mClient == null) { return; } try { mClient.notifySessionInfoChanged(sessionInfo); mClient.notifySessionCreationFailed(requestId); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session info changed."); Log.w(TAG, "Failed to notify session creation failed."); } } /** * Notifies clients of that the session is created and ready for use. If the session can be * controlled, pass a {@link Bundle} that contains how to control it. * Notifies the existing session is updated. For example, when * {@link RoutingSessionInfo#getSelectedRoutes() selected routes} are changed. * * @param sessionInfo information of the new session. * The {@link RoutingSessionInfo#getId() id} of the session must be * unique. Pass {@code null} to reject the request or inform clients that * session creation is failed. * @param requestId id of the previous request to create this session * @hide */ // TODO: fail reason? // TODO: Maybe better to create notifySessionCreationFailed? public final void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, long requestId) { if (sessionInfo != null) { public final void notifySessionUpdated(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { Log.w(TAG, "Ignoring duplicate session id."); mSessionInfo.put(sessionId, sessionInfo); } else { Log.w(TAG, "Ignoring unknown session info."); return; } mSessionInfo.put(sessionInfo.getId(), sessionInfo); } schedulePublishState(); } if (mClient == null) { return; } try { mClient.notifySessionCreated(sessionInfo, requestId); mClient.notifySessionUpdated(sessionInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session created."); Log.w(TAG, "Failed to notify session info changed."); } } /** * Releases a session with the given id. * {@link #onDestroySession} is called if the session is released. * Notifies that the session is released. * * @param sessionId id of the session to be released * @see #onDestroySession(String, RoutingSessionInfo) * @param sessionId id of the released session. * @see #onReleaseSession(String) * @hide */ public final void releaseSession(@NonNull String sessionId) { public final void notifySessionReleased(@NonNull String sessionId) { if (TextUtils.isEmpty(sessionId)) { throw new IllegalArgumentException("sessionId must not be empty"); } //TODO: notify media router service of release. RoutingSessionInfo sessionInfo; synchronized (mSessionLock) { sessionInfo = mSessionInfo.remove(sessionId); } if (sessionInfo != null) { mHandler.sendMessage(obtainMessage( MediaRoute2ProviderService::onDestroySession, this, sessionId, sessionInfo)); schedulePublishState(); if (sessionInfo == null) { Log.w(TAG, "Ignoring unknown session info."); return; } if (mClient == null) { return; } try { mClient.notifySessionReleased(sessionInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session info changed."); } } /** * Called when a session should be created. * Called when the service receives a request to create a session. * <p> * You should create and maintain your own session and notifies the client of * session info. Call {@link #notifySessionCreated(RoutingSessionInfo, long)} * with the given {@code requestId} to notify the information of a new session. * If you can't create the session or want to reject the request, pass {@code null} * as session info in {@link #notifySessionCreated(RoutingSessionInfo, long)} * with the given {@code requestId}. * The created session must have the same route feature and must include the given route * specified by {@code routeId}. * <p> * If the session can be controlled, you can optionally pass the control hints to * {@link RoutingSessionInfo.Builder#setControlHints(Bundle)}. Control hints is a * {@link Bundle} which contains how to control the session. * <p> * If you can't create the session or want to reject the request, call * {@link #notifySessionCreationFailed(long)} with the given {@code requestId}. * * @param packageName the package name of the application that selected the route * @param routeId the id of the route initially being connected * @param routeFeature the route feature of the new session * @param requestId the id of this session creation request * * @see RoutingSessionInfo.Builder#Builder(String, String, String) * @see RoutingSessionInfo.Builder#addSelectedRoute(String) * @see RoutingSessionInfo.Builder#setControlHints(Bundle) * @hide */ public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId, @NonNull String routeFeature, long requestId); /** * Called when a session is about to be destroyed. * You can clean up your session here. This can happen by the * client or provider itself. * Called when the session should be released. A client of the session or system can request * a session to be released. * <p> * After releasing the session, call {@link #notifySessionReleased(String)} * with the ID of the released session. * * Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger * this method to be called. * * @param sessionId id of the session being destroyed. * @param lastSessionInfo information of the session being destroyed. * @see #releaseSession(String) * @param sessionId id of the session being released. * @see #notifySessionReleased(String) * @see #getSessionInfo(String) * @hide */ public abstract void onDestroySession(@NonNull String sessionId, @NonNull RoutingSessionInfo lastSessionInfo); public abstract void onReleaseSession(@NonNull String sessionId); //TODO: make a way to reject the request /** * Called when a client requests selecting a route for the session. * After the route is selected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * * @param sessionId id of the session * @param routeId id of the route * @see #updateSessionInfo(RoutingSessionInfo) * @hide */ public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId); Loading @@ -305,9 +332,8 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: make a way to reject the request /** * Called when a client requests deselecting a route from the session. * After the route is deselected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * * @param sessionId id of the session * @param routeId id of the route Loading @@ -318,9 +344,8 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: make a way to reject the request /** * Called when a client requests transferring a session to a route. * After the transfer is finished, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * * @param sessionId id of the session * @param routeId id of the route Loading @@ -344,6 +369,8 @@ public abstract class MediaRoute2ProviderService extends Service { * </p> * * @param preference the new discovery preference * * TODO: This method needs tests. */ public void onDiscoveryPreferenceChanged(@NonNull RouteDiscoveryPreference preference) {} Loading Loading @@ -383,7 +410,7 @@ public abstract class MediaRoute2ProviderService extends Service { sessionInfos = new ArrayList<>(mSessionInfo.values()); } try { mClient.updateState(mProviderInfo, sessionInfos); mClient.updateState(mProviderInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to send onProviderInfoUpdated"); } Loading Loading @@ -415,6 +442,7 @@ public abstract class MediaRoute2ProviderService extends Service { MediaRoute2ProviderService.this, packageName, routeId, routeFeature, requestId)); } @Override public void releaseSession(@NonNull String sessionId) { if (!checkCallerisSystem()) { Loading @@ -424,7 +452,7 @@ public abstract class MediaRoute2ProviderService extends Service { Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service."); return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession, mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession, MediaRoute2ProviderService.this, sessionId)); } Loading media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +13 −10 Original line number Diff line number Diff line Loading @@ -172,7 +172,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService MediaRoute2Info route = mRoutes.get(routeId); if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { // Tell the router that session cannot be created by passing null as sessionInfo. notifySessionCreated(/* sessionInfo= */ null, requestId); notifySessionCreationFailed(requestId); return; } maybeDeselectRoute(routeId); Loading @@ -196,8 +196,13 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override public void onDestroySession(String sessionId, RoutingSessionInfo lastSessionInfo) { for (String routeId : lastSessionInfo.getSelectedRoutes()) { public void onReleaseSession(String sessionId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); if (sessionInfo == null) { return; } for (String routeId : sessionInfo.getSelectedRoutes()) { mRouteIdToSessionId.remove(routeId); MediaRoute2Info route = mRoutes.get(routeId); if (route != null) { Loading @@ -206,6 +211,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .build()); } } notifySessionReleased(sessionId); } @Override Loading @@ -227,8 +233,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .removeSelectableRoute(routeId) .addDeselectableRoute(routeId) .build(); updateSessionInfo(newSessionInfo); notifySessionInfoChanged(newSessionInfo); notifySessionUpdated(newSessionInfo); } @Override Loading @@ -247,7 +252,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .build()); if (sessionInfo.getSelectedRoutes().size() == 1) { releaseSession(sessionId); notifySessionReleased(sessionId); return; } Loading @@ -256,8 +261,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .addSelectableRoute(routeId) .removeDeselectableRoute(routeId) .build(); updateSessionInfo(newSessionInfo); notifySessionInfoChanged(newSessionInfo); notifySessionUpdated(newSessionInfo); } @Override Loading @@ -269,8 +273,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .removeDeselectableRoute(routeId) .removeTransferrableRoute(routeId) .build(); updateSessionInfo(newSessionInfo); notifySessionInfoChanged(newSessionInfo); notifySessionUpdated(newSessionInfo); } void maybeDeselectRoute(String routeId) { Loading services/core/java/com/android/server/media/MediaRoute2Provider.java +14 −19 Original line number Diff line number Diff line Loading @@ -23,18 +23,22 @@ import android.content.Intent; import android.media.MediaRoute2ProviderInfo; import android.media.RoutingSessionInfo; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; abstract class MediaRoute2Provider { final ComponentName mComponentName; final String mUniqueId; final Object mLock = new Object(); Callback mCallback; private volatile MediaRoute2ProviderInfo mProviderInfo; private volatile List<RoutingSessionInfo> mSessionInfos = Collections.emptyList(); @GuardedBy("mLock") final List<RoutingSessionInfo> mSessionInfos = new ArrayList<>(); MediaRoute2Provider(@NonNull ComponentName componentName) { mComponentName = Objects.requireNonNull(componentName, "Component name must not be null."); Loading Loading @@ -69,11 +73,12 @@ abstract class MediaRoute2Provider { @NonNull public List<RoutingSessionInfo> getSessionInfos() { synchronized (mLock) { return mSessionInfos; } } void setProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { void setProviderState(MediaRoute2ProviderInfo providerInfo) { if (providerInfo == null) { mProviderInfo = null; } else { Loading @@ -81,14 +86,6 @@ abstract class MediaRoute2Provider { .setUniqueId(mUniqueId) .build(); } List<RoutingSessionInfo> sessionInfoWithProviderId = new ArrayList<RoutingSessionInfo>(); for (RoutingSessionInfo sessionInfo : sessionInfos) { sessionInfoWithProviderId.add( new RoutingSessionInfo.Builder(sessionInfo) .setProviderId(mUniqueId) .build()); } mSessionInfos = sessionInfoWithProviderId; } void notifyProviderState() { Loading @@ -97,9 +94,8 @@ abstract class MediaRoute2Provider { } } void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { setProviderState(providerInfo, sessionInfos); void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo) { setProviderState(providerInfo); notifyProviderState(); } Loading @@ -112,10 +108,9 @@ abstract class MediaRoute2Provider { void onProviderStateChanged(@Nullable MediaRoute2Provider provider); void onSessionCreated(@NonNull MediaRoute2Provider provider, @Nullable RoutingSessionInfo sessionInfo, long requestId); // TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes. void onSessionInfoChanged(@NonNull MediaRoute2Provider provider, void onSessionCreationFailed(@NonNull MediaRoute2Provider provider, long requestId); void onSessionUpdated(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo); // TODO: Call this when service actually notifies of session release. void onSessionReleased(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo); } Loading services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +128 −29 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
media/java/android/media/IMediaRoute2ProviderClient.aidl +6 −4 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ import android.os.Bundle; * @hide */ oneway interface IMediaRoute2ProviderClient { void updateState(in MediaRoute2ProviderInfo providerInfo, in List<RoutingSessionInfo> sessionInfos); void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, long requestId); void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo); // TODO: Change it to updateRoutes? void updateState(in MediaRoute2ProviderInfo providerInfo); void notifySessionCreated(in RoutingSessionInfo sessionInfo, long requestId); void notifySessionCreationFailed(long requestId); void notifySessionUpdated(in RoutingSessionInfo sessionInfo); void notifySessionReleased(in RoutingSessionInfo sessionInfo); }
media/java/android/media/MediaRoute2ProviderService.java +114 −86 Original line number Diff line number Diff line Loading @@ -58,6 +58,16 @@ public abstract class MediaRoute2ProviderService extends Service { public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; /** * The request ID to pass {@link #notifySessionCreated(RoutingSessionInfo, long)} * when {@link MediaRoute2ProviderService} created a session although there was no creation * request. * * @see #notifySessionCreated(RoutingSessionInfo, long) * @hide */ public static final long REQUEST_ID_UNKNOWN = 0; private final Handler mHandler; private final Object mSessionLock = new Object(); private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false); Loading Loading @@ -118,7 +128,7 @@ public abstract class MediaRoute2ProviderService extends Service { * * @param sessionId id of the session * @return information of the session with the given id. * null if the session is destroyed or id is not valid. * null if the session is released or ID is not valid. * @hide */ @Nullable Loading @@ -143,161 +153,178 @@ public abstract class MediaRoute2ProviderService extends Service { } /** * Updates the information of a session. * If the session is destroyed or not created before, it will be ignored. * Call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify clients of * session info changes. * Notifies clients of that the session is created and ready for use. * <p> * If this session is created without any creation request, use {@link #REQUEST_ID_UNKNOWN} * as the request ID. * * @param sessionInfo new session information * @see #notifySessionCreated(RoutingSessionInfo, long) * @param sessionInfo information of the new session. * The {@link RoutingSessionInfo#getId() id} of the session must be unique. * @param requestId id of the previous request to create this session provided in * {@link #onCreateSession(String, String, String, long)} * @see #onCreateSession(String, String, String, long) * @hide */ public final void updateSessionInfo(@NonNull RoutingSessionInfo sessionInfo) { public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo, long requestId) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); Log.w(TAG, "Ignoring duplicate session id."); return; } mSessionInfo.put(sessionInfo.getId(), sessionInfo); } schedulePublishState(); } else { Log.w(TAG, "Ignoring unknown session info."); if (mClient == null) { return; } try { // TODO: Calling binder calls in multiple thread may cause timing issue. // Consider to change implementations to avoid the problems. // For example, post binder calls, always send all sessions at once, etc. mClient.notifySessionCreated(sessionInfo, requestId); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session created."); } } /** * Notifies the session is changed. * Notifies clients of that the session could not be created. * * TODO: This method is temporary, only created for tests. Remove when the alternative is ready. * @param requestId id of the previous request to create the session provided in * {@link #onCreateSession(String, String, String, long)}. * @see #onCreateSession(String, String, String, long) * @hide */ public final void notifySessionInfoChanged(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); } else { Log.w(TAG, "Ignoring unknown session info."); return; } } public final void notifySessionCreationFailed(long requestId) { if (mClient == null) { return; } try { mClient.notifySessionInfoChanged(sessionInfo); mClient.notifySessionCreationFailed(requestId); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session info changed."); Log.w(TAG, "Failed to notify session creation failed."); } } /** * Notifies clients of that the session is created and ready for use. If the session can be * controlled, pass a {@link Bundle} that contains how to control it. * Notifies the existing session is updated. For example, when * {@link RoutingSessionInfo#getSelectedRoutes() selected routes} are changed. * * @param sessionInfo information of the new session. * The {@link RoutingSessionInfo#getId() id} of the session must be * unique. Pass {@code null} to reject the request or inform clients that * session creation is failed. * @param requestId id of the previous request to create this session * @hide */ // TODO: fail reason? // TODO: Maybe better to create notifySessionCreationFailed? public final void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, long requestId) { if (sessionInfo != null) { public final void notifySessionUpdated(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); String sessionId = sessionInfo.getId(); synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { Log.w(TAG, "Ignoring duplicate session id."); mSessionInfo.put(sessionId, sessionInfo); } else { Log.w(TAG, "Ignoring unknown session info."); return; } mSessionInfo.put(sessionInfo.getId(), sessionInfo); } schedulePublishState(); } if (mClient == null) { return; } try { mClient.notifySessionCreated(sessionInfo, requestId); mClient.notifySessionUpdated(sessionInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session created."); Log.w(TAG, "Failed to notify session info changed."); } } /** * Releases a session with the given id. * {@link #onDestroySession} is called if the session is released. * Notifies that the session is released. * * @param sessionId id of the session to be released * @see #onDestroySession(String, RoutingSessionInfo) * @param sessionId id of the released session. * @see #onReleaseSession(String) * @hide */ public final void releaseSession(@NonNull String sessionId) { public final void notifySessionReleased(@NonNull String sessionId) { if (TextUtils.isEmpty(sessionId)) { throw new IllegalArgumentException("sessionId must not be empty"); } //TODO: notify media router service of release. RoutingSessionInfo sessionInfo; synchronized (mSessionLock) { sessionInfo = mSessionInfo.remove(sessionId); } if (sessionInfo != null) { mHandler.sendMessage(obtainMessage( MediaRoute2ProviderService::onDestroySession, this, sessionId, sessionInfo)); schedulePublishState(); if (sessionInfo == null) { Log.w(TAG, "Ignoring unknown session info."); return; } if (mClient == null) { return; } try { mClient.notifySessionReleased(sessionInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to notify session info changed."); } } /** * Called when a session should be created. * Called when the service receives a request to create a session. * <p> * You should create and maintain your own session and notifies the client of * session info. Call {@link #notifySessionCreated(RoutingSessionInfo, long)} * with the given {@code requestId} to notify the information of a new session. * If you can't create the session or want to reject the request, pass {@code null} * as session info in {@link #notifySessionCreated(RoutingSessionInfo, long)} * with the given {@code requestId}. * The created session must have the same route feature and must include the given route * specified by {@code routeId}. * <p> * If the session can be controlled, you can optionally pass the control hints to * {@link RoutingSessionInfo.Builder#setControlHints(Bundle)}. Control hints is a * {@link Bundle} which contains how to control the session. * <p> * If you can't create the session or want to reject the request, call * {@link #notifySessionCreationFailed(long)} with the given {@code requestId}. * * @param packageName the package name of the application that selected the route * @param routeId the id of the route initially being connected * @param routeFeature the route feature of the new session * @param requestId the id of this session creation request * * @see RoutingSessionInfo.Builder#Builder(String, String, String) * @see RoutingSessionInfo.Builder#addSelectedRoute(String) * @see RoutingSessionInfo.Builder#setControlHints(Bundle) * @hide */ public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId, @NonNull String routeFeature, long requestId); /** * Called when a session is about to be destroyed. * You can clean up your session here. This can happen by the * client or provider itself. * Called when the session should be released. A client of the session or system can request * a session to be released. * <p> * After releasing the session, call {@link #notifySessionReleased(String)} * with the ID of the released session. * * Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger * this method to be called. * * @param sessionId id of the session being destroyed. * @param lastSessionInfo information of the session being destroyed. * @see #releaseSession(String) * @param sessionId id of the session being released. * @see #notifySessionReleased(String) * @see #getSessionInfo(String) * @hide */ public abstract void onDestroySession(@NonNull String sessionId, @NonNull RoutingSessionInfo lastSessionInfo); public abstract void onReleaseSession(@NonNull String sessionId); //TODO: make a way to reject the request /** * Called when a client requests selecting a route for the session. * After the route is selected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * * @param sessionId id of the session * @param routeId id of the route * @see #updateSessionInfo(RoutingSessionInfo) * @hide */ public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId); Loading @@ -305,9 +332,8 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: make a way to reject the request /** * Called when a client requests deselecting a route from the session. * After the route is deselected, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * * @param sessionId id of the session * @param routeId id of the route Loading @@ -318,9 +344,8 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: make a way to reject the request /** * Called when a client requests transferring a session to a route. * After the transfer is finished, call {@link #updateSessionInfo(RoutingSessionInfo)} to update * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify * clients of updated session info. * After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)} * to update session info. * * @param sessionId id of the session * @param routeId id of the route Loading @@ -344,6 +369,8 @@ public abstract class MediaRoute2ProviderService extends Service { * </p> * * @param preference the new discovery preference * * TODO: This method needs tests. */ public void onDiscoveryPreferenceChanged(@NonNull RouteDiscoveryPreference preference) {} Loading Loading @@ -383,7 +410,7 @@ public abstract class MediaRoute2ProviderService extends Service { sessionInfos = new ArrayList<>(mSessionInfo.values()); } try { mClient.updateState(mProviderInfo, sessionInfos); mClient.updateState(mProviderInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to send onProviderInfoUpdated"); } Loading Loading @@ -415,6 +442,7 @@ public abstract class MediaRoute2ProviderService extends Service { MediaRoute2ProviderService.this, packageName, routeId, routeFeature, requestId)); } @Override public void releaseSession(@NonNull String sessionId) { if (!checkCallerisSystem()) { Loading @@ -424,7 +452,7 @@ public abstract class MediaRoute2ProviderService extends Service { Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service."); return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession, mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession, MediaRoute2ProviderService.this, sessionId)); } Loading
media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +13 −10 Original line number Diff line number Diff line Loading @@ -172,7 +172,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService MediaRoute2Info route = mRoutes.get(routeId); if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { // Tell the router that session cannot be created by passing null as sessionInfo. notifySessionCreated(/* sessionInfo= */ null, requestId); notifySessionCreationFailed(requestId); return; } maybeDeselectRoute(routeId); Loading @@ -196,8 +196,13 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override public void onDestroySession(String sessionId, RoutingSessionInfo lastSessionInfo) { for (String routeId : lastSessionInfo.getSelectedRoutes()) { public void onReleaseSession(String sessionId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); if (sessionInfo == null) { return; } for (String routeId : sessionInfo.getSelectedRoutes()) { mRouteIdToSessionId.remove(routeId); MediaRoute2Info route = mRoutes.get(routeId); if (route != null) { Loading @@ -206,6 +211,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .build()); } } notifySessionReleased(sessionId); } @Override Loading @@ -227,8 +233,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .removeSelectableRoute(routeId) .addDeselectableRoute(routeId) .build(); updateSessionInfo(newSessionInfo); notifySessionInfoChanged(newSessionInfo); notifySessionUpdated(newSessionInfo); } @Override Loading @@ -247,7 +252,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .build()); if (sessionInfo.getSelectedRoutes().size() == 1) { releaseSession(sessionId); notifySessionReleased(sessionId); return; } Loading @@ -256,8 +261,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .addSelectableRoute(routeId) .removeDeselectableRoute(routeId) .build(); updateSessionInfo(newSessionInfo); notifySessionInfoChanged(newSessionInfo); notifySessionUpdated(newSessionInfo); } @Override Loading @@ -269,8 +273,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .removeDeselectableRoute(routeId) .removeTransferrableRoute(routeId) .build(); updateSessionInfo(newSessionInfo); notifySessionInfoChanged(newSessionInfo); notifySessionUpdated(newSessionInfo); } void maybeDeselectRoute(String routeId) { Loading
services/core/java/com/android/server/media/MediaRoute2Provider.java +14 −19 Original line number Diff line number Diff line Loading @@ -23,18 +23,22 @@ import android.content.Intent; import android.media.MediaRoute2ProviderInfo; import android.media.RoutingSessionInfo; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; abstract class MediaRoute2Provider { final ComponentName mComponentName; final String mUniqueId; final Object mLock = new Object(); Callback mCallback; private volatile MediaRoute2ProviderInfo mProviderInfo; private volatile List<RoutingSessionInfo> mSessionInfos = Collections.emptyList(); @GuardedBy("mLock") final List<RoutingSessionInfo> mSessionInfos = new ArrayList<>(); MediaRoute2Provider(@NonNull ComponentName componentName) { mComponentName = Objects.requireNonNull(componentName, "Component name must not be null."); Loading Loading @@ -69,11 +73,12 @@ abstract class MediaRoute2Provider { @NonNull public List<RoutingSessionInfo> getSessionInfos() { synchronized (mLock) { return mSessionInfos; } } void setProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { void setProviderState(MediaRoute2ProviderInfo providerInfo) { if (providerInfo == null) { mProviderInfo = null; } else { Loading @@ -81,14 +86,6 @@ abstract class MediaRoute2Provider { .setUniqueId(mUniqueId) .build(); } List<RoutingSessionInfo> sessionInfoWithProviderId = new ArrayList<RoutingSessionInfo>(); for (RoutingSessionInfo sessionInfo : sessionInfos) { sessionInfoWithProviderId.add( new RoutingSessionInfo.Builder(sessionInfo) .setProviderId(mUniqueId) .build()); } mSessionInfos = sessionInfoWithProviderId; } void notifyProviderState() { Loading @@ -97,9 +94,8 @@ abstract class MediaRoute2Provider { } } void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { setProviderState(providerInfo, sessionInfos); void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo) { setProviderState(providerInfo); notifyProviderState(); } Loading @@ -112,10 +108,9 @@ abstract class MediaRoute2Provider { void onProviderStateChanged(@Nullable MediaRoute2Provider provider); void onSessionCreated(@NonNull MediaRoute2Provider provider, @Nullable RoutingSessionInfo sessionInfo, long requestId); // TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes. void onSessionInfoChanged(@NonNull MediaRoute2Provider provider, void onSessionCreationFailed(@NonNull MediaRoute2Provider provider, long requestId); void onSessionUpdated(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo); // TODO: Call this when service actually notifies of session release. void onSessionReleased(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo); } Loading
services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +128 −29 File changed.Preview size limit exceeded, changes collapsed. Show changes