• 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.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
20 import static android.media.AudioSystem.DEVICE_OUT_EARPIECE;
21 import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.spy;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.bluetooth.BluetoothAdapter;
34 import android.bluetooth.BluetoothDevice;
35 import android.bluetooth.BluetoothManager;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.PackageInfo;
40 import android.content.pm.PackageStats;
41 import android.media.AudioAttributes;
42 import android.media.AudioManager;
43 import android.media.VolumeProvider;
44 import android.media.session.MediaController;
45 import android.media.session.MediaSessionManager;
46 import android.media.session.PlaybackState;
47 
48 import androidx.preference.Preference;
49 import androidx.preference.PreferenceManager;
50 import androidx.preference.PreferenceScreen;
51 
52 import com.android.settings.R;
53 import com.android.settings.bluetooth.Utils;
54 import com.android.settings.testutils.shadow.ShadowAudioManager;
55 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
56 import com.android.settingslib.bluetooth.A2dpProfile;
57 import com.android.settingslib.bluetooth.BluetoothEventManager;
58 import com.android.settingslib.bluetooth.HearingAidProfile;
59 import com.android.settingslib.bluetooth.LocalBluetoothManager;
60 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
61 import com.android.settingslib.media.MediaOutputConstants;
62 
63 import org.junit.After;
64 import org.junit.Before;
65 import org.junit.Ignore;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.ArgumentCaptor;
69 import org.mockito.Mock;
70 import org.mockito.MockitoAnnotations;
71 import org.robolectric.RobolectricTestRunner;
72 import org.robolectric.RuntimeEnvironment;
73 import org.robolectric.Shadows;
74 import org.robolectric.annotation.Config;
75 import org.robolectric.shadows.ShadowBluetoothDevice;
76 import org.robolectric.shadows.ShadowPackageManager;
77 
78 import java.util.ArrayList;
79 import java.util.List;
80 
81 @RunWith(RobolectricTestRunner.class)
82 @Config(shadows = {
83         ShadowAudioManager.class,
84         ShadowBluetoothUtils.class,
85         ShadowBluetoothDevice.class}
86 )
87 public class MediaOutputPreferenceControllerTest {
88     private static final String TEST_KEY = "Test_Key";
89     private static final String TEST_DEVICE_NAME_1 = "Test_A2DP_BT_Device_NAME_1";
90     private static final String TEST_DEVICE_NAME_2 = "Test_A2DP_BT_Device_NAME_2";
91     private static final String TEST_HAP_DEVICE_NAME_1 = "Test_HAP_BT_Device_NAME_1";
92     private static final String TEST_HAP_DEVICE_NAME_2 = "Test_HAP_BT_Device_NAME_2";
93     private static final String TEST_DEVICE_ADDRESS_1 = "00:A1:A1:A1:A1:A1";
94     private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
95     private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
96     private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
97     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
98     private static final String TEST_APPLICATION_LABEL = "APP Test Label";
99 
100     @Mock
101     private LocalBluetoothManager mLocalManager;
102     @Mock
103     private BluetoothEventManager mBluetoothEventManager;
104     @Mock
105     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
106     @Mock
107     private A2dpProfile mA2dpProfile;
108     @Mock
109     private HearingAidProfile mHearingAidProfile;
110     @Mock
111     private AudioSwitchPreferenceController.AudioSwitchCallback mAudioSwitchPreferenceCallback;
112     @Mock
113     private MediaSessionManager mMediaSessionManager;
114     @Mock
115     private MediaController mMediaController;
116 
117     private Context mContext;
118     private PreferenceScreen mScreen;
119     private Preference mPreference;
120     private AudioManager mAudioManager;
121     private ShadowAudioManager mShadowAudioManager;
122     private BluetoothManager mBluetoothManager;
123     private BluetoothAdapter mBluetoothAdapter;
124     private BluetoothDevice mBluetoothDevice;
125     private BluetoothDevice mSecondBluetoothDevice;
126     private BluetoothDevice mLeftBluetoothHapDevice;
127     private BluetoothDevice mRightBluetoothHapDevice;
128     private LocalBluetoothManager mLocalBluetoothManager;
129     private MediaOutputPreferenceController mController;
130     private List<BluetoothDevice> mProfileConnectedDevices;
131     private List<BluetoothDevice> mHearingAidActiveDevices;
132     private List<MediaController> mMediaControllers = new ArrayList<>();
133     private MediaController.PlaybackInfo mPlaybackInfo;
134     private PlaybackState mPlaybackState;
135     private ShadowPackageManager mShadowPackageManager;
136     private ApplicationInfo mAppInfo;
137     private PackageInfo mPackageInfo;
138     private PackageStats mPackageStats;
139 
140     @Before
setUp()141     public void setUp() {
142         MockitoAnnotations.initMocks(this);
143         mContext = spy(RuntimeEnvironment.application);
144 
145         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
146         mShadowAudioManager = ShadowAudioManager.getShadow();
147 
148         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalManager;
149         mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
150 
151         doReturn(mMediaSessionManager).when(mContext).getSystemService(MediaSessionManager.class);
152         when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
153         when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
154         mPlaybackInfo = new MediaController.PlaybackInfo(
155                 MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
156                 VolumeProvider.VOLUME_CONTROL_ABSOLUTE,
157                 100,
158                 10,
159                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(),
160                 null);
161         mPlaybackState = new PlaybackState.Builder()
162                 .setState(PlaybackState.STATE_PLAYING, 0, 1)
163                 .build();
164         when(mMediaController.getPlaybackInfo()).thenReturn(mPlaybackInfo);
165         when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
166         mMediaControllers.add(mMediaController);
167 
168         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
169         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
170         when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
171         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
172 
173         mBluetoothManager = new BluetoothManager(mContext);
174         mBluetoothAdapter = mBluetoothManager.getAdapter();
175 
176         mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
177         when(mBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_1);
178         when(mBluetoothDevice.isConnected()).thenReturn(true);
179 
180         mSecondBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
181         when(mSecondBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME_2);
182         when(mSecondBluetoothDevice.isConnected()).thenReturn(true);
183 
184         mLeftBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_3));
185         when(mLeftBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_1);
186         when(mLeftBluetoothHapDevice.isConnected()).thenReturn(true);
187 
188         mRightBluetoothHapDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_4));
189         when(mRightBluetoothHapDevice.getName()).thenReturn(TEST_HAP_DEVICE_NAME_2);
190         when(mRightBluetoothHapDevice.isConnected()).thenReturn(true);
191 
192         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
193         mScreen = spy(new PreferenceScreen(mContext, null));
194         mPreference = new Preference(mContext);
195         mProfileConnectedDevices = new ArrayList<>();
196         mHearingAidActiveDevices = new ArrayList<>(2);
197 
198         when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
199         when(mScreen.getContext()).thenReturn(mContext);
200         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
201         mScreen.addPreference(mPreference);
202         mController.displayPreference(mScreen);
203         mController.setCallback(mAudioSwitchPreferenceCallback);
204     }
205 
206     @After
tearDown()207     public void tearDown() {
208         ShadowBluetoothUtils.reset();
209     }
210 
211     /**
212      * A2DP Bluetooth device(s) are connected, but no device is set as activated
213      * Preference summary should be "This device"
214      */
215     @Test
updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary()216     public void updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary() {
217         mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
218         mAudioManager.setMode(AudioManager.MODE_NORMAL);
219         mProfileConnectedDevices.clear();
220         mProfileConnectedDevices.add(mBluetoothDevice);
221         mProfileConnectedDevices.add(mSecondBluetoothDevice);
222         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
223         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
224 
225         assertThat(mPreference.getSummary()).isNull();
226         mController.updateState(mPreference);
227         assertThat(mPreference.getSummary()).isEqualTo(
228                 mContext.getText(R.string.media_output_default_summary));
229     }
230 
231     /**
232      * A2DP Bluetooth device(s) are connected and active
233      * Preference summary should be device's name
234      */
235     @Test
236     @Ignore
updateState_withActiveBtDevice_setActivatedDeviceName()237     public void updateState_withActiveBtDevice_setActivatedDeviceName() {
238         mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
239         mAudioManager.setMode(AudioManager.MODE_NORMAL);
240         mProfileConnectedDevices.clear();
241         mProfileConnectedDevices.add(mBluetoothDevice);
242         mProfileConnectedDevices.add(mSecondBluetoothDevice);
243         when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
244         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
245 
246         assertThat(mPreference.getSummary()).isNull();
247         mController.updateState(mPreference);
248         assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
249     }
250 
251     /**
252      * Hearing Aid device(s) are connected and active
253      * Preference summary should be device's name
254      */
255     @Test
256     @Ignore
updateState_withActiveHADevice_setActivatedDeviceName()257     public void updateState_withActiveHADevice_setActivatedDeviceName() {
258         mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
259         mAudioManager.setMode(AudioManager.MODE_NORMAL);
260         mHearingAidActiveDevices.clear();
261         mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
262         when(mHearingAidProfile.getConnectedDevices()).thenReturn(mHearingAidActiveDevices);
263         when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
264 
265         assertThat(mPreference.getSummary()).isNull();
266         mController.updateState(mPreference);
267         assertThat(mPreference.getSummary()).isEqualTo(TEST_HAP_DEVICE_NAME_1);
268 
269     }
270 
271     @Test
updateState_noActiveLocalPlayback_noTitle()272     public void updateState_noActiveLocalPlayback_noTitle() {
273         mPlaybackState = new PlaybackState.Builder()
274                 .setState(PlaybackState.STATE_NONE, 0, 1)
275                 .build();
276         when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
277         mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
278 
279         mController.updateState(mPreference);
280 
281         assertThat(mPreference.getTitle()).isNull();
282     }
283 
284     @Test
updateState_withActiveLocalPlayback_checkTitle()285     public void updateState_withActiveLocalPlayback_checkTitle() {
286         initPackage();
287         mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
288 
289         mController.updateState(mPreference);
290 
291         assertThat(mPreference.getTitle()).isEqualTo(
292                 mContext.getString(R.string.media_output_label_title, TEST_APPLICATION_LABEL));
293     }
294 
295     @Test
click_launch_outputSwitcherSlice()296     public void click_launch_outputSwitcherSlice() {
297         final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
298         mController.handlePreferenceTreeClick(mPreference);
299         verify(mContext, never()).startActivity(intentCaptor.capture());
300 
301         mPreference.setKey(TEST_KEY);
302         mController.handlePreferenceTreeClick(mPreference);
303         verify(mContext).sendBroadcast(intentCaptor.capture());
304         assertThat(intentCaptor.getValue().getAction())
305                 .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
306     }
307 
308     /**
309      * Default status
310      * Preference should be invisible
311      * Summary should be default summary
312      */
313     @Test
updateState_notInCall_preferenceVisible()314     public void updateState_notInCall_preferenceVisible() {
315         mAudioManager.setMode(AudioManager.MODE_NORMAL);
316 
317         mController.updateState(mPreference);
318 
319         assertThat(mPreference.isVisible()).isTrue();
320     }
321 
322     /**
323      * During a call
324      * Preference should be invisible
325      * Default string should be "Unavailable during calls"
326      */
327     @Test
updateState_inCall_preferenceInvisible()328     public void updateState_inCall_preferenceInvisible() {
329         mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
330 
331         mController.updateState(mPreference);
332 
333         assertThat(mPreference.isVisible()).isFalse();
334     }
335 
336     @Test
findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice()337     public void findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice() {
338         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
339         when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
340 
341         assertThat(mController.findActiveDevice()).isEqualTo(mBluetoothDevice);
342     }
343 
344     @Test
findActiveDevice_allDevicesNotActive_returnNull()345     public void findActiveDevice_allDevicesNotActive_returnNull() {
346         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
347         when(mA2dpProfile.getActiveDevice()).thenReturn(null);
348 
349         assertThat(mController.findActiveDevice()).isNull();
350     }
351 
initPackage()352     private void initPackage() {
353         mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
354         mAppInfo = new ApplicationInfo();
355         mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
356         mAppInfo.packageName = TEST_PACKAGE_NAME;
357         mAppInfo.name = TEST_APPLICATION_LABEL;
358         mPackageInfo = new PackageInfo();
359         mPackageInfo.packageName = TEST_PACKAGE_NAME;
360         mPackageInfo.applicationInfo = mAppInfo;
361         mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
362     }
363 }
364