diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl index 6a763c5110ceb..b1f93a3ac82df 100644 --- a/media/java/android/media/IMediaRoute2ProviderClient.aidl +++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl @@ -25,6 +25,7 @@ import android.os.Bundle; * @hide */ oneway interface IMediaRoute2ProviderClient { - void updateProviderInfo(in MediaRoute2ProviderInfo info); + void updateState(in MediaRoute2ProviderInfo providerInfo, + in List sessionInfos); void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, long requestId); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 86fa20f1cca97..e89fc1e6e3725 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -22,6 +22,7 @@ import android.media.IMediaRouter2Manager; import android.media.IMediaRouterClient; import android.media.MediaRoute2Info; import android.media.MediaRouterClientState; +import android.media.RouteSessionInfo; /** * {@hide} @@ -63,4 +64,6 @@ interface IMediaRouterService { in MediaRoute2Info route, int volume); void requestUpdateVolume2Manager(IMediaRouter2Manager manager, in MediaRoute2Info route, int direction); + + List getActiveSessions(IMediaRouter2Manager manager); } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index d3f47e95380bc..9b44c6e583a2b 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -37,6 +37,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; /** * @hide @@ -48,6 +49,7 @@ public abstract class MediaRoute2ProviderService extends Service { private final Handler mHandler; private final Object mSessionLock = new Object(); + private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false); private ProviderStub mStub; private IMediaRoute2ProviderClient mClient; private MediaRoute2ProviderInfo mProviderInfo; @@ -132,20 +134,22 @@ public abstract class MediaRoute2ProviderService extends Service { */ public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - int sessionId = sessionInfo.getSessionId(); - //TODO: notify updated session info + int sessionId = sessionInfo.getSessionId(); + if (sessionInfo.getSelectedRoutes().isEmpty()) { + releaseSession(sessionId); + return; + } synchronized (mSessionLock) { if (mSessionInfo.containsKey(sessionId)) { mSessionInfo.put(sessionId, sessionInfo); + schedulePublishState(); } else { Log.w(TAG, "Ignoring unknown session info."); + return; } } - if (sessionInfo.getSelectedRoutes().isEmpty()) { - releaseSession(sessionId); - } } /** @@ -169,6 +173,7 @@ public abstract class MediaRoute2ProviderService extends Service { } mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo); } + schedulePublishState(); } if (mClient == null) { @@ -192,11 +197,12 @@ public abstract class MediaRoute2ProviderService extends Service { //TODO: notify media router service of release. RouteSessionInfo sessionInfo; synchronized (mSessionLock) { - sessionInfo = mSessionInfo.put(sessionId, null); + sessionInfo = mSessionInfo.remove(sessionId); } if (sessionInfo != null) { mHandler.sendMessage(obtainMessage( MediaRoute2ProviderService::onDestroySession, this, sessionId, sessionInfo)); + schedulePublishState(); } } @@ -268,23 +274,37 @@ public abstract class MediaRoute2ProviderService extends Service { /** * Updates provider info and publishes routes and session info. */ - public final void updateProviderInfo(MediaRoute2ProviderInfo info) { - mProviderInfo = info; - publishState(); + public final void updateProviderInfo(@NonNull MediaRoute2ProviderInfo providerInfo) { + mProviderInfo = Objects.requireNonNull(providerInfo, "providerInfo must not be null"); + schedulePublishState(); } void setClient(IMediaRoute2ProviderClient client) { mClient = client; - publishState(); + schedulePublishState(); } - void publishState() { - //TODO: sends session info + void schedulePublishState() { + if (mStatePublishScheduled.compareAndSet(false, true)) { + mHandler.post(this::publishState); + } + } + + private void publishState() { + if (!mStatePublishScheduled.compareAndSet(true, false)) { + return; + } + if (mClient == null) { return; } + + List sessionInfos; + synchronized (mSessionLock) { + sessionInfos = new ArrayList<>(mSessionInfo.values()); + } try { - mClient.updateProviderInfo(mProviderInfo); + mClient.updateState(mProviderInfo, sessionInfos); } catch (RemoteException ex) { Log.w(TAG, "Failed to send onProviderInfoUpdated"); } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 4213dd32d44a7..2e68e42e714f8 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -176,7 +176,18 @@ public class MediaRouter2Manager { } @NonNull - public List getRouteSessions() { + public List getActiveSessions() { + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + return mMediaRouterService.getActiveSessions(client); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + } + } return Collections.emptyList(); } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index cd3b50af91279..768716d40f442 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -280,12 +280,12 @@ public class MediaRouterManagerTest { }); //TODO: it fails due to not releasing session - assertEquals(0, mManager.getActiveRoutes().size()); + assertEquals(0, mManager.getActiveSessions().size()); mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)); latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); - assertEquals(1, mManager.getActiveRoutes().size()); + assertEquals(1, mManager.getActiveSessions().size()); //TODO: release the session /* diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index a30dd98987c1f..3cc18b6fe7d3e 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -24,6 +24,9 @@ import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.RouteSessionInfo; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; abstract class MediaRoute2Provider { @@ -31,7 +34,8 @@ abstract class MediaRoute2Provider { final String mUniqueId; Callback mCallback; - private MediaRoute2ProviderInfo mProviderInfo; + private volatile MediaRoute2ProviderInfo mProviderInfo; + private volatile List mSessionInfos = Collections.emptyList(); MediaRoute2Provider(@NonNull ComponentName componentName) { mComponentName = Objects.requireNonNull(componentName, "Component name must not be null."); @@ -64,15 +68,29 @@ abstract class MediaRoute2Provider { return mProviderInfo; } - void setAndNotifyProviderInfo(MediaRoute2ProviderInfo info) { - //TODO: check if info is not updated - if (info == null) { + @NonNull + public List getSessionInfos() { + return mSessionInfos; + } + + void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, + List sessionInfos) { + if (providerInfo == null) { mProviderInfo = null; } else { - mProviderInfo = new MediaRoute2ProviderInfo.Builder(info) + mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo) .setUniqueId(mUniqueId) .build(); } + List sessionInfoWithProviderId = new ArrayList(); + for (RouteSessionInfo sessionInfo : sessionInfos) { + sessionInfoWithProviderId.add( + new RouteSessionInfo.Builder(sessionInfo) + .setProviderId(mUniqueId) + .build()); + } + mSessionInfos = sessionInfoWithProviderId; + if (mCallback != null) { mCallback.onProviderStateChanged(this); } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 0fc06061624db..f9cfe7d73cacc 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -38,6 +38,8 @@ import android.util.Slog; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -268,14 +270,15 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - private void onProviderInfoUpdated(Connection connection, MediaRoute2ProviderInfo info) { + private void onProviderStateUpdated(Connection connection, + MediaRoute2ProviderInfo providerInfo, List sessionInfos) { if (mActiveConnection != connection) { return; } if (DEBUG) { Slog.d(TAG, this + ": State changed "); } - setAndNotifyProviderInfo(info); + setAndNotifyProviderState(providerInfo, sessionInfos); } private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo, @@ -291,7 +294,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv mConnectionReady = false; mActiveConnection.dispose(); mActiveConnection = null; - setAndNotifyProviderInfo(null); + setAndNotifyProviderState(null, Collections.emptyList()); } } @@ -397,8 +400,10 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv mHandler.post(() -> onConnectionDied(Connection.this)); } - void postProviderInfoUpdated(MediaRoute2ProviderInfo info) { - mHandler.post(() -> onProviderInfoUpdated(Connection.this, info)); + void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo, + List sessionInfos) { + mHandler.post(() -> onProviderStateUpdated(Connection.this, + providerInfo, sessionInfos)); } void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) { @@ -419,10 +424,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void updateProviderInfo(MediaRoute2ProviderInfo info) { + public void updateState(MediaRoute2ProviderInfo providerInfo, + List sessionInfos) { Connection connection = mConnectionRef.get(); if (connection != null) { - connection.postProviderInfoUpdated(info); + connection.postProviderStateUpdated(providerInfo, sessionInfos); } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index fd41b6fc363d8..d78879f63de5f 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -288,6 +288,18 @@ class MediaRouter2ServiceImpl { } } + @NonNull + public List getActiveSessions(IMediaRouter2Manager manager) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return getActiveSessionsLocked(manager); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + //TODO: Review this is handling multi-user properly. void switchUser() { synchronized (mLock) { @@ -523,6 +535,21 @@ class MediaRouter2ServiceImpl { } } + private List getActiveSessionsLocked(IMediaRouter2Manager manager) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord == null) { + return Collections.emptyList(); + } + + List sessionInfos = new ArrayList<>(); + for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) { + sessionInfos.addAll(provider.getSessionInfos()); + } + return sessionInfos; + } + private void initializeUserLocked(UserRecord userRecord) { if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); @@ -731,6 +758,7 @@ class MediaRouter2ServiceImpl { this, provider, sessionInfo, requestId)); } + //TODO: notify session info updates private void updateProvider(MediaRoute2Provider provider) { int providerIndex = getProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index a51158b4ae023..8a0d2324cc433 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -39,6 +39,7 @@ import android.media.MediaRouter; import android.media.MediaRouterClientState; import android.media.RemoteDisplayState; import android.media.RemoteDisplayState.RemoteDisplayInfo; +import android.media.RouteSessionInfo; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -523,6 +524,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub mService2.requestUpdateVolume2Manager(manager, route, delta); } + // Binder call + @Override + public List getActiveSessions(IMediaRouter2Manager manager) { + return mService2.getActiveSessions(manager); + } + void restoreBluetoothA2dp() { try { boolean a2dpOn; diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 8451662baa155..263af70a7cadb 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -35,6 +35,8 @@ import android.util.Log; import com.android.internal.R; +import java.util.Collections; + /** * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. */ @@ -214,6 +216,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { builder.addRoute(mBluetoothA2dpRoute); } builder.addRoute(mDefaultRoute); - setAndNotifyProviderInfo(builder.build()); + setAndNotifyProviderState(builder.build(), Collections.emptyList()); } }