Merge "MediaRouter2: Revise MediaRouter2.Callback.onRoute**"
This commit is contained in:
committed by
Android (Google) Code Review
commit
61094d7cd1
@@ -16,12 +16,14 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.MediaRoute2Info;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IMediaRouter2Client {
|
||||
void notifyRestoreRoute();
|
||||
void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers);
|
||||
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
|
||||
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
|
||||
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ public final class MediaRoute2Info implements Parcelable {
|
||||
List<String> mSupportedCategories;
|
||||
int mVolume;
|
||||
int mVolumeMax;
|
||||
int mVolumeHandling;
|
||||
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
|
||||
Bundle mExtras;
|
||||
|
||||
public Builder(@NonNull String id, @NonNull String name) {
|
||||
|
||||
@@ -33,7 +33,9 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -51,23 +53,23 @@ public class MediaRouter2 {
|
||||
@GuardedBy("sLock")
|
||||
private static MediaRouter2 sInstance;
|
||||
|
||||
private Context mContext;
|
||||
private final Context mContext;
|
||||
private final IMediaRouterService mMediaRouterService;
|
||||
|
||||
private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
|
||||
new CopyOnWriteArrayList<>();
|
||||
@GuardedBy("sLock")
|
||||
private List<String> mControlCategories = Collections.emptyList();
|
||||
|
||||
private final String mPackageName;
|
||||
private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
|
||||
|
||||
private volatile List<String> mControlCategories = Collections.emptyList();
|
||||
|
||||
private MediaRoute2Info mSelectedRoute;
|
||||
@GuardedBy("sLock")
|
||||
private Client mClient;
|
||||
|
||||
private final String mPackageName;
|
||||
final Handler mHandler;
|
||||
|
||||
List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList();
|
||||
volatile List<MediaRoute2Info> mRoutes = Collections.emptyList();
|
||||
|
||||
MediaRoute2Info mSelectedRoute;
|
||||
volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Gets an instance of the media router associated with the context.
|
||||
@@ -137,6 +139,7 @@ public class MediaRouter2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO: Is it thread-safe?
|
||||
record.notifyRoutes();
|
||||
|
||||
//TODO: Update discovery request here.
|
||||
@@ -181,31 +184,22 @@ public class MediaRouter2 {
|
||||
public void setControlCategories(@NonNull Collection<String> controlCategories) {
|
||||
Objects.requireNonNull(controlCategories, "control categories must not be null");
|
||||
|
||||
Client client;
|
||||
List<String> newControlCategories = new ArrayList<>(controlCategories);
|
||||
synchronized (sLock) {
|
||||
mControlCategories = newControlCategories;
|
||||
client = mClient;
|
||||
}
|
||||
if (client != null) {
|
||||
try {
|
||||
mMediaRouterService.setControlCategories2(client, newControlCategories);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to set control categories.", ex);
|
||||
}
|
||||
}
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::refreshAndNotifyRoutes, this));
|
||||
// To ensure invoking callbacks correctly according to control categories
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::setControlCategoriesOnHandler,
|
||||
MediaRouter2.this, new ArrayList<>(controlCategories)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the list of {@link MediaRoute2Info routes} currently known to the media router.
|
||||
* Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
|
||||
* known to the media router.
|
||||
*
|
||||
* @return the list of routes that support at least one of the control categories set by
|
||||
* the application
|
||||
*/
|
||||
@NonNull
|
||||
public List<MediaRoute2Info> getRoutes() {
|
||||
return mRoutes;
|
||||
return mFilteredRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,102 +320,145 @@ public class MediaRouter2 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void onProviderInfosUpdated(List<MediaRoute2ProviderInfo> providers) {
|
||||
if (providers == null) {
|
||||
Log.w(TAG, "Providers info is null.");
|
||||
return;
|
||||
}
|
||||
private void setControlCategoriesOnHandler(List<String> newControlCategories) {
|
||||
List<String> prevControlCategories = mControlCategories;
|
||||
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
|
||||
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
|
||||
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
|
||||
|
||||
mProviders = providers;
|
||||
refreshAndNotifyRoutes();
|
||||
}
|
||||
|
||||
void refreshAndNotifyRoutes() {
|
||||
ArrayList<MediaRoute2Info> routes = new ArrayList<>();
|
||||
|
||||
List<String> controlCategories;
|
||||
mControlCategories = newControlCategories;
|
||||
Client client;
|
||||
synchronized (sLock) {
|
||||
controlCategories = mControlCategories;
|
||||
client = mClient;
|
||||
}
|
||||
if (client != null) {
|
||||
try {
|
||||
mMediaRouterService.setControlCategories2(client, mControlCategories);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to set control categories.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
for (MediaRoute2ProviderInfo provider : mProviders) {
|
||||
updateProvider(provider, controlCategories, routes);
|
||||
for (MediaRoute2Info route : mRoutes.values()) {
|
||||
boolean preSupported = route.supportsControlCategory(prevControlCategories);
|
||||
boolean postSupported = route.supportsControlCategory(newControlCategories);
|
||||
if (postSupported) {
|
||||
filteredRoutes.add(route);
|
||||
}
|
||||
if (preSupported == postSupported) {
|
||||
continue;
|
||||
}
|
||||
if (preSupported) {
|
||||
removedRoutes.add(route);
|
||||
} else {
|
||||
addedRoutes.add(route);
|
||||
}
|
||||
}
|
||||
mFilteredRoutes = Collections.unmodifiableList(filteredRoutes);
|
||||
|
||||
//TODO: Can orders be changed?
|
||||
if (!Objects.equals(mRoutes, routes)) {
|
||||
mRoutes = Collections.unmodifiableList(routes);
|
||||
notifyRouteListChanged(mRoutes);
|
||||
if (removedRoutes.size() > 0) {
|
||||
notifyRoutesRemoved(removedRoutes);
|
||||
}
|
||||
if (addedRoutes.size() > 0) {
|
||||
notifyRoutesAdded(addedRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
void updateProvider(MediaRoute2ProviderInfo provider, List<String> controlCategories,
|
||||
List<MediaRoute2Info> outRoutes) {
|
||||
if (provider == null || !provider.isValid()) {
|
||||
Log.w(TAG, "Ignoring invalid provider : " + provider);
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<MediaRoute2Info> routes = provider.getRoutes();
|
||||
void addRoutesOnHandler(List<MediaRoute2Info> routes) {
|
||||
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
|
||||
for (MediaRoute2Info route : routes) {
|
||||
if (!route.isValid()) {
|
||||
Log.w(TAG, "Ignoring invalid route : " + route);
|
||||
continue;
|
||||
mRoutes.put(route.getUniqueId(), route);
|
||||
if (route.supportsControlCategory(mControlCategories)) {
|
||||
addedRoutes.add(route);
|
||||
}
|
||||
if (!route.supportsControlCategory(controlCategories)) {
|
||||
continue;
|
||||
}
|
||||
MediaRoute2Info preRoute = findRouteById(route.getId());
|
||||
if (!route.equals(preRoute)) {
|
||||
notifyRouteChanged(route);
|
||||
}
|
||||
outRoutes.add(route);
|
||||
}
|
||||
if (addedRoutes.size() > 0) {
|
||||
refreshFilteredRoutes();
|
||||
notifyRoutesAdded(addedRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
MediaRoute2Info findRouteById(String id) {
|
||||
for (MediaRoute2Info route : mRoutes) {
|
||||
if (route.getId().equals(id)) return route;
|
||||
void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
|
||||
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
|
||||
for (MediaRoute2Info route : routes) {
|
||||
mRoutes.remove(route.getUniqueId());
|
||||
if (route.supportsControlCategory(mControlCategories)) {
|
||||
removedRoutes.add(route);
|
||||
}
|
||||
}
|
||||
if (removedRoutes.size() > 0) {
|
||||
refreshFilteredRoutes();
|
||||
notifyRoutesRemoved(removedRoutes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void notifyRouteListChanged(List<MediaRoute2Info> routes) {
|
||||
void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
|
||||
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
|
||||
for (MediaRoute2Info route : routes) {
|
||||
mRoutes.put(route.getUniqueId(), route);
|
||||
if (route.supportsControlCategory(mControlCategories)) {
|
||||
changedRoutes.add(route);
|
||||
}
|
||||
}
|
||||
if (changedRoutes.size() > 0) {
|
||||
refreshFilteredRoutes();
|
||||
notifyRoutesChanged(changedRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshFilteredRoutes() {
|
||||
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
|
||||
|
||||
for (MediaRoute2Info route : mRoutes.values()) {
|
||||
if (route.supportsControlCategory(mControlCategories)) {
|
||||
filteredRoutes.add(route);
|
||||
}
|
||||
}
|
||||
mFilteredRoutes = Collections.unmodifiableList(filteredRoutes);
|
||||
}
|
||||
|
||||
private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRoutesAdded(routes));
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRoutesRemoved(routes));
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRoutesChanged(routes));
|
||||
}
|
||||
}
|
||||
|
||||
void notifyRouteChanged(MediaRoute2Info route) {
|
||||
for (CallbackRecord record: mCallbackRecords) {
|
||||
record.mExecutor.execute(
|
||||
() -> record.mCallback.onRouteChanged(route));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for receiving events about media routing changes.
|
||||
*/
|
||||
public static class Callback {
|
||||
//TODO: clean up these callbacks
|
||||
/**
|
||||
* Called when a route is added.
|
||||
*/
|
||||
public void onRouteAdded(MediaRoute2Info routeInfo) {}
|
||||
|
||||
/**
|
||||
* Called when a route is changed.
|
||||
* Called when routes are added.
|
||||
* @param routes the list of routes that have been added. It's never empty.
|
||||
*/
|
||||
public void onRouteChanged(MediaRoute2Info routeInfo) {}
|
||||
public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
|
||||
|
||||
/**
|
||||
* Called when a route is removed.
|
||||
* Called when routes are removed.
|
||||
* @param routes the list of routes that have been removed. It's never empty.
|
||||
*/
|
||||
public void onRouteRemoved(MediaRoute2Info routeInfo) {}
|
||||
public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
|
||||
|
||||
/**
|
||||
* Called when the list of routes is changed.
|
||||
* Called when routes are changed.
|
||||
* @param routes the list of routes that have been changed. It's never empty.
|
||||
*/
|
||||
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
|
||||
}
|
||||
@@ -436,11 +473,10 @@ public class MediaRouter2 {
|
||||
}
|
||||
|
||||
void notifyRoutes() {
|
||||
final List<MediaRoute2Info> routes = mRoutes;
|
||||
final List<MediaRoute2Info> routes = mFilteredRoutes;
|
||||
// notify only when bound to media router service.
|
||||
//TODO: Correct the condition when control category, default route, .. are finalized.
|
||||
if (routes.size() > 0) {
|
||||
mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
|
||||
mExecutor.execute(() -> mCallback.onRoutesAdded(routes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,9 +486,21 @@ public class MediaRouter2 {
|
||||
public void notifyRestoreRoute() throws RemoteException {}
|
||||
|
||||
@Override
|
||||
public void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> info) {
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::onProviderInfosUpdated,
|
||||
MediaRouter2.this, info));
|
||||
public void notifyRoutesAdded(List<MediaRoute2Info> routes) {
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::addRoutesOnHandler,
|
||||
MediaRouter2.this, routes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::removeRoutesOnHandler,
|
||||
MediaRouter2.this, routes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyRoutesChanged(List<MediaRoute2Info> routes) {
|
||||
mHandler.sendMessage(obtainMessage(MediaRouter2::changeRoutesOnHandler,
|
||||
MediaRouter2.this, routes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class MediaRouter2Manager {
|
||||
@NonNull
|
||||
List<MediaRoute2Info> mRoutes = Collections.emptyList();
|
||||
@NonNull
|
||||
ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>();
|
||||
final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Gets an instance of media router manager that controls media route of other applications.
|
||||
@@ -427,6 +427,8 @@ public class MediaRouter2Manager {
|
||||
* A client may refresh available routes for each application.
|
||||
*/
|
||||
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
|
||||
|
||||
//TODO: add onControlCategoriesChanged to notify available routes are changed
|
||||
}
|
||||
|
||||
final class CallbackRecord {
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
@@ -272,29 +273,28 @@ public class MediaRouterManagerTest {
|
||||
|
||||
@Test
|
||||
public void testControlVolumeWithRouter() throws Exception {
|
||||
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
|
||||
mRouter2.registerCallback(mExecutor, mockCallback);
|
||||
|
||||
MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
|
||||
int originalVolume = volRoute.getVolume();
|
||||
int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
|
||||
int targetVolume = originalVolume + deltaVolume;
|
||||
|
||||
mRouter2.requestSetVolume(volRoute, targetVolume);
|
||||
verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
.onRouteChanged(argThat(route ->
|
||||
route.getId().equals(volRoute.getId())
|
||||
&& route.getVolume() == targetVolume));
|
||||
CountDownLatch latch1 = new CountDownLatch(1);
|
||||
MediaRouter2.Callback callback1 =
|
||||
createVolumeChangeCallback(ROUTE_ID_VARIABLE_VOLUME,
|
||||
originalVolume + deltaVolume, latch1);
|
||||
mRouter2.registerCallback(mExecutor, callback1);
|
||||
mRouter2.requestUpdateVolume(volRoute, deltaVolume);
|
||||
assertTrue(latch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
mRouter2.unregisterCallback(callback1);
|
||||
|
||||
mRouter2.requestUpdateVolume(volRoute, -deltaVolume);
|
||||
verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
.onRouteChanged(argThat(route ->
|
||||
route.getId().equals(volRoute.getId())
|
||||
&& route.getVolume() == originalVolume));
|
||||
|
||||
mRouter2.unregisterCallback(mockCallback);
|
||||
CountDownLatch latch2 = new CountDownLatch(1);
|
||||
MediaRouter2.Callback callback2 =
|
||||
createVolumeChangeCallback(ROUTE_ID_VARIABLE_VOLUME, originalVolume, latch2);
|
||||
mRouter2.registerCallback(mExecutor, callback2);
|
||||
mRouter2.requestSetVolume(volRoute, originalVolume);
|
||||
assertTrue(latch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
mRouter2.unregisterCallback(callback1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -312,13 +312,13 @@ public class MediaRouterManagerTest {
|
||||
int targetVolume = originalVolume + deltaVolume;
|
||||
|
||||
mManager.requestSetVolume(volRoute, targetVolume);
|
||||
verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
verify(managerCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
.onRouteChanged(argThat(route ->
|
||||
route.getId().equals(volRoute.getId())
|
||||
&& route.getVolume() == targetVolume));
|
||||
|
||||
mManager.requestUpdateVolume(volRoute, -deltaVolume);
|
||||
verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
verify(managerCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
.onRouteChanged(argThat(route ->
|
||||
route.getId().equals(volRoute.getId())
|
||||
&& route.getVolume() == originalVolume));
|
||||
@@ -347,14 +347,14 @@ public class MediaRouterManagerTest {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
MediaRouter2.Callback callback = new MediaRouter2.Callback() {
|
||||
@Override
|
||||
public void onRoutesChanged(List<MediaRoute2Info> routes) {
|
||||
if (routes.size() > 0) latch.countDown();
|
||||
public void onRoutesAdded(List<MediaRoute2Info> added) {
|
||||
if (added.size() > 0) latch.countDown();
|
||||
}
|
||||
};
|
||||
mRouter2.setControlCategories(controlCategories);
|
||||
mRouter2.registerCallback(mExecutor, callback);
|
||||
try {
|
||||
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
return createRouteMap(mRouter2.getRoutes());
|
||||
} finally {
|
||||
mRouter2.unregisterCallback(callback);
|
||||
@@ -370,14 +370,18 @@ public class MediaRouterManagerTest {
|
||||
MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRoutesChanged(List<MediaRoute2Info> routes) {
|
||||
if (routes.size() > 0) latch.countDown();
|
||||
if (routes.size() > 0) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
mManager.registerCallback(mExecutor, managerCallback);
|
||||
mRouter2.setControlCategories(controlCategories);
|
||||
mRouter2.registerCallback(mExecutor, routerCallback);
|
||||
try {
|
||||
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
//TODO: currently this returns empty list occasionally.
|
||||
//Maybe due to control category is not set yet
|
||||
return createRouteMap(mManager.getAvailableRoutes(mPackageName));
|
||||
} finally {
|
||||
mRouter2.unregisterCallback(routerCallback);
|
||||
@@ -385,6 +389,20 @@ public class MediaRouterManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
MediaRouter2.Callback createVolumeChangeCallback(String routeId,
|
||||
int targetVolume, CountDownLatch latch) {
|
||||
MediaRouter2.Callback callback = new MediaRouter2.Callback() {
|
||||
@Override
|
||||
public void onRoutesChanged(List<MediaRoute2Info> changed) {
|
||||
MediaRoute2Info volRoute = createRouteMap(changed).get(routeId);
|
||||
if (volRoute != null && volRoute.getVolume() == targetVolume) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
|
||||
// Helper for getting routes easily
|
||||
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
|
||||
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
|
||||
|
||||
@@ -119,6 +119,11 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getUniqueId() {
|
||||
return mUniqueId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MediaRoute2ProviderInfo getProviderInfo() {
|
||||
return mProviderInfo;
|
||||
@@ -294,7 +299,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onProviderStateChanged(MediaRoute2ProviderProxy provider);
|
||||
void onProviderStateChanged(@NonNull MediaRoute2ProviderProxy provider);
|
||||
}
|
||||
|
||||
private final class Connection implements DeathRecipient {
|
||||
|
||||
@@ -47,9 +47,12 @@ import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* TODO: Merge this to MediaRouterService once it's finished.
|
||||
@@ -357,8 +360,7 @@ class MediaRouter2ServiceImpl {
|
||||
mAllClientRecords.put(binder, clientRecord);
|
||||
|
||||
userRecord.mHandler.sendMessage(
|
||||
obtainMessage(UserHandler::notifyProviderInfosUpdatedToClient,
|
||||
userRecord.mHandler, client));
|
||||
obtainMessage(UserHandler::notifyRoutesToClient, userRecord.mHandler, client));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,6 +588,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
final class UserRecord {
|
||||
public final int mUserId;
|
||||
//TODO: make records private for thread-safety
|
||||
final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
|
||||
final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
|
||||
final UserHandler mHandler;
|
||||
@@ -705,7 +708,7 @@ class MediaRouter2ServiceImpl {
|
||||
//TODO: Make this thread-safe.
|
||||
private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders =
|
||||
new ArrayList<>();
|
||||
private List<MediaRoute2ProviderInfo> mProviderInfos;
|
||||
private final List<MediaRoute2ProviderInfo> mProviderInfos = new ArrayList<>();
|
||||
|
||||
private boolean mRunning;
|
||||
private boolean mProviderInfosUpdateScheduled;
|
||||
@@ -745,14 +748,89 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderStateChanged(MediaRoute2ProviderProxy provider) {
|
||||
updateProvider(provider);
|
||||
public void onProviderStateChanged(@NonNull MediaRoute2ProviderProxy provider) {
|
||||
sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider));
|
||||
}
|
||||
|
||||
private void updateProvider(MediaRoute2ProviderProxy provider) {
|
||||
int providerIndex = getProviderInfoIndex(provider.getUniqueId());
|
||||
MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
|
||||
MediaRoute2ProviderInfo prevInfo =
|
||||
(providerIndex < 0) ? null : mProviderInfos.get(providerIndex);
|
||||
|
||||
if (Objects.equals(prevInfo, providerInfo)) return;
|
||||
|
||||
if (prevInfo == null) {
|
||||
mProviderInfos.add(providerInfo);
|
||||
Collection<MediaRoute2Info> addedRoutes = providerInfo.getRoutes();
|
||||
if (addedRoutes.size() > 0) {
|
||||
sendMessage(PooledLambda.obtainMessage(UserHandler::notifyRoutesAddedToClients,
|
||||
this, getClients(), new ArrayList<>(addedRoutes)));
|
||||
}
|
||||
} else if (providerInfo == null) {
|
||||
mProviderInfos.remove(prevInfo);
|
||||
Collection<MediaRoute2Info> removedRoutes = prevInfo.getRoutes();
|
||||
if (removedRoutes.size() > 0) {
|
||||
sendMessage(PooledLambda.obtainMessage(
|
||||
UserHandler::notifyRoutesRemovedToClients,
|
||||
this, getClients(), new ArrayList<>(removedRoutes)));
|
||||
}
|
||||
} else {
|
||||
mProviderInfos.set(providerIndex, providerInfo);
|
||||
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
|
||||
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
|
||||
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
|
||||
|
||||
final Collection<MediaRoute2Info> currentRoutes = providerInfo.getRoutes();
|
||||
final Set<String> updatedRouteIds = new HashSet<>();
|
||||
|
||||
for (MediaRoute2Info route : currentRoutes) {
|
||||
if (!route.isValid()) {
|
||||
Slog.w(TAG, "Ignoring invalid route : " + route);
|
||||
continue;
|
||||
}
|
||||
MediaRoute2Info prevRoute = prevInfo.getRoute(route.getId());
|
||||
|
||||
if (prevRoute != null) {
|
||||
if (!Objects.equals(prevRoute, route)) {
|
||||
changedRoutes.add(route);
|
||||
}
|
||||
updatedRouteIds.add(route.getId());
|
||||
} else {
|
||||
addedRoutes.add(route);
|
||||
}
|
||||
}
|
||||
|
||||
for (MediaRoute2Info prevRoute : prevInfo.getRoutes()) {
|
||||
if (!updatedRouteIds.contains(prevRoute.getId())) {
|
||||
removedRoutes.add(prevRoute);
|
||||
}
|
||||
}
|
||||
|
||||
List<IMediaRouter2Client> clients = getClients();
|
||||
if (addedRoutes.size() > 0) {
|
||||
notifyRoutesAddedToClients(clients, addedRoutes);
|
||||
}
|
||||
if (removedRoutes.size() > 0) {
|
||||
notifyRoutesRemovedToClients(clients, removedRoutes);
|
||||
}
|
||||
if (changedRoutes.size() > 0) {
|
||||
notifyRoutesChangedToClients(clients, changedRoutes);
|
||||
}
|
||||
}
|
||||
scheduleUpdateProviderInfos();
|
||||
}
|
||||
|
||||
private int getProviderInfoIndex(String providerId) {
|
||||
for (int i = 0; i < mProviderInfos.size(); i++) {
|
||||
MediaRoute2ProviderInfo providerInfo = mProviderInfos.get(i);
|
||||
if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void selectRoute(ClientRecord clientRecord, MediaRoute2Info route) {
|
||||
if (route != null) {
|
||||
MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
|
||||
@@ -803,6 +881,7 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: should be replaced into notifyRoutes...ToManagers
|
||||
private void updateProviderInfos() {
|
||||
mProviderInfosUpdateScheduled = false;
|
||||
|
||||
@@ -810,55 +889,83 @@ class MediaRouter2ServiceImpl {
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
final List<MediaRoute2ProviderInfo> providers = new ArrayList<>();
|
||||
for (MediaRoute2ProviderProxy mediaProvider : mMediaProviders) {
|
||||
final MediaRoute2ProviderInfo providerInfo =
|
||||
mediaProvider.getProviderInfo();
|
||||
if (providerInfo == null || !providerInfo.isValid()) {
|
||||
Slog.w(TAG, "Ignoring invalid provider info : " + providerInfo);
|
||||
} else {
|
||||
providers.add(providerInfo);
|
||||
}
|
||||
}
|
||||
mProviderInfos = providers;
|
||||
|
||||
final List<IMediaRouter2Manager> managers = new ArrayList<>();
|
||||
final List<IMediaRouter2Client> clients = new ArrayList<>();
|
||||
synchronized (service.mLock) {
|
||||
for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) {
|
||||
managers.add(managerRecord.mManager);
|
||||
}
|
||||
}
|
||||
for (IMediaRouter2Manager manager : managers) {
|
||||
notifyProviderInfosUpdatedToManager(manager);
|
||||
}
|
||||
}
|
||||
|
||||
private List<IMediaRouter2Client> getClients() {
|
||||
final List<IMediaRouter2Client> clients = new ArrayList<>();
|
||||
MediaRouter2ServiceImpl service = mServiceRef.get();
|
||||
if (service == null) {
|
||||
return clients;
|
||||
}
|
||||
synchronized (service.mLock) {
|
||||
for (ClientRecord clientRecord : mUserRecord.mClientRecords) {
|
||||
if (clientRecord instanceof Client2Record) {
|
||||
clients.add(((Client2Record) clientRecord).mClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IMediaRouter2Manager manager : managers) {
|
||||
notifyProviderInfosUpdatedToManager(manager);
|
||||
}
|
||||
for (IMediaRouter2Client client : clients) {
|
||||
notifyProviderInfosUpdatedToClient(client);
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
||||
private void notifyProviderInfosUpdatedToClient(IMediaRouter2Client client) {
|
||||
if (mProviderInfos == null) {
|
||||
scheduleUpdateProviderInfos();
|
||||
private void notifyRoutesToClient(IMediaRouter2Client client) {
|
||||
List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) {
|
||||
routes.addAll(providerInfo.getRoutes());
|
||||
}
|
||||
if (routes.size() == 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
client.notifyProviderInfosUpdated(mProviderInfos);
|
||||
client.notifyRoutesAdded(routes);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify provider infos updated. Client probably died.");
|
||||
Slog.w(TAG, "Failed to notify all routes. Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients,
|
||||
List<MediaRoute2Info> routes) {
|
||||
for (IMediaRouter2Client client : clients) {
|
||||
try {
|
||||
client.notifyRoutesAdded(routes);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify routes added. Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesRemovedToClients(List<IMediaRouter2Client> clients,
|
||||
List<MediaRoute2Info> routes) {
|
||||
for (IMediaRouter2Client client : clients) {
|
||||
try {
|
||||
client.notifyRoutesRemoved(routes);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify routes removed. Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRoutesChangedToClients(List<IMediaRouter2Client> clients,
|
||||
List<MediaRoute2Info> routes) {
|
||||
for (IMediaRouter2Client client : clients) {
|
||||
try {
|
||||
client.notifyRoutesChanged(routes);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to notify routes changed. Client probably died.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyProviderInfosUpdatedToManager(IMediaRouter2Manager manager) {
|
||||
if (mProviderInfos == null) {
|
||||
scheduleUpdateProviderInfos();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
manager.notifyProviderInfosUpdated(mProviderInfos);
|
||||
} catch (RemoteException ex) {
|
||||
@@ -891,9 +998,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
private MediaRoute2ProviderProxy findProvider(String providerId) {
|
||||
for (MediaRoute2ProviderProxy provider : mMediaProviders) {
|
||||
final MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
|
||||
if (providerInfo != null
|
||||
&& TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
|
||||
if (TextUtils.equals(provider.getUniqueId(), providerId)) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user