Merge "MediaRouter: Make MR2M.CB.onRouteSelected to be called"

This commit is contained in:
Kyunglyul Hyun
2019-06-25 08:12:46 +00:00
committed by Android (Google) Code Review
10 changed files with 363 additions and 127 deletions

View File

@@ -17,12 +17,13 @@
package android.media;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
/**
* {@hide}
*/
oneway interface IMediaRouter2Manager {
void notifyRouteSelected(int uid, String routeId);
void notifyRouteSelected(int uid, in MediaRoute2Info route);
void notifyControlCategoriesChanged(int uid, in List<String> categories);
void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers);
}

View File

@@ -45,11 +45,25 @@ interface IMediaRouterService {
void registerClient2AsUser(IMediaRouter2Client client, String packageName, int userId);
void unregisterClient2(IMediaRouter2Client client);
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
/**
* Changes the selected route of the client.
*
* @param client Client to change it's selected route.
* @param route Route to be selected.
*/
void selectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
void setControlCategories(IMediaRouter2Client client, in List<String> categories);
void registerManagerAsUser(IMediaRouter2Manager manager,
String packageName, int userId);
void unregisterManager(IMediaRouter2Manager manager);
void setRemoteRoute(IMediaRouter2Manager manager,
int uid, String routeId, boolean explicit);
/**
* Changes the selected route of an application.
*
* @param manager Manager that calls the method
* @param uid UID of the client that will change the selected route.
* @param route Route to be selected.
*/
void selectClientRoute2(IMediaRouter2Manager manager, int clientUid,
in @nullable MediaRoute2Info route);
}

View File

@@ -23,6 +23,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.Objects;
/**
* Describes the properties of a route.
* @hide
@@ -43,6 +45,8 @@ public final class MediaRoute2Info implements Parcelable {
@NonNull
final String mId;
@Nullable
final String mProviderId;
@NonNull
final String mName;
@Nullable
@@ -52,6 +56,7 @@ public final class MediaRoute2Info implements Parcelable {
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
mProviderId = builder.mProviderId;
mName = builder.mName;
mDescription = builder.mDescription;
mExtras = builder.mExtras;
@@ -59,28 +64,63 @@ public final class MediaRoute2Info implements Parcelable {
MediaRoute2Info(@NonNull Parcel in) {
mId = in.readString();
mProviderId = in.readString();
mName = in.readString();
mDescription = in.readString();
mExtras = in.readBundle();
}
/**
* Returns true if the route info has all of the required field
* Returns true if the route info has all of the required field.
* A route info only obtained from {@link com.android.server.media.MediaRouterService}
* is valid.
* @hide
*/
//TODO: Reconsider the validity of a route info when fields are added.
public boolean isValid() {
if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName())) {
if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName())
|| TextUtils.isEmpty(getProviderId())) {
return false;
}
return true;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MediaRoute2Info)) {
return false;
}
MediaRoute2Info other = (MediaRoute2Info) obj;
return Objects.equals(mId, other.mId)
&& Objects.equals(mProviderId, other.mProviderId)
&& Objects.equals(mName, other.mName)
&& Objects.equals(mDescription, other.mDescription)
//TODO: This will be evaluated as false in most cases. Try not to.
&& Objects.equals(mExtras, other.mExtras);
}
@Override
public int hashCode() {
return Objects.hash(mId, mName, mDescription);
}
@NonNull
public String getId() {
return mId;
}
/**
* Gets the provider id of the route.
* @hide
*/
@Nullable
public String getProviderId() {
return mProviderId;
}
@NonNull
public String getName() {
return mName;
@@ -104,6 +144,7 @@ public final class MediaRoute2Info implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mProviderId);
dest.writeString(mName);
dest.writeString(mDescription);
dest.writeBundle(mExtras);
@@ -115,6 +156,8 @@ public final class MediaRoute2Info implements Parcelable {
.append("MediaRouteInfo{ ")
.append("id=").append(getId())
.append(", name=").append(getName())
.append(", description=").append(getDescription())
.append(", providerId=").append(getProviderId())
.append(" }");
return result.toString();
}
@@ -124,6 +167,7 @@ public final class MediaRoute2Info implements Parcelable {
*/
public static final class Builder {
String mId;
String mProviderId;
String mName;
String mDescription;
Bundle mExtras;
@@ -145,8 +189,14 @@ public final class MediaRoute2Info implements Parcelable {
}
setId(routeInfo.mId);
if (!TextUtils.isEmpty(routeInfo.mProviderId)) {
setProviderId(routeInfo.mProviderId);
}
setName(routeInfo.mName);
mDescription = routeInfo.mDescription;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
}
/**
@@ -161,6 +211,19 @@ public final class MediaRoute2Info implements Parcelable {
return this;
}
/**
* Sets the provider id of the route.
* @hide
*/
@NonNull
public Builder setProviderId(@NonNull String providerId) {
if (TextUtils.isEmpty(providerId)) {
throw new IllegalArgumentException("id must not be null or empty");
}
mProviderId = providerId;
return this;
}
/**
* Sets the user-visible name of the route.
*/
@@ -182,6 +245,15 @@ public final class MediaRoute2Info implements Parcelable {
return this;
}
/**
* Sets a bundle of extras for the route.
*/
@NonNull
public Builder setExtras(@Nullable Bundle extras) {
mExtras = extras;
return this;
}
/**
* Builds the {@link MediaRoute2Info media route info}.
*/

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArrayMap;
import java.util.Arrays;
@@ -82,8 +83,11 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
return true;
}
/**
* @hide
*/
@Nullable
String getUniqueId() {
public String getUniqueId() {
return mUniqueId;
}
@@ -149,9 +153,21 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
* {@link com.android.server.media.MediaRouterService} and used to identify providers.
* The id set by {@link MediaRoute2ProviderService} will be ignored.
* </p>
* @hide
*/
public Builder setUniqueId(@Nullable String uniqueId) {
if (TextUtils.equals(mUniqueId, uniqueId)) {
return this;
}
mUniqueId = uniqueId;
final int count = mRoutes.size();
for (int i = 0; i < count; i++) {
MediaRoute2Info route = mRoutes.valueAt(i);
mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route)
.setProviderId(mUniqueId)
.build());
}
return this;
}
@@ -164,7 +180,12 @@ public final class MediaRoute2ProviderInfo implements Parcelable {
if (mRoutes.containsValue(route)) {
throw new IllegalArgumentException("route descriptor already added");
}
mRoutes.put(route.getId(), route);
if (mUniqueId != null) {
mRoutes.put(route.getId(),
new MediaRoute2Info.Builder(route).setProviderId(mUniqueId).build());
} else {
mRoutes.put(route.getId(), route);
}
return this;
}

