Add support for SELF_SCAN_ONLY providers

Bug: 278249714
Test: atest CtsMediaBetterTogetherTestCases
Change-Id: If24d505e889f933a9b4512b3aa5be0a2c91dc1ee
Merged-In: If24d505e889f933a9b4512b3aa5be0a2c91dc1ee
This commit is contained in:
Santiago Seifert
2023-05-12 17:26:13 +00:00
parent 7cfd239b42
commit e070ed328b
6 changed files with 100 additions and 35 deletions

View File

@@ -80,6 +80,18 @@ public abstract class MediaRoute2ProviderService extends Service {
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; 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.
*
* <p>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)} * The request ID to pass {@link #notifySessionCreated(long, RoutingSessionInfo)}
* when {@link MediaRoute2ProviderService} created a session although there was no creation * when {@link MediaRoute2ProviderService} created a session although there was no creation

View File

@@ -31,6 +31,7 @@ import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
abstract class MediaRoute2Provider { abstract class MediaRoute2Provider {
final ComponentName mComponentName; final ComponentName mComponentName;
@@ -56,7 +57,9 @@ abstract class MediaRoute2Provider {
public abstract void requestCreateSession(long requestId, String packageName, String routeId, public abstract void requestCreateSession(long requestId, String packageName, String routeId,
@Nullable Bundle sessionHints); @Nullable Bundle sessionHints);
public abstract void releaseSession(long requestId, String sessionId); public abstract void releaseSession(long requestId, String sessionId);
public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference);
public abstract void updateDiscoveryPreference(
Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference);
public abstract void selectRoute(long requestId, String sessionId, String routeId); public abstract void selectRoute(long requestId, String sessionId, String routeId);
public abstract void deselectRoute(long requestId, String sessionId, String routeId); public abstract void deselectRoute(long requestId, String sessionId, String routeId);

View File

@@ -49,6 +49,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* Maintains a connection to a particular {@link MediaRoute2ProviderService}. * Maintains a connection to a particular {@link MediaRoute2ProviderService}.
@@ -61,6 +62,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
private final Context mContext; private final Context mContext;
private final int mUserId; private final int mUserId;
private final Handler mHandler; private final Handler mHandler;
private final boolean mIsSelfScanOnlyProvider;
// Connection state // Connection state
private boolean mRunning; private boolean mRunning;
@@ -70,14 +72,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
private boolean mIsManagerScanning; private boolean mIsManagerScanning;
private RouteDiscoveryPreference mLastDiscoveryPreference = null; private RouteDiscoveryPreference mLastDiscoveryPreference = null;
private boolean mLastDiscoveryPreferenceIncludesThisPackage = false;
@GuardedBy("mLock") @GuardedBy("mLock")
final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>(); final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName, MediaRoute2ProviderServiceProxy(
@NonNull Context context,
@NonNull ComponentName componentName,
boolean isSelfScanOnlyProvider,
int userId) { int userId) {
super(componentName); super(componentName);
mContext = Objects.requireNonNull(context, "Context must not be null."); mContext = Objects.requireNonNull(context, "Context must not be null.");
mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
mUserId = userId; mUserId = userId;
mHandler = new Handler(Looper.myLooper()); mHandler = new Handler(Looper.myLooper());
} }
@@ -107,8 +114,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
} }
@Override @Override
public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { public void updateDiscoveryPreference(
Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) {
mLastDiscoveryPreference = discoveryPreference; mLastDiscoveryPreference = discoveryPreference;
mLastDiscoveryPreferenceIncludesThisPackage =
activelyScanningPackages.contains(mComponentName.getPackageName());
if (mConnectionReady) { if (mConnectionReady) {
mActiveConnection.updateDiscoveryPreference(discoveryPreference); mActiveConnection.updateDiscoveryPreference(discoveryPreference);
} }
@@ -209,11 +219,15 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
private boolean shouldBind() { private boolean shouldBind() {
if (mRunning) { if (mRunning) {
// Bind when there is a discovery preference or an active route session. boolean shouldBind =
return (mLastDiscoveryPreference != null mLastDiscoveryPreference != null
&& !mLastDiscoveryPreference.getPreferredFeatures().isEmpty()) && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty();
|| !getSessionInfos().isEmpty() if (mIsSelfScanOnlyProvider) {
|| mIsManagerScanning; shouldBind &= mLastDiscoveryPreferenceIncludesThisPackage;
}
shouldBind |= mIsManagerScanning;
shouldBind |= !getSessionInfos().isEmpty();
return shouldBind;
} }
return false; return false;
} }
@@ -301,7 +315,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
if (mActiveConnection == connection) { if (mActiveConnection == connection) {
mConnectionReady = true; mConnectionReady = true;
if (mLastDiscoveryPreference != null) { if (mLastDiscoveryPreference != null) {
updateDiscoveryPreference(mLastDiscoveryPreference); updateDiscoveryPreference(
mLastDiscoveryPreferenceIncludesThisPackage
? Set.of(mComponentName.getPackageName())
: Set.of(),
mLastDiscoveryPreference);
} }
} }
} }

