diff options
author | Kyunglyul Hyun <klhyun@google.com> | 2020-10-15 06:20:45 +0000 |
---|---|---|
committer | Kyunglyul Hyun <klhyun@google.com> | 2021-01-15 07:39:42 +0000 |
commit | 04e35853327ba498355a2ddb0be15e2e8ce6b51e (patch) | |
tree | a8f3eafedfda4cfcbd1d92af7e13536e5403645f | |
parent | 4064d2da929055650e16a5341d53bd2b41edbde1 (diff) |
MediaRouterService binds services when necessary
MediaRouterService maintained bindings to provider services
once those are established.
With this CL, it only binds services when there is a non-empty
discovery preference set by a foreground app or an app is casting.
MediaRouter2Manager#startScan and #stopScan are added
so that system UI can force the service bind to the services and find
remote devices to cast.
Bug: 169575701
Bug: 172920557
Test: manually and CTS
Change-Id: I4a47fdb1c9fe05a04d26950485833c9cbfb91a69
(cherry picked from commit 9f889ca4e62044900004cb7ce4e85415d2b019e2)
(cherry picked from commit 602b168f99b00c037191234a85190d845680aa64)
7 files changed, 176 insertions, 9 deletions
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 068f9689d06f..4b8a8adade1f 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -73,6 +73,8 @@ interface IMediaRouterService { void unregisterManager(IMediaRouter2Manager manager); void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, in MediaRoute2Info route, int volume); + void startScan(IMediaRouter2Manager manager); + void stopScan(IMediaRouter2Manager manager); void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route); diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 4b09a5f19fb0..68237de2ca98 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -147,6 +147,36 @@ public final class MediaRouter2Manager { } /** + * Starts scanning remote routes. + * @see #stopScan(String) + */ + public void startScan() { + Client client = getOrCreateClient(); + if (client != null) { + try { + mMediaRouterService.startScan(client); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + } + } + } + + /** + * Stops scanning remote routes to reduce resource consumption. + * @see #startScan(String) + */ + public void stopScan() { + Client client = getOrCreateClient(); + if (client != null) { + try { + mMediaRouterService.stopScan(client); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + } + } + } + + /** * Gets a {@link android.media.session.MediaController} associated with the * given routing session. * If there is no matching media session, {@code null} is returned. diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 68f2964dbeb2..2f952474b7f0 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -153,6 +153,7 @@ public final class RouteDiscoveryPreference implements Parcelable { return false; } RouteDiscoveryPreference other = (RouteDiscoveryPreference) o; + //TODO: Make this order-free return Objects.equals(mPreferredFeatures, other.mPreferredFeatures) && mShouldPerformActiveScan == other.mShouldPerformActiveScan; } diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index f882c57e49ba..edc9d7c64146 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -77,7 +77,7 @@ abstract class MediaRoute2Provider { @NonNull public List<RoutingSessionInfo> getSessionInfos() { synchronized (mLock) { - return mSessionInfos; + return new ArrayList<>(mSessionInfos); } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 85af346aa88a..ab38dca2387d 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -108,8 +108,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mLastDiscoveryPreference = discoveryPreference; if (mConnectionReady) { mActiveConnection.updateDiscoveryPreference(discoveryPreference); - updateBinding(); } + updateBinding(); } @Override @@ -205,9 +205,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } private boolean shouldBind() { - //TODO: Binding could be delayed until it's necessary. if (mRunning) { - return true; + // Bind when there is a discovery preference or an active route session. + return (mLastDiscoveryPreference != null + && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty()) + || !getSessionInfos().isEmpty(); } return false; } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 1114fe0d9bf8..31edf43679e9 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.media; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; import static android.media.MediaRouter2Utils.getOriginalId; import static android.media.MediaRouter2Utils.getProviderId; @@ -73,10 +74,12 @@ class MediaRouter2ServiceImpl { // TODO: (In Android S or later) if we add callback methods for generic failures // in MediaRouter2, remove this constant and replace the usages with the real request IDs. private static final long DUMMY_REQUEST_ID = -1; + private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND; private final Context mContext; private final Object mLock = new Object(); final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); + final ActivityManager mActivityManager; @GuardedBy("mLock") private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); @@ -87,8 +90,21 @@ class MediaRouter2ServiceImpl { @GuardedBy("mLock") private int mCurrentUserId = -1; + private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = + (uid, importance) -> { + synchronized (mLock) { + final int count = mUserRecords.size(); + for (int i = 0; i < count; i++) { + mUserRecords.valueAt(i).mHandler.maybeUpdateDiscoveryPreferenceForUid(uid); + } + } + }; + MediaRouter2ServiceImpl(Context context) { mContext = context; + mActivityManager = mContext.getSystemService(ActivityManager.class); + mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, + PACKAGE_IMPORTANCE_FOR_DISCOVERY); } //////////////////////////////////////////////////////////////// @@ -388,6 +404,30 @@ class MediaRouter2ServiceImpl { } } + public void startScan(IMediaRouter2Manager manager) { + Objects.requireNonNull(manager, "manager must not be null"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + startScanLocked(manager); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void stopScan(IMediaRouter2Manager manager) { + Objects.requireNonNull(manager, "manager must not be null"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + stopScanLocked(manager); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, MediaRoute2Info route, int volume) { Objects.requireNonNull(manager, "manager must not be null"); @@ -839,6 +879,24 @@ class MediaRouter2ServiceImpl { disposeUserIfNeededLocked(userRecord); // since manager removed from user } + private void startScanLocked(@NonNull IMediaRouter2Manager manager) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + if (managerRecord == null) { + return; + } + managerRecord.startScan(); + } + + private void stopScanLocked(@NonNull IMediaRouter2Manager manager) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + if (managerRecord == null) { + return; + } + managerRecord.stopScan(); + } + private void setRouteVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull MediaRoute2Info route, int volume) { @@ -1122,6 +1180,7 @@ class MediaRouter2ServiceImpl { public final String mPackageName; public final int mManagerId; public SessionCreationRequest mLastSessionCreationRequest; + public boolean mIsScanning; ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager, int uid, int pid, String packageName) { @@ -1146,6 +1205,24 @@ class MediaRouter2ServiceImpl { pw.println(prefix + this); } + public void startScan() { + if (mIsScanning) { + return; + } + mIsScanning = true; + mUserRecord.mHandler.sendMessage(PooledLambda.obtainMessage( + UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); + } + + public void stopScan() { + if (!mIsScanning) { + return; + } + mIsScanning = false; + mUserRecord.mHandler.sendMessage(PooledLambda.obtainMessage( + UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); + } + @Override public String toString() { return "Manager " + mPackageName + " (pid " + mPid + ")"; @@ -1262,6 +1339,24 @@ class MediaRouter2ServiceImpl { return null; } + public void maybeUpdateDiscoveryPreferenceForUid(int uid) { + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return; + } + boolean isUidRelevant; + synchronized (service.mLock) { + isUidRelevant = mUserRecord.mRouterRecords.stream().anyMatch( + router -> router.mUid == uid) + | mUserRecord.mManagerRecords.stream().anyMatch( + manager -> manager.mUid == uid); + } + if (isUidRelevant) { + sendMessage(PooledLambda.obtainMessage( + UserHandler::updateDiscoveryPreferenceOnHandler, this)); + } + } + private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo(); @@ -1767,6 +1862,16 @@ class MediaRouter2ServiceImpl { return managers; } + private List<RouterRecord> getRouterRecords() { + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return Collections.emptyList(); + } + synchronized (service.mLock) { + return new ArrayList<>(mUserRecord.mRouterRecords); + } + } + private List<ManagerRecord> getManagerRecords() { MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { @@ -2001,13 +2106,28 @@ class MediaRouter2ServiceImpl { return; } List<RouteDiscoveryPreference> discoveryPreferences = new ArrayList<>(); - synchronized (service.mLock) { - for (RouterRecord routerRecord : mUserRecord.mRouterRecords) { + List<RouterRecord> routerRecords = getRouterRecords(); + List<ManagerRecord> managerRecords = getManagerRecords(); + boolean isAnyManagerScanning = + managerRecords.stream().anyMatch(manager -> manager.mIsScanning + && service.mActivityManager.getPackageImportance(manager.mPackageName) + <= PACKAGE_IMPORTANCE_FOR_DISCOVERY); + + for (RouterRecord routerRecord : routerRecords) { + if (isAnyManagerScanning + || service.mActivityManager.getPackageImportance(routerRecord.mPackageName) + <= PACKAGE_IMPORTANCE_FOR_DISCOVERY) { discoveryPreferences.add(routerRecord.mDiscoveryPreference); } - mUserRecord.mCompositeDiscoveryPreference = - new RouteDiscoveryPreference.Builder(discoveryPreferences) - .build(); + } + + synchronized (service.mLock) { + RouteDiscoveryPreference newPreference = + new RouteDiscoveryPreference.Builder(discoveryPreferences).build(); + if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) { + return; + } + mUserRecord.mCompositeDiscoveryPreference = newPreference; } for (MediaRoute2Provider provider : mRouteProviders) { provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 0e52a67c8d39..b6d6cc48d0cd 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -544,6 +544,18 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override + public void startScan(IMediaRouter2Manager manager) { + mService2.startScan(manager); + } + + // Binder call + @Override + public void stopScan(IMediaRouter2Manager manager) { + mService2.stopScan(manager); + } + + // Binder call + @Override public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, MediaRoute2Info route, int volume) { mService2.setRouteVolumeWithManager(manager, requestId, route, volume); |