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. This change may break output switcher, which enables transfer media of background apps. To alleviate that 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
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -146,6 +146,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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
@@ -76,6 +77,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
|
||||
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
|
||||
private final boolean mAboveStatusbar;
|
||||
private final NotificationEntryManager mNotificationEntryManager;
|
||||
private final MediaRouter2Manager mRouterManager;
|
||||
@VisibleForTesting
|
||||
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
|
||||
|
||||
@@ -104,6 +106,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
|
||||
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
|
||||
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mRouterManager = MediaRouter2Manager.getInstance(mContext);
|
||||
}
|
||||
|
||||
void start(@NonNull Callback cb) {
|
||||
@@ -134,6 +137,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
|
||||
mLocalMediaManager.stopScan();
|
||||
mLocalMediaManager.registerCallback(this);
|
||||
mLocalMediaManager.startScan();
|
||||
mRouterManager.startScan();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
@@ -144,6 +148,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
|
||||
mLocalMediaManager.unregisterCallback(this);
|
||||
mLocalMediaManager.stopScan();
|
||||
}
|
||||
if (mRouterManager != null) {
|
||||
mRouterManager.stopScan();
|
||||
}
|
||||
mMediaDevices.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ abstract class MediaRoute2Provider {
|
||||
@NonNull
|
||||
public List<RoutingSessionInfo> getSessionInfos() {
|
||||
synchronized (mLock) {
|
||||
return mSessionInfos;
|
||||
return new ArrayList<>(mSessionInfos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -542,6 +542,18 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
mService2.unregisterManager(manager);
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
||||
Reference in New Issue
Block a user