Loading services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java +77 −12 Original line number Diff line number Diff line Loading @@ -62,7 +62,11 @@ import java.util.Objects; private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); private int mDeviceVolume; @NonNull private MediaRoute2Info mDeviceRoute; @Nullable private MediaRoute2Info mSelectedRoute; @VisibleForTesting /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context, Loading Loading @@ -91,14 +95,26 @@ import java.util.Objects; } @Override public boolean selectRoute(@Nullable Integer type) { // No-op as the controller does not support selection from the outside of the class. public synchronized boolean selectRoute(@Nullable Integer type) { if (type == null) { mSelectedRoute = null; return true; } if (!isDeviceRouteType(type)) { return false; } mSelectedRoute = createRouteFromAudioInfo(type); return true; } @Override @NonNull public synchronized MediaRoute2Info getDeviceRoute() { if (mSelectedRoute != null) { return mSelectedRoute; } return mDeviceRoute; } Loading @@ -109,6 +125,13 @@ import java.util.Objects; } mDeviceVolume = volume; if (mSelectedRoute != null) { mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute) .setVolume(volume) .build(); } mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(volume) .build(); Loading @@ -116,29 +139,47 @@ import java.util.Objects; return true; } @NonNull private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; int type = TYPE_BUILTIN_SPEAKER; if (newRoutes != null) { if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { type = TYPE_WIRED_HEADPHONES; name = R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { type = TYPE_WIRED_HEADSET; name = R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { type = TYPE_DOCK; name = R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { type = TYPE_HDMI; name = R.string.default_audio_route_name_external_device; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { type = TYPE_USB_DEVICE; name = R.string.default_audio_route_name_usb; } } return createRouteFromAudioInfo(type); } @NonNull private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) { int name = R.string.default_audio_route_name; switch (type) { case TYPE_WIRED_HEADPHONES: case TYPE_WIRED_HEADSET: name = R.string.default_audio_route_name_headphones; break; case TYPE_DOCK: name = R.string.default_audio_route_name_dock_speakers; break; case TYPE_HDMI: name = R.string.default_audio_route_name_external_device; break; case TYPE_USB_DEVICE: name = R.string.default_audio_route_name_usb; break; } synchronized (this) { return new MediaRoute2Info.Builder( DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) Loading @@ -156,19 +197,43 @@ import java.util.Objects; } } private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) { mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); /** * Checks if the given type is a device route. * * <p>Device route means a route which is either built-in or wired to the current device. * * @param type specifies the type of the device. * @return {@code true} if the device is wired or built-in and {@code false} otherwise. */ private boolean isDeviceRouteType(@MediaRoute2Info.Type int type) { switch (type) { case TYPE_BUILTIN_SPEAKER: case TYPE_WIRED_HEADPHONES: case TYPE_WIRED_HEADSET: case TYPE_DOCK: case TYPE_HDMI: case TYPE_USB_DEVICE: return true; default: return false; } } private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { @Override public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) { boolean isDeviceRouteChanged; MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes); synchronized (AudioPoliciesDeviceRouteController.this) { mDeviceRoute = deviceRoute; isDeviceRouteChanged = mSelectedRoute == null; } if (isDeviceRouteChanged) { mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); } notifyDeviceRouteUpdate(deviceRoute); } } Loading services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java 0 → 100644 +247 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.media; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.IAudioRoutesObserver; import android.media.MediaRoute2Info; import android.os.RemoteException; import com.android.internal.R; import com.android.server.audio.AudioService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(JUnit4.class) public class AudioPoliciesDeviceRouteControllerTest { private static final String ROUTE_NAME_DEFAULT = "default"; private static final String ROUTE_NAME_DOCK = "dock"; private static final String ROUTE_NAME_HEADPHONES = "headphones"; private static final int VOLUME_SAMPLE_1 = 25; @Mock private Context mContext; @Mock private Resources mResources; @Mock private AudioManager mAudioManager; @Mock private AudioService mAudioService; @Mock private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; @Captor private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor; private AudioPoliciesDeviceRouteController mController; private IAudioRoutesObserver.Stub mAudioRoutesObserver; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getResources()).thenReturn(mResources); when(mResources.getText(anyInt())).thenReturn(ROUTE_NAME_DEFAULT); // Setting built-in speaker as default speaker. AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER; when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture())) .thenReturn(audioRoutesInfo); mController = new AudioPoliciesDeviceRouteController( mContext, mAudioManager, mAudioService, mOnDeviceRouteChangedListener); mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue(); } @Test public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() { MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); } @Test public void getDeviceRoute_audioRouteHasChanged_returnsRouteFromAudioService() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @Test public void getDeviceRoute_selectDevice_returnsSelectedRoute() { when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); } @Test public void getDeviceRoute_hasSelectedAndAudioServiceRoutes_returnsSelectedRoute() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); } @Test public void getDeviceRoute_unselectRoute_returnsAudioServiceRoute() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.selectRoute(null); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @Test public void getDeviceRoute_selectRouteFails_returnsAudioServiceRoute() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @Test public void selectRoute_selectWiredRoute_returnsTrue() { assertThat(mController.selectRoute(MediaRoute2Info.TYPE_HDMI)).isTrue(); } @Test public void selectRoute_selectBluetoothRoute_returnsFalse() { assertThat(mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)).isFalse(); } @Test public void selectRoute_unselectRoute_returnsTrue() { assertThat(mController.selectRoute(null)).isTrue(); } @Test public void updateVolume_noSelectedRoute_deviceRouteVolumeChanged() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.updateVolume(VOLUME_SAMPLE_1); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1); } @Test public void updateVolume_connectSelectedRouteLater_selectedRouteVolumeChanged() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.updateVolume(VOLUME_SAMPLE_1); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1); } /** * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)} * from {@link AudioService}. This happens when there is a wired route change, * like a wired headset being connected. * * @param audioRoutesInfo updated state of connected wired device */ private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) { try { // this is a captured observer implementation // from WiredRoutesController's AudioService#startWatchingRoutes call mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo); } catch (RemoteException exception) { // Should not happen since the object is mocked. assertWithMessage("An unexpected RemoteException happened.").fail(); } } } Loading
services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java +77 −12 Original line number Diff line number Diff line Loading @@ -62,7 +62,11 @@ import java.util.Objects; private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); private int mDeviceVolume; @NonNull private MediaRoute2Info mDeviceRoute; @Nullable private MediaRoute2Info mSelectedRoute; @VisibleForTesting /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context, Loading Loading @@ -91,14 +95,26 @@ import java.util.Objects; } @Override public boolean selectRoute(@Nullable Integer type) { // No-op as the controller does not support selection from the outside of the class. public synchronized boolean selectRoute(@Nullable Integer type) { if (type == null) { mSelectedRoute = null; return true; } if (!isDeviceRouteType(type)) { return false; } mSelectedRoute = createRouteFromAudioInfo(type); return true; } @Override @NonNull public synchronized MediaRoute2Info getDeviceRoute() { if (mSelectedRoute != null) { return mSelectedRoute; } return mDeviceRoute; } Loading @@ -109,6 +125,13 @@ import java.util.Objects; } mDeviceVolume = volume; if (mSelectedRoute != null) { mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute) .setVolume(volume) .build(); } mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(volume) .build(); Loading @@ -116,29 +139,47 @@ import java.util.Objects; return true; } @NonNull private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; int type = TYPE_BUILTIN_SPEAKER; if (newRoutes != null) { if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { type = TYPE_WIRED_HEADPHONES; name = R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { type = TYPE_WIRED_HEADSET; name = R.string.default_audio_route_name_headphones; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { type = TYPE_DOCK; name = R.string.default_audio_route_name_dock_speakers; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { type = TYPE_HDMI; name = R.string.default_audio_route_name_external_device; } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { type = TYPE_USB_DEVICE; name = R.string.default_audio_route_name_usb; } } return createRouteFromAudioInfo(type); } @NonNull private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) { int name = R.string.default_audio_route_name; switch (type) { case TYPE_WIRED_HEADPHONES: case TYPE_WIRED_HEADSET: name = R.string.default_audio_route_name_headphones; break; case TYPE_DOCK: name = R.string.default_audio_route_name_dock_speakers; break; case TYPE_HDMI: name = R.string.default_audio_route_name_external_device; break; case TYPE_USB_DEVICE: name = R.string.default_audio_route_name_usb; break; } synchronized (this) { return new MediaRoute2Info.Builder( DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) Loading @@ -156,19 +197,43 @@ import java.util.Objects; } } private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) { mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); /** * Checks if the given type is a device route. * * <p>Device route means a route which is either built-in or wired to the current device. * * @param type specifies the type of the device. * @return {@code true} if the device is wired or built-in and {@code false} otherwise. */ private boolean isDeviceRouteType(@MediaRoute2Info.Type int type) { switch (type) { case TYPE_BUILTIN_SPEAKER: case TYPE_WIRED_HEADPHONES: case TYPE_WIRED_HEADSET: case TYPE_DOCK: case TYPE_HDMI: case TYPE_USB_DEVICE: return true; default: return false; } } private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { @Override public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) { boolean isDeviceRouteChanged; MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes); synchronized (AudioPoliciesDeviceRouteController.this) { mDeviceRoute = deviceRoute; isDeviceRouteChanged = mSelectedRoute == null; } if (isDeviceRouteChanged) { mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); } notifyDeviceRouteUpdate(deviceRoute); } } Loading
services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java 0 → 100644 +247 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.media; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.IAudioRoutesObserver; import android.media.MediaRoute2Info; import android.os.RemoteException; import com.android.internal.R; import com.android.server.audio.AudioService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(JUnit4.class) public class AudioPoliciesDeviceRouteControllerTest { private static final String ROUTE_NAME_DEFAULT = "default"; private static final String ROUTE_NAME_DOCK = "dock"; private static final String ROUTE_NAME_HEADPHONES = "headphones"; private static final int VOLUME_SAMPLE_1 = 25; @Mock private Context mContext; @Mock private Resources mResources; @Mock private AudioManager mAudioManager; @Mock private AudioService mAudioService; @Mock private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; @Captor private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor; private AudioPoliciesDeviceRouteController mController; private IAudioRoutesObserver.Stub mAudioRoutesObserver; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getResources()).thenReturn(mResources); when(mResources.getText(anyInt())).thenReturn(ROUTE_NAME_DEFAULT); // Setting built-in speaker as default speaker. AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER; when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture())) .thenReturn(audioRoutesInfo); mController = new AudioPoliciesDeviceRouteController( mContext, mAudioManager, mAudioService, mOnDeviceRouteChangedListener); mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue(); } @Test public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() { MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); } @Test public void getDeviceRoute_audioRouteHasChanged_returnsRouteFromAudioService() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @Test public void getDeviceRoute_selectDevice_returnsSelectedRoute() { when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); } @Test public void getDeviceRoute_hasSelectedAndAudioServiceRoutes_returnsSelectedRoute() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); } @Test public void getDeviceRoute_unselectRoute_returnsAudioServiceRoute() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.selectRoute(null); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @Test public void getDeviceRoute_selectRouteFails_returnsAudioServiceRoute() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); } @Test public void selectRoute_selectWiredRoute_returnsTrue() { assertThat(mController.selectRoute(MediaRoute2Info.TYPE_HDMI)).isTrue(); } @Test public void selectRoute_selectBluetoothRoute_returnsFalse() { assertThat(mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)).isFalse(); } @Test public void selectRoute_unselectRoute_returnsTrue() { assertThat(mController.selectRoute(null)).isTrue(); } @Test public void updateVolume_noSelectedRoute_deviceRouteVolumeChanged() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.updateVolume(VOLUME_SAMPLE_1); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1); } @Test public void updateVolume_connectSelectedRouteLater_selectedRouteVolumeChanged() { when(mResources.getText(R.string.default_audio_route_name_headphones)) .thenReturn(ROUTE_NAME_HEADPHONES); when(mResources.getText(R.string.default_audio_route_name_dock_speakers)) .thenReturn(ROUTE_NAME_DOCK); AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; callAudioRoutesObserver(audioRoutesInfo); mController.updateVolume(VOLUME_SAMPLE_1); mController.selectRoute(MediaRoute2Info.TYPE_DOCK); MediaRoute2Info route2Info = mController.getDeviceRoute(); assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK); assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1); } /** * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)} * from {@link AudioService}. This happens when there is a wired route change, * like a wired headset being connected. * * @param audioRoutesInfo updated state of connected wired device */ private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) { try { // this is a captured observer implementation // from WiredRoutesController's AudioService#startWatchingRoutes call mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo); } catch (RemoteException exception) { // Should not happen since the object is mocked. assertWithMessage("An unexpected RemoteException happened.").fail(); } } }