View File

@@ -16,6 +16,8 @@
package com.android.server.media; package com.android.server.media;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
@@ -34,6 +36,7 @@ import android.util.Slog;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
/** /**
* Watches changes of packages, or scan them for finding media route providers. * Watches changes of packages, or scan them for finding media route providers.
@@ -41,8 +44,8 @@ import java.util.Collections;
final class MediaRoute2ProviderWatcher { final class MediaRoute2ProviderWatcher {
private static final String TAG = "MR2ProviderWatcher"; private static final String TAG = "MR2ProviderWatcher";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS_NONE = private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS =
PackageManager.ResolveInfoFlags.of(0); PackageManager.ResolveInfoFlags.of(GET_RESOLVED_FILTER);
private final Context mContext; private final Context mContext;
private final Callback mCallback; private final Callback mCallback;
@@ -118,16 +121,26 @@ final class MediaRoute2ProviderWatcher {
int targetIndex = 0; int targetIndex = 0;
Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE); Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
for (ResolveInfo resolveInfo : for (ResolveInfo resolveInfo :
mPackageManager.queryIntentServicesAsUser( mPackageManager.queryIntentServicesAsUser(intent, RESOLVE_INFO_FLAGS, mUserId)) {
intent, RESOLVE_INFO_FLAGS_NONE, mUserId)) {
ServiceInfo serviceInfo = resolveInfo.serviceInfo; ServiceInfo serviceInfo = resolveInfo.serviceInfo;
if (serviceInfo != null) { if (serviceInfo != null) {
boolean isSelfScanOnlyProvider = false;
Iterator<String> 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); int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
if (sourceIndex < 0) { if (sourceIndex < 0) {
MediaRoute2ProviderServiceProxy proxy = MediaRoute2ProviderServiceProxy proxy =
new MediaRoute2ProviderServiceProxy(mContext, new MediaRoute2ProviderServiceProxy(
new ComponentName(serviceInfo.packageName, serviceInfo.name), mContext,
mUserId); new ComponentName(serviceInfo.packageName, serviceInfo.name),
isSelfScanOnlyProvider,
mUserId);
proxy.start(); proxy.start();
mProxies.add(targetIndex++, proxy); mProxies.add(targetIndex++, proxy);
mCallback.onAddProviderService(proxy); mCallback.onAddProviderService(proxy);

View File

@@ -1478,6 +1478,7 @@ class MediaRouter2ServiceImpl {
final ArrayList<RouterRecord> mRouterRecords = new ArrayList<>(); final ArrayList<RouterRecord> mRouterRecords = new ArrayList<>();
final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>(); final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY; RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
Set<String> mActivelyScanningPackages = Set.of();
final UserHandler mHandler; final UserHandler mHandler;
UserRecord(int userId) { UserRecord(int userId) {
@@ -1525,7 +1526,12 @@ class MediaRouter2ServiceImpl {
pw.println(indent + "<no manager records>"); pw.println(indent + "<no manager records>");
} }
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)) { if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) {
pw.println(indent + "<could not dump handler state>"); pw.println(indent + "<could not dump handler state>");
@@ -1834,7 +1840,9 @@ class MediaRouter2ServiceImpl {
public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) {
proxy.setCallback(this); proxy.setCallback(this);
mRouteProviders.add(proxy); mRouteProviders.add(proxy);
proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); proxy.updateDiscoveryPreference(
mUserRecord.mActivelyScanningPackages,
mUserRecord.mCompositeDiscoveryPreference);
} }
@Override @Override
@@ -2341,8 +2349,8 @@ class MediaRouter2ServiceImpl {
return; return;
} }
notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo); notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo);
notifySessionInfoChangedToRouters(getRouterRecords(false), notifySessionInfoChangedToRouters(
mSystemProvider.getDefaultSessionInfo()); getRouterRecords(false), mSystemProvider.getDefaultSessionInfo());
return; return;
} }
@@ -2711,8 +2719,8 @@ class MediaRouter2ServiceImpl {
if (service == null) { if (service == null) {
return; return;
} }
List<RouteDiscoveryPreference> discoveryPreferences = Collections.emptyList(); List<RouterRecord> activeRouterRecords = Collections.emptyList();
List<RouterRecord> routerRecords = getRouterRecords(); List<RouterRecord> allRouterRecords = getRouterRecords();
List<ManagerRecord> managerRecords = getManagerRecords(); List<ManagerRecord> managerRecords = getManagerRecords();
boolean isManagerScanning = false; boolean isManagerScanning = false;
@@ -2723,15 +2731,16 @@ class MediaRouter2ServiceImpl {
<= sPackageImportanceForScanning); <= sPackageImportanceForScanning);
if (isManagerScanning) { if (isManagerScanning) {
discoveryPreferences = routerRecords.stream() activeRouterRecords = allRouterRecords;
.map(record -> record.mDiscoveryPreference)
.collect(Collectors.toList());
} else { } else {
discoveryPreferences = routerRecords.stream().filter(record -> activeRouterRecords =
service.mActivityManager.getPackageImportance(record.mPackageName) allRouterRecords.stream()
<= sPackageImportanceForScanning) .filter(
.map(record -> record.mDiscoveryPreference) record ->
.collect(Collectors.toList()); 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 // to query route providers once to obtain all of the routes of interest, which
// can be subsequently filtered for the individual discovery preferences. // can be subsequently filtered for the individual discovery preferences.
Set<String> preferredFeatures = new HashSet<>(); Set<String> preferredFeatures = new HashSet<>();
Set<String> activelyScanningPackages = new HashSet<>();
boolean activeScan = false; boolean activeScan = false;
for (RouteDiscoveryPreference preference : discoveryPreferences) { for (RouterRecord activeRouterRecord : activeRouterRecords) {
RouteDiscoveryPreference preference = activeRouterRecord.mDiscoveryPreference;
preferredFeatures.addAll(preference.getPreferredFeatures()); preferredFeatures.addAll(preference.getPreferredFeatures());
activeScan |= preference.shouldPerformActiveScan(); if (preference.shouldPerformActiveScan()) {
activeScan = true;
activelyScanningPackages.add(activeRouterRecord.mPackageName);
}
} }
RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder( RouteDiscoveryPreference newPreference = new RouteDiscoveryPreference.Builder(
List.copyOf(preferredFeatures), activeScan || isManagerScanning).build(); List.copyOf(preferredFeatures), activeScan || isManagerScanning).build();
synchronized (service.mLock) { synchronized (service.mLock) {
if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) { if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)
&& activelyScanningPackages.equals(mUserRecord.mActivelyScanningPackages)) {
return; return;
} }
mUserRecord.mCompositeDiscoveryPreference = newPreference; mUserRecord.mCompositeDiscoveryPreference = newPreference;
mUserRecord.mActivelyScanningPackages = activelyScanningPackages;
} }
for (MediaRoute2Provider provider : mRouteProviders) { for (MediaRoute2Provider provider : mRouteProviders) {
provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); provider.updateDiscoveryPreference(
activelyScanningPackages, mUserRecord.mCompositeDiscoveryPreference);
} }
} }

View File

@@ -42,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
@@ -196,7 +197,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
} }
@Override @Override
public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) { public void updateDiscoveryPreference(
Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference) {
// Do nothing // Do nothing
} }