• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.sound;
18 
19 import static android.content.pm.PackageManager.FEATURE_PC;
20 import static android.media.AudioSystem.DEVICE_OUT_BLE_HEADSET;
21 import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
22 import static android.media.AudioSystem.DEVICE_OUT_EARPIECE;
23 import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
24 
25 import static com.android.settingslib.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING;
26 import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING;
27 
28 import static com.google.common.truth.Truth.assertThat;
29 
30 import static org.mockito.ArgumentMatchers.any;
31 import static org.mockito.Mockito.doReturn;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.spy;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.bluetooth.BluetoothAdapter;
39 import android.bluetooth.BluetoothDevice;
40 import android.bluetooth.BluetoothLeBroadcast;
41 import android.bluetooth.BluetoothManager;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.pm.ApplicationInfo;
45 import android.content.pm.PackageInfo;
46 import android.content.pm.PackageManager;
47 import android.content.pm.PackageStats;
48 import android.media.AudioAttributes;
49 import android.media.AudioManager;
50 import android.media.VolumeProvider;
51 import android.media.session.MediaController;
52 import android.media.session.MediaSessionManager;
53 import android.media.session.PlaybackState;
54 import android.platform.test.annotations.EnableFlags;
55 import android.platform.test.flag.junit.SetFlagsRule;
56 
57 import androidx.preference.Preference;
58 import androidx.preference.PreferenceManager;
59 import androidx.preference.PreferenceScreen;
60 
61 import com.android.media.flags.Flags;
62 import com.android.settings.R;
63 import com.android.settings.bluetooth.Utils;
64 import com.android.settings.media.MediaOutputUtils;
65 import com.android.settings.testutils.shadow.ShadowAudioManager;
66 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
67 import com.android.settingslib.bluetooth.A2dpProfile;
68 import com.android.settingslib.bluetooth.BluetoothEventManager;
69 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
70 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
71 import com.android.settingslib.bluetooth.HearingAidProfile;
72 import com.android.settingslib.bluetooth.LeAudioProfile;
73 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
74 import com.android.settingslib.bluetooth.LocalBluetoothManager;
75 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
76 import com.android.settingslib.media.MediaOutputConstants;
77 
78 import org.junit.After;
79 import org.junit.Before;
80 import org.junit.Rule;
81 import org.junit.Test;
82 import org.junit.runner.RunWith;
83 import org.mockito.ArgumentCaptor;
84 import org.mockito.Mock;
85 import org.mockito.MockitoAnnotations;
86 import org.robolectric.RobolectricTestRunner;
87 import org.robolectric.RuntimeEnvironment;
88 import org.robolectric.Shadows;
89 import org.robolectric.annotation.Config;
90 import org.robolectric.shadows.ShadowBluetoothDevice;
91 import org.robolectric.shadows.ShadowPackageManager;
92 
93 import java.util.ArrayList;
94 import java.util.Collection;
95 import java.util.List;
96 
97 @RunWith(RobolectricTestRunner.class)
98 @Config(shadows = {
99         ShadowAudioManager.class,
100         ShadowBluetoothUtils.class,
101         ShadowBluetoothDevice.class}
102 )
103 public class MediaOutputPreferenceControllerTest {
104     private static final String TEST_KEY = "Test_Key";
105     private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
106     private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
107     private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
108     private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
109     private static final String TEST_LE_AUDIO_DEVICE_NAME_1 = "Test_LE_AUDIO_Device_NAME_1";
110     private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
111     private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
112     private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
113     private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
114     private static final String TEST_DEVICE_ADDRESS_5 = "00:E5:E5:E5:E5:E5";
115     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
116     private static final String TEST_APPLICATION_LABEL = "APP Test Label";
117 
118     @Rule
119     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
120 
121     @Mock
122     private PackageManager mPackageManager;
123     @Mock
124     private LocalBluetoothManager mLocalManager;
125     @Mock
126     private BluetoothEventManager mBluetoothEventManager;
127     @Mock
128     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
129     @Mock
130     private A2dpProfile mA2dpProfile;
131     @Mock
132     private HearingAidProfile mHearingAidProfile;
133     @Mock
134     private LeAudioProfile mLeAudioProfile;
135     @Mock
136     private LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast;
137     @Mock
138     private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;
139     @Mock
140     private MediaSessionManager mMediaSessionManager;
141     @Mock
142     private MediaController mMediaController;
143     @Mock
144     private CachedBluetoothDeviceManager mCachedDeviceManager;
145     @Mock
146     private CachedBluetoothDevice mCachedBluetoothDeviceL;
147     @Mock
148     private CachedBluetoothDevice mCachedBluetoothDeviceR;
149 
150     private Context mContext;
151     private PreferenceScreen mScreen;
152     private Preference mPreference;
153     private AudioManager mAudioManager;
154     private ShadowAudioManager mShadowAudioManager;
155     private BluetoothManager mBluetoothManager;
156     private BluetoothAdapter mBluetoothAdapter;
157     private BluetoothDevice mBluetoothDevice;
158     private BluetoothDevice mSecondBluetoothDevice;
159     private BluetoothDevice mLeftBluetoothHapDevice;
160     private BluetoothDevice mRightBluetoothHapDevice;
161     private LocalBluetoothManager mLocalBluetoothManager;
162     private MediaOutputPreferenceController mController;
163     private List<BluetoothDevice> mProfileConnectedDevices;
164     private List<BluetoothDevice> mHearingAidActiveDevices;
165     private List<BluetoothDevice> mLeAudioActiveDevices;
166     private List<MediaController> mMediaControllers = new ArrayList<>();
167     private MediaController.PlaybackInfo mPlaybackInfo;
168     private PlaybackState mPlaybackState;
169     private ShadowPackageManager mShadowPackageManager;
170     private ApplicationInfo mAppInfo;
171     private PackageInfo mPackageInfo;
172     private PackageStats mPackageStats;
173     private Collection<CachedBluetoothDevice> mCachedDevices;
174 
175     @Before
setUp()176     public void setUp() {
177         MockitoAnnotations.initMocks(this);
178         mContext = spy(RuntimeEnvironment.application);
179 
180         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
181         mShadowAudioManager = ShadowAudioManager.getShadow();
182 
183         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalManager;
184         mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
185 
186         doReturn(mMediaSessionManager).when(mContext).getSystemService(MediaSessionManager.class);
187         when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
188         when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
189         mPlaybackInfo = new MediaController.PlaybackInfo(
190                 MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
191                 VolumeProvider.VOLUME_CONTROL_ABSOLUTE,
192                 100,
193                 10,
194                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(),
195                 null);
196         mPlaybackState = new PlaybackState.Builder()
197                 .setState(PlaybackState.STATE_PLAYING, 0, 1)
198                 .build();
199         when(mMediaController.getPlaybackInfo()).thenReturn(mPlaybackInfo);
200         when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
201         mMediaControllers.add(mMediaController);
202 
203         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
204         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
205         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
206         when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
207         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
208         when(mLocalBluetoothProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
209         when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile())
210                 .thenReturn(mLocalBluetoothLeBroadcast);
211 
212         mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
213         mBluetoothAdapter = mBluetoothManager.getAdapter();
214 
215         mCachedDevices = new ArrayList<>();
216         mCachedDevices.add(mCachedBluetoothDeviceL);
217         mCachedDevices.add(mCachedBluetoothDeviceR);
218         when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
219 
220         mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
221         when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
222         when(mBluetoothDevice.getAlias()).thenReturn(TEST_DEVICE_NAME_1);
223         when(mBluetoothDevice.isConnected()).thenReturn(true);
224 
225         mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
226         when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
227         when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
228 
229         mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
230         when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
231         when(mLeftBluetoothHapDevice.getAlias()).thenReturn(TEST_HAP_DEVICE_NAME_1);
232         when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
233 
234         mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
235         when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
236         when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
237 
238         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
239         mScreen = spy(new PreferenceScreen(mContext, null));
240         mPreference = new Preference(mContext);
241         mProfileConnectedDevices = new ArrayList<>();
242         mHearingAidActiveDevices = new ArrayList<>(2);
243         mLeAudioActiveDevices = new ArrayList<>();
244 
245         when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
246         when(mScreen.getContext()).thenReturn(mContext);
247         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
248         mScreen.addPreference(mPreference);
249         mController.displayPreference(mScreen);
250         mController.setCallback(mAudioSwitchPreferenceCallback);
251     }
252 
253     @After
tearDown()254     public void tearDown() {
255         ShadowBluetoothUtils.reset();
256     }
257 
258     /** Device start broadcasting so Preference summary should become "Audio Sharing" */
259     @Test
audioSharingStart_changeSummary()260     public void audioSharingStart_changeSummary() {
261         mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING);
262         mController.onStart();
263         ArgumentCaptor<BluetoothLeBroadcast.Callback> broadcastCallbackCaptor =
264                 ArgumentCaptor.forClass(BluetoothLeBroadcast.Callback.class);
265         mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
266         mAudioManager.setMode(AudioManager.MODE_NORMAL);
267         when(mLocalBluetoothLeBroadcast.isEnabled(null)).thenReturn(true);
268         verify(mLocalBluetoothLeBroadcast)
269                 .registerServiceCallBack(any(), broadcastCallbackCaptor.capture());
270         BluetoothLeBroadcast.Callback callback = broadcastCallbackCaptor.getValue();
271 
272         callback.onBroadcastStarted(0, 0);
273         assertThat(mPreference.getSummary().toString())
274                 .isEqualTo(mContext.getText(R.string.media_output_audio_sharing).toString());
275     }
276 
277     /**
278      * A2DP Bluetooth device(s) are connected, but no device is set as activated
279      * Preference summary should be "This device"
280      */
281     @Test
updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary()282     public void updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary() {
283         mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
284         mAudioManager.setMode(AudioManager.MODE_NORMAL);
285         mProfileConnectedDevices.clear();
286         mProfileConnectedDevices.add(mBluetoothDevice);
287         mProfileConnectedDevices.add(mSecondBluetoothDevice);
288         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
289         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
290 
291         assertThat(mPreference.getSummary()).isNull();
292         mController.updateState(mPreference);
293         assertThat(mPreference.getSummary()).isEqualTo(
294                 mContext.getText(R.string.media_output_default_summary));
295     }
296 
297     /**
298      * A2DP Bluetooth device(s) are connected and active
299      * Preference summary should be device's name
300      */
301     @Test
updateState_withActiveBtDevice_setActivatedDeviceName()302     public void updateState_withActiveBtDevice_setActivatedDeviceName() {
303         mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
304         mAudioManager.setMode(AudioManager.MODE_NORMAL);
305         mProfileConnectedDevices.clear();
306         mProfileConnectedDevices.add(mBluetoothDevice);
307         mProfileConnectedDevices.add(mSecondBluetoothDevice);
308         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
309         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
310 
311         assertThat(mPreference.getSummary()).isNull();
312         mController.updateState(mPreference);
313         assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
314     }
315 
316     /**
317      * Hearing Aid device(s) are connected and active
318      * Preference summary should be device's name
319      */
320     @Test
updateState_withActiveHADevice_setActivatedDeviceName()321     public void updateState_withActiveHADevice_setActivatedDeviceName() {
322         mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
323         mAudioManager.setMode(AudioManager.MODE_NORMAL);
324         mHearingAidActiveDevices.clear();
325         mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
326         when(mHearingAidProfile.getConnectedDevices()).thenReturn(mHearingAidActiveDevices);
327         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
328 
329         assertThat(mPreference.getSummary()).isNull();
330         mController.updateState(mPreference);
331         assertThat(mPreference.getSummary()).isEqualTo(TEST_HAP_DEVICE_NAME_1);
332 
333     }
334 
335     @Test
updateState_withActiveLeAudioDevice_setActivatedDeviceName()336     public void updateState_withActiveLeAudioDevice_setActivatedDeviceName() {
337         mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLE_HEADSET);
338         mAudioManager.setMode(AudioManager.MODE_NORMAL);
339         when(mCachedBluetoothDeviceL.getDevice()).thenReturn(mBluetoothDevice);
340         when(mCachedBluetoothDeviceR.getDevice()).thenReturn(mSecondBluetoothDevice);
341         when(mBluetoothDevice.getAlias()).thenReturn(TEST_LE_AUDIO_DEVICE_NAME_1);
342         mProfileConnectedDevices.clear();
343         mProfileConnectedDevices.add(mBluetoothDevice);
344         mProfileConnectedDevices.add(mSecondBluetoothDevice);
345         mLeAudioActiveDevices.clear();
346         mLeAudioActiveDevices.add(mBluetoothDevice);
347         when(mLeAudioProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
348         when(mLeAudioProfile.getActiveDevices()).thenReturn(mLeAudioActiveDevices);
349 
350         assertThat(mPreference.getSummary()).isNull();
351         mController.updateState(mPreference);
352         assertThat(mPreference.getSummary()).isEqualTo(TEST_LE_AUDIO_DEVICE_NAME_1);
353     }
354 
355     @Test
updateState_noActiveLocalPlayback_noTitle()356     public void updateState_noActiveLocalPlayback_noTitle() {
357         mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
358         mPlaybackState = new PlaybackState.Builder()
359                 .setState(PlaybackState.STATE_NONE, 0, 1)
360                 .build();
361         when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
362         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
363 
364         mController.updateState(mPreference);
365 
366         assertThat(mPreference.getTitle()).isNull();
367     }
368 
369     @Test
updateState_noActiveLocalPlayback_checkTitle()370     public void updateState_noActiveLocalPlayback_checkTitle() {
371         mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
372         mPlaybackState = new PlaybackState.Builder()
373                 .setState(PlaybackState.STATE_NONE, 0, 1)
374                 .build();
375         when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
376         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
377         mController.displayPreference(mScreen);
378 
379         mController.updateState(mPreference);
380 
381         assertThat(mPreference.getTitle().toString()).isEqualTo(
382                 mContext.getString(R.string.media_output_title_without_playing,
383                         TEST_APPLICATION_LABEL));
384     }
385 
386     @Test
updateState_withNullMediaController_noTitle()387     public void updateState_withNullMediaController_noTitle() {
388         mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
389         mMediaControllers.clear();
390         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
391 
392         mController.updateState(mPreference);
393 
394         assertThat(mPreference.getTitle()).isNull();
395     }
396 
397     @Test
updateState_withNullMediaController_checkTitle()398     public void updateState_withNullMediaController_checkTitle() {
399         mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
400         mMediaControllers.clear();
401         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
402         mController.displayPreference(mScreen);
403 
404         mController.updateState(mPreference);
405 
406         assertThat(mPreference.getTitle().toString()).isEqualTo(
407                 mContext.getString(R.string.media_output_title_without_playing,
408                         TEST_APPLICATION_LABEL));
409     }
410 
411     @Test
updateState_withActiveLocalPlayback_checkTitle()412     public void updateState_withActiveLocalPlayback_checkTitle() {
413         initPackage();
414         mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
415 
416         mController.updateState(mPreference);
417 
418         assertThat(mPreference.getTitle()).isEqualTo(
419                 mContext.getString(R.string.media_output_label_title, TEST_APPLICATION_LABEL));
420     }
421 
422     @Test
click_launch_outputSwitcherSlice()423     public void click_launch_outputSwitcherSlice() {
424         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
425         mController.handlePreferenceTreeClick(mPreference);
426         verify(mContext, never()).startActivity(intentCaptor.capture());
427 
428         mPreference.setKey(TEST_KEY);
429         mController.handlePreferenceTreeClick(mPreference);
430         verify(mContext).sendBroadcast(intentCaptor.capture());
431         assertThat(intentCaptor.getValue().getAction())
432                 .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
433     }
434 
435     @Test
handlePreferenceTreeClick_WithNoLocalPlaybackFlagEnabled_verifyIntentExtra()436     public void handlePreferenceTreeClick_WithNoLocalPlaybackFlagEnabled_verifyIntentExtra() {
437         mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
438         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
439         mPlaybackState = new PlaybackState.Builder()
440                 .setState(PlaybackState.STATE_NONE, 0, 1)
441                 .build();
442         when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
443         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
444         mPreference.setKey(TEST_KEY);
445 
446         mController.handlePreferenceTreeClick(mPreference);
447 
448         verify(mContext).sendBroadcast(intentCaptor.capture());
449         assertThat(intentCaptor.getValue().getAction())
450                 .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG);
451     }
452 
453     @Test
handlePreferenceTreeClick_WithNullControllerFlagEnabled_verifyIntentExtra()454     public void handlePreferenceTreeClick_WithNullControllerFlagEnabled_verifyIntentExtra() {
455         mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
456         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
457         mMediaControllers.clear();
458         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
459         mPreference.setKey(TEST_KEY);
460 
461         mController.handlePreferenceTreeClick(mPreference);
462 
463         verify(mContext).sendBroadcast(intentCaptor.capture());
464         assertThat(intentCaptor.getValue().getAction())
465                 .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG);
466     }
467 
468     /**
469      * Default status
470      * Preference should be invisible
471      * Summary should be default summary
472      */
473     @Test
updateState_notInCall_preferenceVisible()474     public void updateState_notInCall_preferenceVisible() {
475         mAudioManager.setMode(AudioManager.MODE_NORMAL);
476 
477         mController.updateState(mPreference);
478 
479         assertThat(mPreference.isVisible()).isTrue();
480     }
481 
482     /**
483      * During a call
484      * Preference should be invisible
485      * Default string should be "Unavailable during calls"
486      */
487     @Test
updateState_inCall_preferenceInvisible()488     public void updateState_inCall_preferenceInvisible() {
489         mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
490 
491         mController.updateState(mPreference);
492 
493         assertThat(mPreference.isVisible()).isFalse();
494     }
495 
496     /**
497      * During a call
498      * Preference should be visible when input routing is available in desktop
499      */
500     @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
501     @Test
updateState_inCall_preferenceVisible_inputRoutingEnabledInDesktop()502     public void updateState_inCall_preferenceVisible_inputRoutingEnabledInDesktop()
503             throws PackageManager.NameNotFoundException {
504         when(mContext.getPackageManager()).thenReturn(mPackageManager);
505         when(mPackageManager.hasSystemFeature(FEATURE_PC)).thenReturn(true);
506 
507         ApplicationInfo appInfo = new ApplicationInfo();
508         appInfo.flags = ApplicationInfo.FLAG_INSTALLED;
509         appInfo.packageName = TEST_PACKAGE_NAME;
510         appInfo.name = TEST_APPLICATION_LABEL;
511         when(mPackageManager.getApplicationInfo(TEST_PACKAGE_NAME,
512                 PackageManager.MATCH_DISABLED_COMPONENTS
513                         | PackageManager.MATCH_ANY_USER)).thenReturn(appInfo);
514 
515 
516         mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
517         mController.updateState(mPreference);
518 
519         assertThat(mPreference.isVisible()).isTrue();
520     }
521 
522     @Test
findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice()523     public void findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice() {
524         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
525         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
526 
527         assertThat(mController.findActiveDevice()).isEqualTo(mBluetoothDevice);
528     }
529 
530     @Test
findActiveDevice_allDevicesNotActive_returnNull()531     public void findActiveDevice_allDevicesNotActive_returnNull() {
532         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
533         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
534 
535         assertThat(mController.findActiveDevice()).isNull();
536     }
537 
538     @Test
findActiveDevice_allProfilesWithActiveDevice_returnHADevice()539     public void findActiveDevice_allProfilesWithActiveDevice_returnHADevice() {
540         BluetoothDevice btLeDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_5));
541         when(btLeDevice.getName()).thenReturn(TEST_LE_AUDIO_DEVICE_NAME_1);
542         mLeAudioActiveDevices.clear();
543         mLeAudioActiveDevices.add(btLeDevice);
544         mHearingAidActiveDevices.clear();
545         mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
546         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
547         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
548         when(mLeAudioProfile.getActiveDevices()).thenReturn(mLeAudioActiveDevices);
549 
550         assertThat(mController.findActiveDevice()).isEqualTo(mLeftBluetoothHapDevice);
551     }
552 
553     @Test
findActiveDevice_a2dpDeviceAndLeAudioDeviceActive_returnLeAudioDevice()554     public void findActiveDevice_a2dpDeviceAndLeAudioDeviceActive_returnLeAudioDevice() {
555         BluetoothDevice btLeDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_5));
556         when(btLeDevice.getName()).thenReturn(TEST_LE_AUDIO_DEVICE_NAME_1);
557         mLeAudioActiveDevices.clear();
558         mLeAudioActiveDevices.add(btLeDevice);
559         mHearingAidActiveDevices.clear();
560         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
561         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
562         when(mLeAudioProfile.getActiveDevices()).thenReturn(mLeAudioActiveDevices);
563 
564         assertThat(mController.findActiveDevice()).isEqualTo(btLeDevice);
565     }
566 
567     @Test
findActiveDevice_onlyLeAudioDeviceActive_returnLeAudioDevice()568     public void findActiveDevice_onlyLeAudioDeviceActive_returnLeAudioDevice() {
569         BluetoothDevice btLeDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_5));
570         when(btLeDevice.getName()).thenReturn(TEST_LE_AUDIO_DEVICE_NAME_1);
571         mLeAudioActiveDevices.clear();
572         mLeAudioActiveDevices.add(btLeDevice);
573         mHearingAidActiveDevices.clear();
574         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
575         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
576         when(mLeAudioProfile.getActiveDevices()).thenReturn(mLeAudioActiveDevices);
577 
578         assertThat(mController.findActiveDevice()).isEqualTo(btLeDevice);
579     }
580 
initPackage()581     private void initPackage() {
582         mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
583         mAppInfo = new ApplicationInfo();
584         mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
585         mAppInfo.packageName = TEST_PACKAGE_NAME;
586         mAppInfo.name = TEST_APPLICATION_LABEL;
587         mPackageInfo = new PackageInfo();
588         mPackageInfo.packageName = TEST_PACKAGE_NAME;
589         mPackageInfo.applicationInfo = mAppInfo;
590         mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
591     }
592 }
593