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