1 /*
2  * Copyright (C) 2017 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 package com.android.settings.bluetooth;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.doNothing;
22 import static org.mockito.Mockito.doReturn;
23 import static org.mockito.Mockito.doThrow;
24 import static org.mockito.Mockito.never;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.bluetooth.BluetoothAdapter;
30 import android.bluetooth.BluetoothDevice;
31 import android.bluetooth.BluetoothProfile;
32 import android.content.Context;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageManager;
35 import android.graphics.drawable.Drawable;
36 import android.media.AudioManager;
37 import android.platform.test.annotations.RequiresFlagsEnabled;
38 import android.platform.test.flag.junit.CheckFlagsRule;
39 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
40 import android.util.Pair;
41 
42 import com.android.settings.connecteddevice.DevicePreferenceCallback;
43 import com.android.settings.dashboard.DashboardFragment;
44 import com.android.settings.testutils.shadow.ShadowAudioManager;
45 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
46 import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager;
47 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
48 import com.android.settingslib.flags.Flags;
49 
50 import org.junit.Before;
51 import org.junit.Rule;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 import org.robolectric.RobolectricTestRunner;
57 import org.robolectric.RuntimeEnvironment;
58 import org.robolectric.annotation.Config;
59 import org.robolectric.shadow.api.Shadow;
60 
61 import java.util.ArrayList;
62 import java.util.Collection;
63 
64 @RunWith(RobolectricTestRunner.class)
65 @Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class,
66         ShadowCachedBluetoothDeviceManager.class})
67 public class ConnectedBluetoothDeviceUpdaterTest {
68 
69     private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
70     private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager";
71 
72     @Rule
73     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
74 
75     @Mock
76     private DashboardFragment mDashboardFragment;
77     @Mock
78     private DevicePreferenceCallback mDevicePreferenceCallback;
79     @Mock
80     private CachedBluetoothDevice mCachedBluetoothDevice;
81     @Mock
82     private BluetoothDevice mBluetoothDevice;
83     @Mock
84     private Drawable mDrawable;
85     @Mock
86     private PackageManager mPackageManager;
87 
88     private Context mContext;
89     private ConnectedBluetoothDeviceUpdater mBluetoothDeviceUpdater;
90     private Collection<CachedBluetoothDevice> mCachedDevices;
91     private AudioManager mAudioManager;
92     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
93     private ShadowCachedBluetoothDeviceManager mShadowCachedBluetoothDeviceManager;
94 
95     @Before
setUp()96     public void setUp() {
97         MockitoAnnotations.initMocks(this);
98 
99         Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
100         mContext = spy(RuntimeEnvironment.application);
101         mAudioManager = mContext.getSystemService(AudioManager.class);
102         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
103         mShadowBluetoothAdapter.setEnabled(true);
104         mShadowCachedBluetoothDeviceManager = Shadow.extract(
105                 Utils.getLocalBtManager(mContext).getCachedDeviceManager());
106         doReturn(mContext).when(mDashboardFragment).getContext();
107         mCachedDevices = new ArrayList<>();
108         mCachedDevices.add(mCachedBluetoothDevice);
109 
110         when(mContext.getPackageManager()).thenReturn(mPackageManager);
111         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
112         when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
113         when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
114         mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
115         mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
116                 mDevicePreferenceCallback, /* metricsCategory= */ 0));
117         mBluetoothDeviceUpdater.setPrefContext(mContext);
118         doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
119         doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
120     }
121 
122     @Test
onAudioModeChanged_hfpDeviceConnected_notInCall_addPreference()123     public void onAudioModeChanged_hfpDeviceConnected_notInCall_addPreference() {
124         mAudioManager.setMode(AudioManager.MODE_NORMAL);
125         when(mBluetoothDeviceUpdater.
126                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
127         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
128 
129         mBluetoothDeviceUpdater.onAudioModeChanged();
130 
131         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
132     }
133 
134     @Test
onAudioModeChanged_hfpDeviceConnected_inCall_removePreference()135     public void onAudioModeChanged_hfpDeviceConnected_inCall_removePreference() {
136         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
137         when(mBluetoothDeviceUpdater.
138                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
139         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
140 
141         mBluetoothDeviceUpdater.onAudioModeChanged();
142 
143         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
144     }
145 
146     @Test
onAudioModeChanged_a2dpDeviceConnected_notInCall_removePreference()147     public void onAudioModeChanged_a2dpDeviceConnected_notInCall_removePreference() {
148         mAudioManager.setMode(AudioManager.MODE_NORMAL);
149         when(mBluetoothDeviceUpdater.
150                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
151         when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
152 
153         mBluetoothDeviceUpdater.onAudioModeChanged();
154 
155         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
156     }
157 
158     @Test
onAudioModeChanged_a2dpDeviceConnected_inCall_addPreference()159     public void onAudioModeChanged_a2dpDeviceConnected_inCall_addPreference() {
160         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
161         when(mBluetoothDeviceUpdater.
162                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
163         when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
164 
165         mBluetoothDeviceUpdater.onAudioModeChanged();
166 
167         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
168     }
169 
170     @Test
onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_addPreference()171     public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_addPreference() {
172         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
173         when(mBluetoothDeviceUpdater.
174                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
175         when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
176 
177         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
178                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
179 
180         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
181     }
182 
183     @Test
onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference()184     public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
185         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
186         when(mBluetoothDeviceUpdater.
187                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
188         when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
189         mCachedDevices.clear();
190 
191         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
192                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
193 
194         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
195     }
196 
197     @Test
onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_removePreference()198     public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_removePreference() {
199         mAudioManager.setMode(AudioManager.MODE_NORMAL);
200         when(mBluetoothDeviceUpdater.
201                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
202         when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
203 
204         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
205                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
206 
207         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
208     }
209 
210     @Test
onProfileConnectionStateChanged_hfpDeviceConnected_inCall_removePreference()211     public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_removePreference() {
212         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
213         when(mBluetoothDeviceUpdater.
214                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
215         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
216 
217         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
218                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
219 
220         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
221     }
222 
223     @Test
onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_addPreference()224     public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_addPreference() {
225         mAudioManager.setMode(AudioManager.MODE_NORMAL);
226         when(mBluetoothDeviceUpdater.
227                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
228         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
229 
230         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
231                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
232 
233         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
234     }
235 
236     @Test
onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_removePreference()237     public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_removePreference()
238     {
239         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
240         when(mBluetoothDeviceUpdater.
241                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
242         when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
243 
244         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
245                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
246 
247         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
248     }
249 
250     @Test
onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_removePreference()251     public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_removePreference()
252     {
253         mAudioManager.setMode(AudioManager.MODE_NORMAL);
254         when(mBluetoothDeviceUpdater.
255                 isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
256         when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
257 
258         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
259                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
260 
261         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
262     }
263 
264     @Test
onProfileConnectionStateChanged_leAudioDeviceConnected_inCall_removesPreference()265     public void onProfileConnectionStateChanged_leAudioDeviceConnected_inCall_removesPreference() {
266         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
267         when(mBluetoothDeviceUpdater
268                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
269         when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
270 
271         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
272                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
273 
274         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
275     }
276 
277     @Test
onProfileConnectionStateChanged_leAudioDeviceConnected_notInCall_removesPreference()278     public void onProfileConnectionStateChanged_leAudioDeviceConnected_notInCall_removesPreference()
279     {
280         mAudioManager.setMode(AudioManager.MODE_NORMAL);
281         when(mBluetoothDeviceUpdater
282                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
283         when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
284 
285         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
286                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
287 
288         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
289     }
290     @Test
onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovesPreference()291     public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovesPreference()
292     {
293         mAudioManager.setMode(AudioManager.MODE_IN_CALL);
294         when(mBluetoothDeviceUpdater
295                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
296         when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
297         mCachedDevices.clear();
298 
299         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
300                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
301 
302         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
303     }
304 
305     @Test
onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovesPreference()306     public void onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovesPreference
307             () {
308         mAudioManager.setMode(AudioManager.MODE_NORMAL);
309         when(mBluetoothDeviceUpdater
310                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
311         when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
312         mCachedDevices.clear();
313 
314         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
315                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
316 
317         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
318     }
319 
320     @Test
onProfileConnectionStateChanged_deviceDisconnected_removePreference()321     public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
322         mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
323                 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
324 
325         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
326     }
327 
328     @Test
addPreference_addPreference_shouldHideSecondTarget()329     public void addPreference_addPreference_shouldHideSecondTarget() {
330         BluetoothDevicePreference btPreference =
331                 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
332                         true, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
333         mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, btPreference);
334 
335         mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice);
336 
337         assertThat(btPreference.shouldHideSecondTarget()).isTrue();
338     }
339 
340     @Test
341     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
update_notExclusiveManagedDevice_addDevice()342     public void update_notExclusiveManagedDevice_addDevice() {
343         mAudioManager.setMode(AudioManager.MODE_NORMAL);
344         when(mBluetoothDeviceUpdater
345                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
346         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
347         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
348                 null);
349 
350         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
351 
352         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
353     }
354 
355     @Test
356     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
update_exclusivelyManagedDevice_packageNotInstalled_addDevice()357     public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
358             throws Exception {
359         mAudioManager.setMode(AudioManager.MODE_NORMAL);
360         when(mBluetoothDeviceUpdater
361                 .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
362         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
363         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
364                 TEST_EXCLUSIVE_MANAGER.getBytes());
365         doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
366                 .getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
367 
368         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
369 
370         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
371     }
372 
373     @Test
374     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
update_exclusivelyManagedDevice_packageNotEnabled_addDevice()375     public void update_exclusivelyManagedDevice_packageNotEnabled_addDevice()
376             throws Exception {
377         ApplicationInfo appInfo = new ApplicationInfo();
378         appInfo.enabled = false;
379         mAudioManager.setMode(AudioManager.MODE_NORMAL);
380         when(mBluetoothDeviceUpdater
381             .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
382         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
383         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
384                 TEST_EXCLUSIVE_MANAGER.getBytes());
385         doReturn(appInfo).when(mPackageManager).getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0);
386 
387         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
388 
389         verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
390     }
391 
392     @Test
393     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
update_exclusivelyManagedDevice_packageInstalledAndEnabled_removePreference()394     public void update_exclusivelyManagedDevice_packageInstalledAndEnabled_removePreference()
395             throws Exception {
396         mAudioManager.setMode(AudioManager.MODE_NORMAL);
397         when(mBluetoothDeviceUpdater
398             .isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
399         when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
400         when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
401                 TEST_EXCLUSIVE_MANAGER.getBytes());
402         doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
403                 TEST_EXCLUSIVE_MANAGER, 0);
404 
405         mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
406 
407         verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
408         verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
409     }
410 }
411