Add support for SELF_SCAN_ONLY providers
Bug: 278249714 Test: atest CtsMediaBetterTogetherTestCases Change-Id: If24d505e889f933a9b4512b3aa5be0a2c91dc1ee Merged-In: If24d505e889f933a9b4512b3aa5be0a2c91dc1ee
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,15 +121,25 @@ 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(
|
||||||
|
mContext,
|
||||||
new ComponentName(serviceInfo.packageName, serviceInfo.name),
|
new ComponentName(serviceInfo.packageName, serviceInfo.name),
|
||||||
|
isSelfScanOnlyProvider,
|
||||||
mUserId);
|
mUserId);
|
||||||
proxy.start();
|
proxy.start();
|
||||||
mProxies.add(targetIndex++, proxy);
|
mProxies.add(targetIndex++, proxy);
|
||||||
|
|||||||
@@ -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,14 +2731,15 @@ 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()
|
||||||
|
.filter(
|
||||||
|
record ->
|
||||||
|
service.mActivityManager.getPackageImportance(
|
||||||
|
record.mPackageName)
|
||||||
<= sPackageImportanceForScanning)
|
<= sPackageImportanceForScanning)
|
||||||
.map(record -> record.mDiscoveryPreference)
|
|
||||||
.collect(Collectors.toList());
|
.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user