Merge "[Output Switcher] Deal with deplicated id and route preference"

This commit is contained in:
Shaowei Shen
2022-12-30 08:27:03 +00:00
committed by Android (Google) Code Review
2 changed files with 237 additions and 3 deletions

View File

@@ -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;
}
}
}

View File

@@ -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);
@@ -207,12 +225,105 @@ public class InfoMediaManagerTest {
assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
}
@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);