summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author shaoweishen <shaoweishen@google.com> 2022-11-14 06:15:27 +0000
committer Shaowei Shen <shaoweishen@google.com> 2022-12-30 03:53:28 +0000
commitdc01faf80abb2a6960eed1fb90bcf6e5201fa508 (patch)
tree8f802413d3b3a629be6ddf1e51df3ede540b8489
parent287a6b7e3aca7d17f65a6b0aabf16dd43e059990 (diff)
[Output Switcher] Deal with deplicated id and route preference
1. When contruct devices list, filtered duplicated devices by going through all devices and remove extra devices shared with same duplicated ids. 2. Get preference route list and order routes. 3. move suggested devices to the top in preference list before using it. reference : go/output-switcher-device-organization Bug: 257851968 Test: make RunSettingsLibRoboTests -j40 Change-Id: I38035240793ea67fc6b4f1ad886e8beee1e7464a
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java86
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java154
2 files changed, 237 insertions, 3 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 7353cc0393c5..e112915cecd7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -41,11 +41,13 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.DoNotInline;
import androidx.annotation.RequiresApi;
import com.android.internal.annotations.VisibleForTesting;
@@ -53,9 +55,13 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
/**
* InfoMediaManager provide interface to get InfoMediaDevice list.
@@ -448,10 +454,12 @@ public class InfoMediaManager extends MediaManager {
}
private synchronized List<MediaRoute2Info> getAvailableRoutes(String packageName) {
- final List<MediaRoute2Info> infos = new ArrayList<>();
+ List<MediaRoute2Info> infos = new ArrayList<>();
RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
+ List<MediaRoute2Info> selectedRouteInfos = new ArrayList<>();
if (routingSessionInfo != null) {
- infos.addAll(mRouterManager.getSelectedRoutes(routingSessionInfo));
+ selectedRouteInfos = mRouterManager.getSelectedRoutes(routingSessionInfo);
+ infos.addAll(selectedRouteInfos);
infos.addAll(mRouterManager.getSelectableRoutes(routingSessionInfo));
}
final List<MediaRoute2Info> transferableRoutes =
@@ -468,11 +476,26 @@ public class InfoMediaManager extends MediaManager {
infos.add(transferableRoute);
}
}
- return infos;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ RouteListingPreference routeListingPreference =
+ mRouterManager.getRouteListingPreference(mPackageName);
+ if (routeListingPreference != null) {
+ final List<RouteListingPreference.Item> preferenceRouteListing =
+ Api34Impl.composePreferenceRouteListing(
+ routeListingPreference);
+ infos = Api34Impl.arrangeRouteListByPreference(selectedRouteInfos,
+ infos,
+ preferenceRouteListing);
+ }
+ return Api34Impl.filterDuplicatedIds(infos);
+ } else {
+ return infos;
+ }
}
@VisibleForTesting
void addMediaDevice(MediaRoute2Info route) {
+ //TODO(b/258141461): Attach flag and disable reason in MediaDevice
final int deviceType = route.getType();
MediaDevice mediaDevice = null;
switch (deviceType) {
@@ -574,4 +597,61 @@ public class InfoMediaManager extends MediaManager {
refreshDevices();
}
}
+
+ @RequiresApi(34)
+ private static class Api34Impl {
+ @DoNotInline
+ static List<RouteListingPreference.Item> composePreferenceRouteListing(
+ RouteListingPreference routeListingPreference) {
+ List<RouteListingPreference.Item> finalizedItemList = new ArrayList<>();
+ List<RouteListingPreference.Item> itemList = routeListingPreference.getItems();
+ for (RouteListingPreference.Item item : itemList) {
+ //Put suggested devices on the top first before further organization
+ if (item.getFlags() == RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE) {
+ finalizedItemList.add(0, item);
+ } else {
+ finalizedItemList.add(item);
+ }
+ }
+ return finalizedItemList;
+ }
+
+
+ @DoNotInline
+ static synchronized List<MediaRoute2Info> filterDuplicatedIds(List<MediaRoute2Info> infos) {
+ List<MediaRoute2Info> filteredInfos = new ArrayList<>();
+ Set<String> foundDeduplicationIds = new HashSet<>();
+ for (MediaRoute2Info mediaRoute2Info : infos) {
+ if (!Collections.disjoint(mediaRoute2Info.getDeduplicationIds(),
+ foundDeduplicationIds)) {
+ continue;
+ }
+ filteredInfos.add(mediaRoute2Info);
+ foundDeduplicationIds.addAll(mediaRoute2Info.getDeduplicationIds());
+ }
+ return filteredInfos;
+ }
+
+ @DoNotInline
+ static List<MediaRoute2Info> arrangeRouteListByPreference(
+ List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist,
+ List<RouteListingPreference.Item> preferenceRouteListing) {
+ final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos);
+ for (RouteListingPreference.Item item : preferenceRouteListing) {
+ for (MediaRoute2Info info : infolist) {
+ if (item.getRouteId().equals(info.getId())
+ && !selectedRouteInfos.contains(info)) {
+ sortedInfoList.add(info);
+ break;
+ }
+ }
+ }
+ if (sortedInfoList.size() != infolist.size()) {
+ infolist.removeAll(sortedInfoList);
+ sortedInfoList.addAll(infolist.stream().filter(
+ MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
+ }
+ return sortedInfoList;
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 33fb91d2252c..2820a132162b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -19,6 +19,7 @@ package com.android.settingslib.media;
import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR;
@@ -37,14 +38,18 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import android.media.session.MediaSessionManager;
+import android.os.Build;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,9 +58,11 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowRouter2Manager.class})
@@ -63,7 +70,15 @@ public class InfoMediaManagerTest {
private static final String TEST_PACKAGE_NAME = "com.test.packagename";
private static final String TEST_ID = "test_id";
+ private static final String TEST_ID_1 = "test_id_1";
+ private static final String TEST_ID_2 = "test_id_2";
+ private static final String TEST_ID_3 = "test_id_3";
+ private static final String TEST_ID_4 = "test_id_4";
+
private static final String TEST_NAME = "test_name";
+ private static final String TEST_DUPLICATED_ID_1 = "test_duplicated_id_1";
+ private static final String TEST_DUPLICATED_ID_2 = "test_duplicated_id_2";
+ private static final String TEST_DUPLICATED_ID_3 = "test_duplicated_id_3";
@Mock
private MediaRouter2Manager mRouterManager;
@@ -104,6 +119,7 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.getDeduplicationIds()).thenReturn(Set.of());
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -155,6 +171,7 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.getDeduplicationIds()).thenReturn(Set.of());
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -191,6 +208,7 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.getDeduplicationIds()).thenReturn(Set.of());
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -208,11 +226,104 @@ public class InfoMediaManagerTest {
}
@Test
+ public void onRoutesChanged_getAvailableRoutes_shouldFilterDevice() {
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+ final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
+ routingSessionInfos.add(sessionInfo);
+
+ final List<String> selectedRoutes = new ArrayList<>();
+ selectedRoutes.add(TEST_ID);
+ when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
+ mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+
+ mShadowRouter2Manager.setTransferableRoutes(getRoutesListWithDuplicatedIds());
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(2);
+ }
+
+ @Test
+ public void onRouteChanged_getAvailableRoutesWithPrefernceListExit_ordersRoutes() {
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT",
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>();
+ RouteListingPreference.Item item1 = new RouteListingPreference.Item.Builder(
+ TEST_ID_4).build();
+ RouteListingPreference.Item item2 = new RouteListingPreference.Item.Builder(
+ TEST_ID_3).build();
+ preferenceItemList.add(item1);
+ preferenceItemList.add(item2);
+ when(mRouterManager.getRouteListingPreference(TEST_PACKAGE_NAME))
+ .thenReturn(new RouteListingPreference.Builder().setItems(
+ preferenceItemList).setUseSystemOrdering(false).build());
+
+ final List<MediaRoute2Info> selectedRoutes = new ArrayList<>();
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
+ selectedRoutes.add(info);
+ when(mRouterManager.getSelectedRoutes(any())).thenReturn(selectedRoutes);
+
+ final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+ final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
+ routingSessionInfos.add(sessionInfo);
+
+ when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos);
+ when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID));
+
+ setTransferableRoutesList();
+
+ mInfoMediaManager.mRouterManager = mRouterManager;
+
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
+
+ assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
+ }
+
+ private List<MediaRoute2Info> setTransferableRoutesList() {
+ final List<MediaRoute2Info> transferableRoutes = new ArrayList<>();
+ final MediaRoute2Info transferableInfo1 = mock(MediaRoute2Info.class);
+ when(transferableInfo1.getId()).thenReturn(TEST_ID_2);
+ when(transferableInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(transferableInfo1.getType()).thenReturn(TYPE_REMOTE_TV);
+ transferableRoutes.add(transferableInfo1);
+
+ final MediaRoute2Info transferableInfo2 = mock(MediaRoute2Info.class);
+ when(transferableInfo2.getId()).thenReturn(TEST_ID_3);
+ when(transferableInfo2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ transferableRoutes.add(transferableInfo2);
+
+ final MediaRoute2Info transferableInfo3 = mock(MediaRoute2Info.class);
+ when(transferableInfo3.getId()).thenReturn(TEST_ID_4);
+ when(transferableInfo3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ transferableRoutes.add(transferableInfo3);
+
+ when(mRouterManager.getTransferableRoutes(TEST_PACKAGE_NAME)).thenReturn(
+ transferableRoutes);
+
+ return transferableRoutes;
+ }
+
+ @Test
public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
when(info.isSystemRoute()).thenReturn(true);
+ when(info.getDeduplicationIds()).thenReturn(Set.of());
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -229,6 +340,47 @@ public class InfoMediaManagerTest {
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
}
+ private List<MediaRoute2Info> getRoutesListWithDuplicatedIds() {
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
+ when(info.getDeduplicationIds()).thenReturn(
+ Set.of(TEST_DUPLICATED_ID_1, TEST_DUPLICATED_ID_2));
+ routes.add(info);
+
+ final MediaRoute2Info info1 = mock(MediaRoute2Info.class);
+ when(info1.getId()).thenReturn(TEST_ID_1);
+ when(info1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info1.isSystemRoute()).thenReturn(true);
+ when(info1.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_3));
+ routes.add(info1);
+
+ final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
+ when(info2.getId()).thenReturn(TEST_ID_2);
+ when(info2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info2.isSystemRoute()).thenReturn(true);
+ when(info2.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_3));
+ routes.add(info2);
+
+ final MediaRoute2Info info3 = mock(MediaRoute2Info.class);
+ when(info3.getId()).thenReturn(TEST_ID_3);
+ when(info3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info3.isSystemRoute()).thenReturn(true);
+ when(info3.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_1));
+ routes.add(info3);
+
+ final MediaRoute2Info info4 = mock(MediaRoute2Info.class);
+ when(info4.getId()).thenReturn(TEST_ID_4);
+ when(info4.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info4.isSystemRoute()).thenReturn(true);
+ when(info4.getDeduplicationIds()).thenReturn(Set.of(TEST_DUPLICATED_ID_2));
+ routes.add(info4);
+
+ return routes;
+ }
+
@Test
public void connectDeviceWithoutPackageName_noSession_returnFalse() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
@@ -255,6 +407,7 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.getDeduplicationIds()).thenReturn(Set.of());
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -620,6 +773,7 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
mInfoMediaManager.registerCallback(mCallback);
+ when(info.getDeduplicationIds()).thenReturn(Set.of());
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);