Merge "MediaRouter: Make MR2M.CB.onRouteSelected to be called"
This commit is contained in:
committed by
Android (Google) Code Review
commit
49fb0e7fe8
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user