From 920a676240b1443f824469a1c60d7cca5e5eebc7 Mon Sep 17 00:00:00 2001 From: Kyunglyul Hyun Date: Fri, 30 Apr 2021 08:59:41 +0000 Subject: Bind provider services when necessary The media router service bound media route provider services only when there was a discovery request. This prevented output switcher to work when there are only legacy media router clients (i.e. apps that didn't enable MR2) This CL makes the media router service binds media route provider services when the MediaRouter2Manager#startScan is called so that the output switcher can show cast sessions created by the providers. This CL also changes communication between the media router service and the provider services as cast sessions can be created/released even when the media router service is disconnected from media route provider. Bug: 176774510 Test: manually Change-Id: I481e1aa8f9dcc136cbf5788b9f66540708665af2 --- .../media/IMediaRoute2ProviderServiceCallback.aidl | 4 +- .../android/media/MediaRoute2ProviderService.java | 42 ++++++-- .../android/server/media/MediaRoute2Provider.java | 2 +- .../media/MediaRoute2ProviderServiceProxy.java | 108 +++++++++++++-------- .../server/media/MediaRouter2ServiceImpl.java | 10 ++ 5 files changed, 115 insertions(+), 51 deletions(-) diff --git a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl index 1755657ddc64..63c52a142fdd 100644 --- a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl +++ b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl @@ -26,9 +26,9 @@ import android.os.Bundle; */ oneway interface IMediaRoute2ProviderServiceCallback { // TODO: Change it to updateRoutes? - void updateState(in MediaRoute2ProviderInfo providerInfo); + void notifyProviderUpdated(in MediaRoute2ProviderInfo providerInfo); void notifySessionCreated(long requestId, in RoutingSessionInfo sessionInfo); - void notifySessionUpdated(in RoutingSessionInfo sessionInfo); + void notifySessionsUpdated(in List sessionInfo); void notifySessionReleased(in RoutingSessionInfo sessionInfo); void notifyRequestFailed(long requestId, int reason); } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 93fe06ac99aa..49e0411cc4d1 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -141,6 +141,7 @@ public abstract class MediaRoute2ProviderService extends Service { private final Object mSessionLock = new Object(); private final Object mRequestIdsLock = new Object(); private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false); + private final AtomicBoolean mSessionUpdateScheduled = new AtomicBoolean(false); private MediaRoute2ProviderServiceStub mStub; private IMediaRoute2ProviderServiceCallback mRemoteCallback; private volatile MediaRoute2ProviderInfo mProviderInfo; @@ -287,16 +288,8 @@ public abstract class MediaRoute2ProviderService extends Service { Log.w(TAG, "notifySessionUpdated: Ignoring unknown session info."); return; } - - if (mRemoteCallback == null) { - return; - } - try { - mRemoteCallback.notifySessionUpdated(sessionInfo); - } catch (RemoteException ex) { - Log.w(TAG, "Failed to notify session info changed."); - } } + scheduleUpdateSessions(); } /** @@ -479,6 +472,7 @@ public abstract class MediaRoute2ProviderService extends Service { void setCallback(IMediaRoute2ProviderServiceCallback callback) { mRemoteCallback = callback; schedulePublishState(); + scheduleUpdateSessions(); } void schedulePublishState() { @@ -497,12 +491,40 @@ public abstract class MediaRoute2ProviderService extends Service { } try { - mRemoteCallback.updateState(mProviderInfo); + mRemoteCallback.notifyProviderUpdated(mProviderInfo); } catch (RemoteException ex) { Log.w(TAG, "Failed to publish provider state.", ex); } } + void scheduleUpdateSessions() { + if (mSessionUpdateScheduled.compareAndSet(false, true)) { + mHandler.post(this::updateSessions); + } + } + + private void updateSessions() { + if (!mSessionUpdateScheduled.compareAndSet(true, false)) { + return; + } + + if (mRemoteCallback == null) { + return; + } + + List sessions; + synchronized (mSessionLock) { + sessions = new ArrayList<>(mSessionInfo.values()); + } + + try { + mRemoteCallback.notifySessionsUpdated(sessions); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to notify session info changed."); + } + + } + /** * Adds a requestId in the request ID list whose max size is {@link #MAX_REQUEST_IDS_SIZE}. * When the max size is reached, the first element is removed (FIFO). diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index edc9d7c64146..204ebfc678f5 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -47,7 +47,7 @@ abstract class MediaRoute2Provider { mUniqueId = componentName.flattenToShortString(); } - public void setCallback(MediaRoute2ProviderServiceProxy.Callback callback) { + public void setCallback(Callback callback) { mCallback = callback; } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index ab38dca2387d..21f61ca3978a 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -16,6 +16,10 @@ package com.android.server.media; +import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; @@ -43,6 +47,7 @@ import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -64,6 +69,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private Connection mActiveConnection; private boolean mConnectionReady; + private boolean mIsManagerScanning; private RouteDiscoveryPreference mLastDiscoveryPreference = null; @GuardedBy("mLock") @@ -86,6 +92,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider pw.println(prefix + " mConnectionReady=" + mConnectionReady); } + public void setManagerScanning(boolean managerScanning) { + if (mIsManagerScanning != managerScanning) { + mIsManagerScanning = managerScanning; + updateBinding(); + } + } + @Override public void requestCreateSession(long requestId, String packageName, String routeId, Bundle sessionHints) { @@ -209,7 +222,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider // Bind when there is a discovery preference or an active route session. return (mLastDiscoveryPreference != null && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty()) - || !getSessionInfos().isEmpty(); + || !getSessionInfos().isEmpty() + || mIsManagerScanning; } return false; } @@ -311,13 +325,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } } - private void onProviderStateUpdated(Connection connection, - MediaRoute2ProviderInfo providerInfo) { + private void onProviderUpdated(Connection connection, MediaRoute2ProviderInfo providerInfo) { if (mActiveConnection != connection) { return; } if (DEBUG) { - Slog.d(TAG, this + ": State changed "); + Slog.d(TAG, this + ": updated"); } setAndNotifyProviderState(providerInfo); } @@ -350,40 +363,44 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mCallback.onSessionCreated(this, requestId, newSession); } - private void onSessionUpdated(Connection connection, RoutingSessionInfo updatedSession) { - if (mActiveConnection != connection) { - return; + private int findSessionByIdLocked(RoutingSessionInfo session) { + for (int i = 0; i < mSessionInfos.size(); i++) { + if (TextUtils.equals(mSessionInfos.get(i).getId(), session.getId())) { + return i; + } } - if (updatedSession == null) { - Slog.w(TAG, "onSessionUpdated: Ignoring null session sent from " - + mComponentName); + return -1; + } + + + private void onSessionsUpdated(Connection connection, List sessions) { + if (mActiveConnection != connection) { return; } - updatedSession = assignProviderIdForSession(updatedSession); - - boolean found = false; + int targetIndex = 0; synchronized (mLock) { - for (int i = 0; i < mSessionInfos.size(); i++) { - if (mSessionInfos.get(i).getId().equals(updatedSession.getId())) { - mSessionInfos.set(i, updatedSession); - found = true; - break; + for (RoutingSessionInfo session : sessions) { + if (session == null) continue; + session = assignProviderIdForSession(session); + + int sourceIndex = findSessionByIdLocked(session); + if (sourceIndex < 0) { + mSessionInfos.add(targetIndex++, session); + dispatchSessionCreated(REQUEST_ID_NONE, session); + } else if (sourceIndex < targetIndex) { + Slog.w(TAG, "Ignoring duplicate session ID: " + session.getId()); + } else { + mSessionInfos.set(sourceIndex, session); + Collections.swap(mSessionInfos, sourceIndex, targetIndex++); + dispatchSessionUpdated(session); } } - - if (!found) { - for (RoutingSessionInfo releasingSession : mReleasingSessions) { - if (TextUtils.equals(releasingSession.getId(), updatedSession.getId())) { - return; - } - } - Slog.w(TAG, "onSessionUpdated: Matching session info not found"); - return; + for (int i = mSessionInfos.size() - 1; i >= targetIndex; i--) { + RoutingSessionInfo releasedSession = mSessionInfos.remove(i); + dispatchSessionReleased(releasedSession); } } - - mCallback.onSessionUpdated(this, updatedSession); } private void onSessionReleased(Connection connection, RoutingSessionInfo releaedSession) { @@ -424,6 +441,21 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mCallback.onSessionReleased(this, releaedSession); } + private void dispatchSessionCreated(long requestId, RoutingSessionInfo session) { + mHandler.sendMessage( + obtainMessage(mCallback::onSessionCreated, this, requestId, session)); + } + + private void dispatchSessionUpdated(RoutingSessionInfo session) { + mHandler.sendMessage( + obtainMessage(mCallback::onSessionUpdated, this, session)); + } + + private void dispatchSessionReleased(RoutingSessionInfo session) { + mHandler.sendMessage( + obtainMessage(mCallback::onSessionReleased, this, session)); + } + private RoutingSessionInfo assignProviderIdForSession(RoutingSessionInfo sessionInfo) { return new RoutingSessionInfo.Builder(sessionInfo) .setOwnerPackageName(mComponentName.getPackageName()) @@ -436,7 +468,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider return; } - if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) { + if (requestId == REQUEST_ID_NONE) { Slog.w(TAG, "onRequestFailed: Ignoring requestId REQUEST_ID_NONE"); return; } @@ -561,16 +593,16 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mHandler.post(() -> onConnectionDied(Connection.this)); } - void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo) { - mHandler.post(() -> onProviderStateUpdated(Connection.this, providerInfo)); + void postProviderUpdated(MediaRoute2ProviderInfo providerInfo) { + mHandler.post(() -> onProviderUpdated(Connection.this, providerInfo)); } void postSessionCreated(long requestId, RoutingSessionInfo sessionInfo) { mHandler.post(() -> onSessionCreated(Connection.this, requestId, sessionInfo)); } - void postSessionUpdated(RoutingSessionInfo sessionInfo) { - mHandler.post(() -> onSessionUpdated(Connection.this, sessionInfo)); + void postSessionsUpdated(List sessionInfo) { + mHandler.post(() -> onSessionsUpdated(Connection.this, sessionInfo)); } void postSessionReleased(RoutingSessionInfo sessionInfo) { @@ -595,10 +627,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void updateState(MediaRoute2ProviderInfo providerInfo) { + public void notifyProviderUpdated(MediaRoute2ProviderInfo providerInfo) { Connection connection = mConnectionRef.get(); if (connection != null) { - connection.postProviderStateUpdated(providerInfo); + connection.postProviderUpdated(providerInfo); } } @@ -611,10 +643,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void notifySessionUpdated(RoutingSessionInfo sessionInfo) { + public void notifySessionsUpdated(List sessionInfo) { Connection connection = mConnectionRef.get(); if (connection != null) { - connection.postSessionUpdated(sessionInfo); + connection.postSessionsUpdated(sessionInfo); } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 1dbc8a926996..168ca551317a 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -2156,6 +2156,8 @@ class MediaRouter2ServiceImpl { List routerRecords = getRouterRecords(); List managerRecords = getManagerRecords(); + boolean shouldBindProviders = false; + if (service.mPowerManager.isInteractive()) { boolean isManagerScanning = managerRecords.stream().anyMatch(manager -> manager.mIsScanning && service.mActivityManager @@ -2166,6 +2168,7 @@ class MediaRouter2ServiceImpl { discoveryPreferences = routerRecords.stream() .map(record -> record.mDiscoveryPreference) .collect(Collectors.toList()); + shouldBindProviders = true; } else { discoveryPreferences = routerRecords.stream().filter(record -> service.mActivityManager.getPackageImportance(record.mPackageName) @@ -2175,6 +2178,13 @@ class MediaRouter2ServiceImpl { } } + for (MediaRoute2Provider provider : mRouteProviders) { + if (provider instanceof MediaRoute2ProviderServiceProxy) { + ((MediaRoute2ProviderServiceProxy) provider) + .setManagerScanning(shouldBindProviders); + } + } + synchronized (service.mLock) { RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder(discoveryPreferences).build(); -- cgit v1.2.3-59-g8ed1b