View File

@@ -18,6 +18,7 @@ package android.media;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.os.Looper;
@@ -168,6 +169,22 @@ public class MediaRouter2 {
return -1;
}
/**
* Selects the specified route.
*
* @param route The route to select.
*/
//TODO: add a parameter for category (e.g. mirroring/casting)
public void selectRoute(@Nullable MediaRoute2Info route) {
if (mClient != null) {
try {
mMediaRouterService.selectRoute2(mClient, route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select route.", ex);
}
}
}
/**
* Sends a media control request to be performed asynchronously by the route's destination.
* @param route the route that will receive the control request

View File

@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
@@ -59,8 +60,9 @@ public class MediaRouter2Manager {
@GuardedBy("sLock")
final ArrayList<CallbackRecord> mCallbacks = new ArrayList<>();
@SuppressWarnings("WeakerAccess") /* synthetic access */
@NonNull
private List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList();
List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList();
/**
* Gets an instance of media router manager that controls media route of other applications.
@@ -93,17 +95,12 @@ public class MediaRouter2Manager {
public void addCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull Callback callback) {
if (executor == null) {
throw new IllegalArgumentException("executor must not be null");
}
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(callback, "callback must not be null");
synchronized (sLock) {
final int index = findCallbackRecord(callback);
if (index >= 0) {
Log.w(TAG, "Ignore adding the same callback twice.");
if (findCallbackRecordIndex(callback) >= 0) {
Log.w(TAG, "Ignoring to add the same callback twice.");
return;
}
if (mCallbacks.size() == 0) {
@@ -116,7 +113,9 @@ public class MediaRouter2Manager {
Log.e(TAG, "Unable to register media router manager.", ex);
}
}
mCallbacks.add(new CallbackRecord(executor, callback));
CallbackRecord record = new CallbackRecord(executor, callback);
mCallbacks.add(record);
record.notifyRoutes();
}
}
@@ -131,7 +130,7 @@ public class MediaRouter2Manager {
}
synchronized (sLock) {
final int index = findCallbackRecord(callback);
final int index = findCallbackRecordIndex(callback);
if (index < 0) {
Log.w(TAG, "Ignore removing unknown callback. " + callback);
return;
@@ -148,7 +147,7 @@ public class MediaRouter2Manager {
}
}
private int findCallbackRecord(Callback callback) {
private int findCallbackRecordIndex(Callback callback) {
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
if (mCallbacks.get(i).mCallback == callback) {
@@ -162,12 +161,12 @@ public class MediaRouter2Manager {
* Selects media route for the specified application uid.
*
* @param uid The uid of the application that should change it's media route.
* @param routeId The id of the route to select
* @param route The route to select
*/
public void selectRoute(int uid, String routeId) {
public void selectRoute(int uid, MediaRoute2Info route) {
if (mClient != null) {
try {
mMediaRouterService.setRemoteRoute(mClient, uid, routeId, /* explicit= */true);
mMediaRouterService.selectClientRoute2(mClient, uid, route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select media route", ex);
}
@@ -182,7 +181,7 @@ public class MediaRouter2Manager {
public void unselectRoute(int uid) {
if (mClient != null) {
try {
mMediaRouterService.setRemoteRoute(mClient, uid, null, /* explicit= */ true);
mMediaRouterService.selectClientRoute2(mClient, uid, null);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select media route", ex);
}
@@ -220,15 +219,18 @@ public class MediaRouter2Manager {
if (prevRoute == null) {
notifyRouteAdded(routeInfo);
} else {
//TODO: Notify only it's really changed.
notifyRouteChanged(routeInfo);
if (!Objects.equals(prevRoute, routeInfo)) {
notifyRouteChanged(routeInfo);
}
updatedRouteIds.add(routeInfo.getId());
}
}
final Collection<MediaRoute2Info> prevRoutes = prevProvider.getRoutes();
for (MediaRoute2Info prevRoute : prevRoutes) {
notifyRouteRemoved(prevRoute);
if (!updatedRouteIds.contains(prevRoute.getId())) {
notifyRouteRemoved(prevRoute);
}
}
} else {
for (MediaRoute2Info routeInfo: routes) {
@@ -267,15 +269,15 @@ public class MediaRouter2Manager {
for (MediaRoute2ProviderInfo provider : providers) {
updateProvider(provider);
}
//TODO: Call notifyProviderRemoved for removed providers.
//TODO: Call notifyRouteRemoved for the routes of the removed providers.
//TODO: Filter invalid providers.
mProviders = providers;
}
void notifyRouteSelected(int uid, String routeId) {
void notifyRouteSelected(int uid, MediaRoute2Info route) {
for (CallbackRecord record : mCallbacks) {
record.mExecutor.execute(() -> record.mCallback.onRouteSelected(uid, routeId));
record.mExecutor.execute(() -> record.mCallback.onRouteSelected(uid, route));
}
}
@@ -289,41 +291,35 @@ public class MediaRouter2Manager {
/**
* Interface for receiving events about media routing changes.
*/
public abstract static class Callback {
public static class Callback {
/**
* Called when a route is added.
*/
public void onRouteAdded(MediaRoute2Info routeInfo) {}
public void onRouteAdded(@NonNull MediaRoute2Info routeInfo) {}
/**
* Called when a route is changed.
*/
public void onRouteChanged(MediaRoute2Info routeInfo) {}
public void onRouteChanged(@NonNull MediaRoute2Info routeInfo) {}
/**
* Called when a route is removed.
*/
public void onRouteRemoved(MediaRoute2Info routeInfo) {}
public void onRouteRemoved(@NonNull MediaRoute2Info routeInfo) {}
/**
* Called when a route is selected for some application uid.
* @param uid
* @param routeId
* Called when a route is selected for an application.
* @param uid uid of the application
* @param route selected route of the application.
*/
public abstract void onRouteSelected(int uid, String routeId);
public void onRouteSelected(int uid, @Nullable MediaRoute2Info route) {}
/**
* Called when the control categories of an application is changed.
* @param uid the uid of the app that changed control categories
* @param categories the changed categories
*/
public abstract void onControlCategoriesChanged(int uid, List<String> categories);
/**
* Called when the provider updates its information
* @param info the changed provider information
*/
public void onProviderInfoUpdated(MediaRoute2ProviderInfo info) {}
public void onControlCategoriesChanged(int uid, @NonNull List<String> categories) {}
}
final class CallbackRecord {
@@ -334,13 +330,22 @@ public class MediaRouter2Manager {
mExecutor = executor;
mCallback = callback;
}
void notifyRoutes() {
for (MediaRoute2ProviderInfo provider : mProviders) {
for (MediaRoute2Info routeInfo : provider.getRoutes()) {
mExecutor.execute(
() -> mCallback.onRouteAdded(routeInfo));
}
}
}
}
class Client extends IMediaRouter2Manager.Stub {
@Override
public void notifyRouteSelected(int uid, String routeId) {
public void notifyRouteSelected(int uid, MediaRoute2Info route) {
mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected,
MediaRouter2Manager.this, uid, routeId));
MediaRouter2Manager.this, uid, route));
}
@Override

View File

@@ -17,7 +17,6 @@
package com.android.mediaroutertest;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -30,13 +29,16 @@ import android.media.MediaRouter2Manager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
@@ -96,7 +98,7 @@ public class MediaRouterManagerTest {
mManager.removeCallback(mockCallback);
}
@Test
//TODO: Recover this test when media router 2 is finalized.
public void testRouteRemoved() {
MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
mManager.addCallback(mExecutor, mockCallback);
@@ -129,9 +131,6 @@ public class MediaRouterManagerTest {
MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
verify(mockCallback, after(AWAIT_MS).never()).onControlCategoriesChanged(uid,
TEST_CONTROL_CATEGORIES);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
(Runnable) () -> {
mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback);
@@ -143,4 +142,49 @@ public class MediaRouterManagerTest {
mManager.removeCallback(mockCallback);
}
@Test
public void selectRouteTest() throws Exception {
final int uid = android.os.Process.myUid();
CountDownLatch latch = new CountDownLatch(1);
MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
(Runnable) () -> {
mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback);
}
);
MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
MediaRoute2Info mSelectedRoute = null;
@Override
public void onRouteAdded(MediaRoute2Info routeInfo) {
if (mSelectedRoute == null) {
mSelectedRoute = routeInfo;
mManager.selectRoute(uid, mSelectedRoute);
}
}
@Override
public void onRouteSelected(int uid, MediaRoute2Info route) {
if (mSelectedRoute != null && route != null
&& TextUtils.equals(route.getId(), mSelectedRoute.getId())) {
latch.countDown();
}
}
@Override
public void onControlCategoriesChanged(int uid, List<String> categories) {
}
};
mManager.addCallback(mExecutor, managerCallback);
Assert.assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
mManager.removeCallback(managerCallback);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.server.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -98,6 +99,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
}
}
@Nullable
public MediaRoute2ProviderInfo getProviderInfo() {
return mProviderInfo;
}

View File

@@ -16,6 +16,8 @@
package com.android.server.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -32,6 +34,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -167,12 +170,24 @@ class MediaRouter2ServiceImpl {
}
}
public void setRemoteRoute(@NonNull IMediaRouter2Manager manager,
int uid, @Nullable String routeId, boolean explicit) {
public void selectRoute2(@NonNull IMediaRouter2Client client,
@Nullable MediaRoute2Info route) {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
setRemoteRouteLocked(manager, uid, routeId, explicit);
selectRoute2Locked(client, route);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
public void selectClientRoute2(@NonNull IMediaRouter2Manager manager,
int clientUid, @Nullable MediaRoute2Info route) {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
selectClientRoute2Locked(manager, clientUid, route);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -252,6 +267,53 @@ class MediaRouter2ServiceImpl {
}
}
private void selectRoute2Locked(IMediaRouter2Client client,
MediaRoute2Info route) {
ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
if (clientRecord != null) {
MediaRoute2Info oldRoute = clientRecord.mSelectedRoute;
clientRecord.mSelectedRoute = route;
UserHandler handler = clientRecord.mUserRecord.mHandler;
//TODO: Handle transfer instead of unselect and select
if (oldRoute != null) {
handler.sendMessage(
obtainMessage(UserHandler::unselectRoute, handler, clientRecord,
oldRoute));
}
if (route != null) {
handler.sendMessage(
obtainMessage(UserHandler::selectRoute, handler, clientRecord, route));
}
handler.sendMessage(
obtainMessage(UserHandler::updateClientUsage, handler, clientRecord));
}
}
private void setControlCategoriesLocked(IMediaRouter2Client client, List<String> categories) {
final IBinder binder = client.asBinder();
ClientRecord clientRecord = mAllClientRecords.get(binder);
if (clientRecord != null) {
clientRecord.mControlCategories = categories;
clientRecord.mUserRecord.mHandler.obtainMessage(
UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
}
}
private void sendControlRequestLocked(IMediaRouter2Client client, MediaRoute2Info route,
Intent request) {
final IBinder binder = client.asBinder();
ClientRecord clientRecord = mAllClientRecords.get(binder);
if (clientRecord != null) {
Pair<MediaRoute2Info, Intent> obj = new Pair<>(route, request);
clientRecord.mUserRecord.mHandler.obtainMessage(
UserHandler.MSG_SEND_CONTROL_REQUEST, obj).sendToTarget();
}
}
private void registerManagerLocked(IMediaRouter2Manager manager,
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = manager.asBinder();
@@ -301,38 +363,17 @@ class MediaRouter2ServiceImpl {
}
}
private void setRemoteRouteLocked(IMediaRouter2Manager manager,
int uid, String routeId, boolean explicit) {
private void selectClientRoute2Locked(IMediaRouter2Manager manager,
int clientUid, MediaRoute2Info route) {
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
if (managerRecord != null) {
if (explicit && managerRecord.mTrusted) {
Pair<Integer, String> obj = new Pair<>(uid, routeId);
managerRecord.mUserRecord.mHandler.obtainMessage(
UserHandler.MSG_SELECT_REMOTE_ROUTE, obj).sendToTarget();
ClientRecord clientRecord = managerRecord.mUserRecord.findClientRecordByUid(clientUid);
if (clientRecord == null) {
Slog.w(TAG, "Ignoring route selection for unknown client.");
}
if (clientRecord != null && managerRecord.mTrusted) {
selectRoute2Locked(clientRecord.mClient, route);
}
}
}
private void setControlCategoriesLocked(IMediaRouter2Client client, List<String> categories) {
final IBinder binder = client.asBinder();
ClientRecord clientRecord = mAllClientRecords.get(binder);
if (clientRecord != null) {
clientRecord.mControlCategories = categories;
clientRecord.mUserRecord.mHandler.obtainMessage(
UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
}
}
private void sendControlRequestLocked(IMediaRouter2Client client, MediaRoute2Info route,
Intent request) {
final IBinder binder = client.asBinder();
ClientRecord clientRecord = mAllClientRecords.get(binder);
if (clientRecord != null) {
Pair<MediaRoute2Info, Intent> obj = new Pair<>(route, request);
clientRecord.mUserRecord.mHandler.obtainMessage(
UserHandler.MSG_SEND_CONTROL_REQUEST, obj).sendToTarget();
}
}
@@ -371,6 +412,13 @@ class MediaRouter2ServiceImpl {
mUserId = userId;
mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this);
}
ClientRecord findClientRecordByUid(int uid) {
for (ClientRecord clientRecord : mClientRecords) {
if (clientRecord.mUid == uid) return clientRecord;
}
return null;
}
}
final class ClientRecord implements IBinder.DeathRecipient {
@@ -381,6 +429,7 @@ class MediaRouter2ServiceImpl {
public final String mPackageName;
public final boolean mTrusted;
public List<String> mControlCategories;
public MediaRoute2Info mSelectedRoute;
ClientRecord(UserRecord userRecord, IMediaRouter2Client client,
int uid, int pid, String packageName, boolean trusted) {
@@ -447,20 +496,20 @@ class MediaRouter2ServiceImpl {
MediaRoute2ProviderWatcher.Callback,
MediaRoute2ProviderProxy.Callback {
//TODO: Should be rearranged
public static final int MSG_START = 1;
public static final int MSG_STOP = 2;
//TODO: Use PooledLambda instead.
static final int MSG_START = 1;
static final int MSG_STOP = 2;
private static final int MSG_SELECT_REMOTE_ROUTE = 10;
private static final int MSG_UPDATE_CLIENT_USAGE = 11;
private static final int MSG_UPDATE_MANAGER_STATE = 12;
private static final int MSG_SEND_CONTROL_REQUEST = 13;
static final int MSG_UPDATE_CLIENT_USAGE = 11;
static final int MSG_UPDATE_MANAGER_STATE = 12;
static final int MSG_SEND_CONTROL_REQUEST = 13;
private final WeakReference<MediaRouter2ServiceImpl> mServiceRef;
private final UserRecord mUserRecord;
private final MediaRoute2ProviderWatcher mWatcher;
private final ArrayList<IMediaRouter2Manager> mTempManagers = new ArrayList<>();
//TODO: Make this thread-safe.
private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders =
new ArrayList<>();
@@ -486,11 +535,6 @@ class MediaRouter2ServiceImpl {
stop();
break;
}
case MSG_SELECT_REMOTE_ROUTE: {
Pair<Integer, String> obj = (Pair<Integer, String>) msg.obj;
selectRemoteRoute(obj.first, obj.second);
break;
}
case MSG_UPDATE_CLIENT_USAGE: {
updateClientUsage((ClientRecord) msg.obj);
break;
@@ -541,22 +585,28 @@ class MediaRouter2ServiceImpl {
scheduleUpdateManagerState();
}
private void unselectRoute(ClientRecord clientRecord, MediaRoute2Info route) {
if (route != null) {
MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
if (provider != null) {
provider.setSelectedRoute(clientRecord.mUid, null);
}
}
}
private void selectRemoteRoute(int uid, String routeId) {
if (routeId != null) {
final int providerCount = mMediaProviders.size();
//TODO: should find proper provider (currently assumes a single provider)
for (int i = 0; i < providerCount; i++) {
mMediaProviders.get(i).setSelectedRoute(uid, routeId);
private void selectRoute(ClientRecord clientRecord, MediaRoute2Info route) {
if (route != null) {
MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
if (provider != null) {
provider.setSelectedRoute(clientRecord.mUid, route.getId());
}
}
}
private void sendControlRequest(MediaRoute2Info route, Intent request) {
final int providerCount = mMediaProviders.size();
for (int i = 0; i < providerCount; i++) {
mMediaProviders.get(i).sendControlRequest(route, request);
final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
if (provider != null) {
provider.sendControlRequest(route, request);
}
}
@@ -576,10 +626,9 @@ class MediaRouter2ServiceImpl {
}
//TODO: Consider using a member variable (like mTempManagers).
final List<MediaRoute2ProviderInfo> providers = new ArrayList<>();
final int mediaCount = mMediaProviders.size();
for (int i = 0; i < mediaCount; i++) {
for (MediaRoute2ProviderProxy mediaProvider : mMediaProviders) {
final MediaRoute2ProviderInfo providerInfo =
mMediaProviders.get(i).getProviderInfo();
mediaProvider.getProviderInfo();
if (providerInfo == null || !providerInfo.isValid()) {
Log.w(TAG, "Ignoring invalid provider info : " + providerInfo);
} else {
@@ -594,16 +643,11 @@ class MediaRouter2ServiceImpl {
mTempManagers.add(mUserRecord.mManagerRecords.get(i).mManager);
}
}
//TODO: Call !proper callbacks when provider descriptor is implemented.
if (!providers.isEmpty()) {
final int count = mTempManagers.size();
for (int i = 0; i < count; i++) {
try {
mTempManagers.get(i).notifyProviderInfosUpdated(providers);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to call onStateChanged. Manager probably died.",
ex);
}
for (IMediaRouter2Manager tempManager : mTempManagers) {
try {
tempManager.notifyProviderInfosUpdated(providers);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update manager state. Manager probably died.", ex);
}
}
} finally {
@@ -624,16 +668,26 @@ class MediaRouter2ServiceImpl {
managers.add(mUserRecord.mManagerRecords.get(i).mManager);
}
}
final int count = managers.size();
for (int i = 0; i < count; i++) {
for (IMediaRouter2Manager manager : managers) {
try {
managers.get(i).notifyControlCategoriesChanged(clientRecord.mUid,
manager.notifyRouteSelected(clientRecord.mUid, clientRecord.mSelectedRoute);
manager.notifyControlCategoriesChanged(clientRecord.mUid,
clientRecord.mControlCategories);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to call onControlCategoriesChanged. "
+ "Manager probably died.", ex);
Slog.w(TAG, "Failed to update client usage. Manager probably died.", ex);
}
}
}
private MediaRoute2ProviderProxy findProvider(String providerId) {
for (MediaRoute2ProviderProxy provider : mMediaProviders) {
final MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
if (providerInfo != null
&& TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
return provider;
}
}
return null;
}
}
}

View File

@@ -449,6 +449,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub
mService2.unregisterClient(client);
}
// Binder call
@Override
public void selectRoute2(IMediaRouter2Client client, MediaRoute2Info route) {
mService2.selectRoute2(client, route);
}
// Binder call
@Override
public void sendControlRequest(IMediaRouter2Client client, MediaRoute2Info route,
@@ -475,9 +481,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
public void setRemoteRoute(IMediaRouter2Manager manager,
int uid, String routeId, boolean explicit) {
mService2.setRemoteRoute(manager, uid, routeId, explicit);
public void selectClientRoute2(IMediaRouter2Manager manager,
int clientUid, MediaRoute2Info route) {
mService2.selectClientRoute2(manager, clientUid, route);
}
// Binder call