diff options
| author | 2022-11-14 06:15:27 +0000 | |
|---|---|---|
| committer | 2022-12-30 03:53:28 +0000 | |
| commit | dc01faf80abb2a6960eed1fb90bcf6e5201fa508 (patch) | |
| tree | 8f802413d3b3a629be6ddf1e51df3ede540b8489 | |
| parent | 287a6b7e3aca7d17f65a6b0aabf16dd43e059990 (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.java | 86 | ||||
| -rw-r--r-- | packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java | 154 |
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); |