diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 11cb2be6660bd..1e3c15419cffc 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -80,6 +80,18 @@ public abstract class MediaRoute2ProviderService extends Service { @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; + /** + * A category indicating that the associated provider is only intended for use within the app + * that hosts the provider. + * + *

Declaring this category helps the system save resources by avoiding the launch of services + * whose routes are known to be private to the app that provides them. + * + * @hide + */ + public static final String CATEGORY_SELF_SCAN_ONLY = + "android.media.MediaRoute2ProviderService.SELF_SCAN_ONLY"; + /** * The request ID to pass {@link #notifySessionCreated(long, RoutingSessionInfo)} * when {@link MediaRoute2ProviderService} created a session although there was no creation diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index c076c0574afe6..c59b733508653 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -31,6 +31,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; abstract class MediaRoute2Provider { final ComponentName mComponentName; @@ -56,7 +57,9 @@ abstract class MediaRoute2Provider { public abstract void requestCreateSession(long requestId, String packageName, String routeId, @Nullable Bundle sessionHints); public abstract void releaseSession(long requestId, String sessionId); - public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference); + + public abstract void updateDiscoveryPreference( + Set activelyScanningPackages, RouteDiscoveryPreference discoveryPreference); public abstract void selectRoute(long requestId, String sessionId, String routeId); public abstract void deselectRoute(long requestId, String sessionId, String routeId); diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 72b843672ae8f..3cf0786b7e287 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -49,6 +49,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Maintains a connection to a particular {@link MediaRoute2ProviderService}. @@ -61,6 +62,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private final Context mContext; private final int mUserId; private final Handler mHandler; + private final boolean mIsSelfScanOnlyProvider; // Connection state private boolean mRunning; @@ -70,14 +72,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private boolean mIsManagerScanning; private RouteDiscoveryPreference mLastDiscoveryPreference = null; + private boolean mLastDiscoveryPreferenceIncludesThisPackage = false; @GuardedBy("mLock") final List mReleasingSessions = new ArrayList<>(); - MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName, + MediaRoute2ProviderServiceProxy( + @NonNull Context context, + @NonNull ComponentName componentName, + boolean isSelfScanOnlyProvider, int userId) { super(componentName); mContext = Objects.requireNonNull(context, "Context must not be null."); + mIsSelfScanOnlyProvider = isSelfScanOnlyProvider; mUserId = userId; mHandler = new Handler(Looper.myLooper()); } @@ -107,8 +114,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } @Override - public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { + public void updateDiscoveryPreference( + Set activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) { mLastDiscoveryPreference = discoveryPreference; + mLastDiscoveryPreferenceIncludesThisPackage = + activelyScanningPackages.contains(mComponentName.getPackageName()); if (mConnectionReady) { mActiveConnection.updateDiscoveryPreference(discoveryPreference); } @@ -209,11 +219,15 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider private boolean shouldBind() { if (mRunning) { - // Bind when there is a discovery preference or an active route session. - return (mLastDiscoveryPreference != null - && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty()) - || !getSessionInfos().isEmpty() - || mIsManagerScanning; + boolean shouldBind = + mLastDiscoveryPreference != null + && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty(); + if (mIsSelfScanOnlyProvider) { + shouldBind &= mLastDiscoveryPreferenceIncludesThisPackage; + } + shouldBind |= mIsManagerScanning; + shouldBind |= !getSessionInfos().isEmpty(); + return shouldBind; } return false; } @@ -301,7 +315,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider if (mActiveConnection == connection) { mConnectionReady = true; if (mLastDiscoveryPreference != null) { - updateDiscoveryPreference(mLastDiscoveryPreference); + updateDiscoveryPreference( + mLastDiscoveryPreferenceIncludesThisPackage + ? Set.of(mComponentName.getPackageName()) + : Set.of(), + mLastDiscoveryPreference); } } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java index 46bccaf314d90..bd252e7fdfd26 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java @@ -16,6 +16,8 @@ package com.android.server.media; +import static android.content.pm.PackageManager.GET_RESOLVED_FILTER; + import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -34,6 +36,7 @@ import android.util.Slog; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; /** * Watches changes of packages, or scan them for finding media route providers. @@ -41,8 +44,8 @@ import java.util.Collections; final class MediaRoute2ProviderWatcher { private static final String TAG = "MR2ProviderWatcher"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS_NONE = - PackageManager.ResolveInfoFlags.of(0); + private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS = + PackageManager.ResolveInfoFlags.of(GET_RESOLVED_FILTER); private final Context mContext; private final Callback mCallback; @@ -118,16 +121,26 @@ final class MediaRoute2ProviderWatcher { int targetIndex = 0; Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE); for (ResolveInfo resolveInfo : - mPackageManager.queryIntentServicesAsUser( - intent, RESOLVE_INFO_FLAGS_NONE, mUserId)) { + mPackageManager.queryIntentServicesAsUser(intent, RESOLVE_INFO_FLAGS, mUserId)) { ServiceInfo serviceInfo = resolveInfo.serviceInfo; if (serviceInfo != null) { + boolean isSelfScanOnlyProvider = false; + Iterator categoriesIterator = resolveInfo.filter.categoriesIterator(); + if (categoriesIterator != null) { + while (categoriesIterator.hasNext()) { + isSelfScanOnlyProvider |= + MediaRoute2ProviderService.CATEGORY_SELF_SCAN_ONLY.equals( + categoriesIterator.next()); + } + } int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name); if (sourceIndex < 0) { MediaRoute2ProviderServiceProxy proxy = - new MediaRoute2ProviderServiceProxy(mContext, - new ComponentName(serviceInfo.packageName, serviceInfo.name), - mUserId); + new MediaRoute2ProviderServiceProxy( + mContext, + new ComponentName(serviceInfo.packageName, serviceInfo.name), + isSelfScanOnlyProvider, + mUserId); proxy.start(); mProxies.add(targetIndex++, proxy); mCallback.onAddProviderService(proxy); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index b79991e5d0132..1059c1981ce51 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -1478,6 +1478,7 @@ class MediaRouter2ServiceImpl { final ArrayList mRouterRecords = new ArrayList<>(); final ArrayList mManagerRecords = new ArrayList<>(); RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY; + Set mActivelyScanningPackages = Set.of(); final UserHandler mHandler; UserRecord(int userId) { @@ -1525,7 +1526,12 @@ class MediaRouter2ServiceImpl { pw.println(indent + ""); } - mCompositeDiscoveryPreference.dump(pw, indent); + pw.println(indent + "Composite discovery preference:"); + mCompositeDiscoveryPreference.dump(pw, indent + " "); + pw.println( + indent + + "Packages actively scanning: " + + String.join(", ", mActivelyScanningPackages)); if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) { pw.println(indent + ""); @@ -1834,7 +1840,9 @@ class MediaRouter2ServiceImpl { public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { proxy.setCallback(this); mRouteProviders.add(proxy); - proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); + proxy.updateDiscoveryPreference( + mUserRecord.mActivelyScanningPackages, + mUserRecord.mCompositeDiscoveryPreference); } @Override @@ -2341,8 +2349,8 @@ class MediaRouter2ServiceImpl { return; } notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo); - notifySessionInfoChangedToRouters(getRouterRecords(false), - mSystemProvider.getDefaultSessionInfo()); + notifySessionInfoChangedToRouters( + getRouterRecords(false), mSystemProvider.getDefaultSessionInfo()); return; } @@ -2711,8 +2719,8 @@ class MediaRouter2ServiceImpl { if (service == null) { return; } - List discoveryPreferences = Collections.emptyList(); - List routerRecords = getRouterRecords(); + List activeRouterRecords = Collections.emptyList(); + List allRouterRecords = getRouterRecords(); List managerRecords = getManagerRecords(); boolean isManagerScanning = false; @@ -2723,15 +2731,16 @@ class MediaRouter2ServiceImpl { <= sPackageImportanceForScanning); if (isManagerScanning) { - discoveryPreferences = routerRecords.stream() - .map(record -> record.mDiscoveryPreference) - .collect(Collectors.toList()); + activeRouterRecords = allRouterRecords; } else { - discoveryPreferences = routerRecords.stream().filter(record -> - service.mActivityManager.getPackageImportance(record.mPackageName) - <= sPackageImportanceForScanning) - .map(record -> record.mDiscoveryPreference) - .collect(Collectors.toList()); + activeRouterRecords = + allRouterRecords.stream() + .filter( + record -> + service.mActivityManager.getPackageImportance( + record.mPackageName) + <= sPackageImportanceForScanning) + .collect(Collectors.toList()); } } @@ -2748,22 +2757,30 @@ class MediaRouter2ServiceImpl { // to query route providers once to obtain all of the routes of interest, which // can be subsequently filtered for the individual discovery preferences. Set preferredFeatures = new HashSet<>(); + Set activelyScanningPackages = new HashSet<>(); boolean activeScan = false; - for (RouteDiscoveryPreference preference : discoveryPreferences) { + for (RouterRecord activeRouterRecord : activeRouterRecords) { + RouteDiscoveryPreference preference = activeRouterRecord.mDiscoveryPreference; preferredFeatures.addAll(preference.getPreferredFeatures()); - activeScan |= preference.shouldPerformActiveScan(); + if (preference.shouldPerformActiveScan()) { + activeScan = true; + activelyScanningPackages.add(activeRouterRecord.mPackageName); + } } RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder( List.copyOf(preferredFeatures), activeScan || isManagerScanning).build(); synchronized (service.mLock) { - if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) { + if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference) + && activelyScanningPackages.equals(mUserRecord.mActivelyScanningPackages)) { return; } mUserRecord.mCompositeDiscoveryPreference = newPreference; + mUserRecord.mActivelyScanningPackages = activelyScanningPackages; } for (MediaRoute2Provider provider : mRouteProviders) { - provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); + provider.updateDiscoveryPreference( + activelyScanningPackages, mUserRecord.mCompositeDiscoveryPreference); } } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 426bc5eed0515..f4e6abd47b440 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -42,6 +42,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. @@ -196,7 +197,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { + public void updateDiscoveryPreference( + Set activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) { // Do nothing }