1 /* 2 * Copyright 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 package com.android.settings.bluetooth; 17 18 import static org.mockito.ArgumentMatchers.any; 19 import static org.mockito.Mockito.doNothing; 20 import static org.mockito.Mockito.doReturn; 21 import static org.mockito.Mockito.spy; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 25 import android.bluetooth.BluetoothAdapter; 26 import android.bluetooth.BluetoothDevice; 27 import android.bluetooth.BluetoothLeBroadcastReceiveState; 28 import android.bluetooth.BluetoothProfile; 29 import android.bluetooth.BluetoothStatusCodes; 30 import android.content.Context; 31 import android.graphics.drawable.Drawable; 32 import android.media.AudioManager; 33 import android.platform.test.flag.junit.SetFlagsRule; 34 import android.util.Pair; 35 36 import com.android.settings.connecteddevice.DevicePreferenceCallback; 37 import com.android.settings.dashboard.DashboardFragment; 38 import com.android.settings.testutils.shadow.ShadowAudioManager; 39 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 40 import com.android.settings.testutils.shadow.ShadowBluetoothUtils; 41 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 42 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 43 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; 44 import com.android.settingslib.bluetooth.LocalBluetoothManager; 45 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; 46 import com.android.settingslib.flags.Flags; 47 48 import com.google.common.collect.ImmutableList; 49 import com.google.common.collect.ImmutableSet; 50 51 import org.junit.Before; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.mockito.Mock; 56 import org.mockito.MockitoAnnotations; 57 import org.robolectric.RobolectricTestRunner; 58 import org.robolectric.RuntimeEnvironment; 59 import org.robolectric.annotation.Config; 60 import org.robolectric.shadow.api.Shadow; 61 62 import java.util.ArrayList; 63 import java.util.Collection; 64 import java.util.List; 65 66 @RunWith(RobolectricTestRunner.class) 67 @Config( 68 shadows = { 69 ShadowAudioManager.class, 70 ShadowBluetoothAdapter.class, 71 ShadowBluetoothUtils.class 72 }) 73 public class AvailableMediaBluetoothDeviceUpdaterTest { 74 private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; 75 76 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 77 78 @Mock private DashboardFragment mDashboardFragment; 79 @Mock private DevicePreferenceCallback mDevicePreferenceCallback; 80 @Mock private CachedBluetoothDevice mCachedBluetoothDevice; 81 @Mock private BluetoothDevice mBluetoothDevice; 82 @Mock private Drawable mDrawable; 83 @Mock private LocalBluetoothManager mLocalBtManager; 84 @Mock private CachedBluetoothDeviceManager mCachedDeviceManager; 85 @Mock private LocalBluetoothProfileManager mProfileManager; 86 @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; 87 @Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState; 88 89 private Context mContext; 90 private AvailableMediaBluetoothDeviceUpdater mBluetoothDeviceUpdater; 91 private Collection<CachedBluetoothDevice> mCachedDevices; 92 private AudioManager mAudioManager; 93 private BluetoothDevicePreference mPreference; 94 private ShadowBluetoothAdapter mShadowBluetoothAdapter; 95 96 @Before setUp()97 public void setUp() { 98 MockitoAnnotations.initMocks(this); 99 100 mContext = RuntimeEnvironment.application; 101 mAudioManager = mContext.getSystemService(AudioManager.class); 102 ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; 103 mLocalBtManager = Utils.getLocalBtManager(mContext); 104 when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); 105 when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); 106 when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); 107 mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); 108 mShadowBluetoothAdapter.setEnabled(true); 109 mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( 110 BluetoothStatusCodes.FEATURE_SUPPORTED); 111 mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( 112 BluetoothStatusCodes.FEATURE_SUPPORTED); 113 mCachedDevices = new ArrayList<>(); 114 mCachedDevices.add(mCachedBluetoothDevice); 115 when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices); 116 Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device"); 117 118 doReturn(mContext).when(mDashboardFragment).getContext(); 119 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 120 when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS); 121 when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs); 122 when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(ImmutableSet.of()); 123 124 mBluetoothDeviceUpdater = 125 spy( 126 new AvailableMediaBluetoothDeviceUpdater( 127 mContext, mDevicePreferenceCallback, /* metricsCategory= */ 0)); 128 mBluetoothDeviceUpdater.setPrefContext(mContext); 129 mPreference = 130 new BluetoothDevicePreference( 131 mContext, 132 mCachedBluetoothDevice, 133 false, 134 BluetoothDevicePreference.SortType.TYPE_DEFAULT); 135 doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); 136 doNothing().when(mBluetoothDeviceUpdater).removePreference(any()); 137 } 138 139 @Test onAudioModeChanged_hfpDeviceConnected_inCall_addPreference()140 public void onAudioModeChanged_hfpDeviceConnected_inCall_addPreference() { 141 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 142 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 143 .thenReturn(true); 144 when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); 145 146 mBluetoothDeviceUpdater.onAudioModeChanged(); 147 148 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 149 } 150 151 @Test onAudioModeChanged_hfpDeviceConnected_notInCall_removePreference()152 public void onAudioModeChanged_hfpDeviceConnected_notInCall_removePreference() { 153 mAudioManager.setMode(AudioManager.MODE_NORMAL); 154 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 155 .thenReturn(true); 156 when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); 157 158 mBluetoothDeviceUpdater.onAudioModeChanged(); 159 160 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 161 } 162 163 @Test onAudioModeChanged_a2dpDeviceConnected_inCall_removePreference()164 public void onAudioModeChanged_a2dpDeviceConnected_inCall_removePreference() { 165 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 166 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 167 .thenReturn(true); 168 when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true); 169 170 mBluetoothDeviceUpdater.onAudioModeChanged(); 171 172 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 173 } 174 175 @Test onAudioModeChanged_a2dpDeviceConnected_notInCall_addPreference()176 public void onAudioModeChanged_a2dpDeviceConnected_notInCall_addPreference() { 177 mAudioManager.setMode(AudioManager.MODE_NORMAL); 178 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 179 .thenReturn(true); 180 when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true); 181 182 mBluetoothDeviceUpdater.onAudioModeChanged(); 183 184 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 185 } 186 187 @Test onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_addPreference()188 public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_addPreference() { 189 mAudioManager.setMode(AudioManager.MODE_NORMAL); 190 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 191 .thenReturn(true); 192 when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true); 193 194 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 195 mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP); 196 197 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 198 } 199 200 @Test onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_removePreference()201 public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_removePreference() { 202 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 203 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 204 .thenReturn(true); 205 when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true); 206 207 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 208 mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP); 209 210 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 211 } 212 213 @Test onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_removePreference()214 public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_removePreference() { 215 mAudioManager.setMode(AudioManager.MODE_NORMAL); 216 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 217 .thenReturn(true); 218 when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); 219 220 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 221 mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP); 222 223 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 224 } 225 226 @Test onProfileConnectionStateChanged_hfpDeviceConnected_inCall_addPreference()227 public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_addPreference() { 228 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 229 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 230 .thenReturn(true); 231 when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); 232 233 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 234 mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP); 235 236 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 237 } 238 239 @Test onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_addPreference()240 public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_addPreference() { 241 mAudioManager.setMode(AudioManager.MODE_NORMAL); 242 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 243 .thenReturn(true); 244 when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true); 245 246 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 247 mCachedBluetoothDevice, 248 BluetoothProfile.STATE_CONNECTED, 249 BluetoothProfile.HEARING_AID); 250 251 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 252 } 253 254 @Test onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_addPreference()255 public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_addPreference() { 256 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 257 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 258 .thenReturn(true); 259 when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true); 260 261 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 262 mCachedBluetoothDevice, 263 BluetoothProfile.STATE_CONNECTED, 264 BluetoothProfile.HEARING_AID); 265 266 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 267 } 268 269 @Test 270 public void onProfileConnectionStateChanged_leaConnected_notInCallSharingFlagOff_addsPreference()271 onProfileConnectionStateChanged_leaConnected_notInCallSharingFlagOff_addsPreference() { 272 mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 273 mAudioManager.setMode(AudioManager.MODE_NORMAL); 274 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 275 .thenReturn(true); 276 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 277 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState)); 278 List<Long> bisSyncState = new ArrayList<>(); 279 bisSyncState.add(1L); 280 when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); 281 282 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 283 mCachedBluetoothDevice, 284 BluetoothProfile.STATE_CONNECTED, 285 BluetoothProfile.LE_AUDIO); 286 287 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 288 } 289 290 @Test 291 public void onProfileConnectionStateChanged_leaConnected_notInCallNotInSharing_addsPreference()292 onProfileConnectionStateChanged_leaConnected_notInCallNotInSharing_addsPreference() { 293 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 294 mAudioManager.setMode(AudioManager.MODE_NORMAL); 295 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 296 .thenReturn(true); 297 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 298 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); 299 300 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 301 mCachedBluetoothDevice, 302 BluetoothProfile.STATE_CONNECTED, 303 BluetoothProfile.LE_AUDIO); 304 305 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 306 } 307 308 @Test onProfileConnectionStateChanged_leaConnected_inCallSharingFlagOff_addsPreference()309 public void onProfileConnectionStateChanged_leaConnected_inCallSharingFlagOff_addsPreference() { 310 mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 311 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 312 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 313 .thenReturn(true); 314 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 315 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState)); 316 List<Long> bisSyncState = new ArrayList<>(); 317 bisSyncState.add(1L); 318 when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); 319 320 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 321 mCachedBluetoothDevice, 322 BluetoothProfile.STATE_CONNECTED, 323 BluetoothProfile.LE_AUDIO); 324 325 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 326 } 327 328 @Test onProfileConnectionStateChanged_leaConnected_inCallNotInSharing_addsPreference()329 public void onProfileConnectionStateChanged_leaConnected_inCallNotInSharing_addsPreference() { 330 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 331 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 332 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 333 .thenReturn(true); 334 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 335 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); 336 337 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 338 mCachedBluetoothDevice, 339 BluetoothProfile.STATE_CONNECTED, 340 BluetoothProfile.LE_AUDIO); 341 342 verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); 343 } 344 345 @Test 346 public void onProfileConnectionStateChanged_leaDeviceConnected_notInCallInSharing_removesPref()347 onProfileConnectionStateChanged_leaDeviceConnected_notInCallInSharing_removesPref() { 348 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 349 mAudioManager.setMode(AudioManager.MODE_NORMAL); 350 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 351 .thenReturn(true); 352 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 353 when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true); 354 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState)); 355 List<Long> bisSyncState = new ArrayList<>(); 356 bisSyncState.add(1L); 357 when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); 358 359 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 360 mCachedBluetoothDevice, 361 BluetoothProfile.STATE_CONNECTED, 362 BluetoothProfile.LE_AUDIO); 363 364 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 365 } 366 367 @Test onProfileConnectionStateChanged_leaDeviceConnected_inCallInSharing_removesPref()368 public void onProfileConnectionStateChanged_leaDeviceConnected_inCallInSharing_removesPref() { 369 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 370 mAudioManager.setMode(AudioManager.MODE_NORMAL); 371 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 372 .thenReturn(true); 373 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 374 when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true); 375 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mBroadcastReceiveState)); 376 List<Long> bisSyncState = new ArrayList<>(); 377 bisSyncState.add(1L); 378 when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); 379 380 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 381 mCachedBluetoothDevice, 382 BluetoothProfile.STATE_CONNECTED, 383 BluetoothProfile.LE_AUDIO); 384 385 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 386 } 387 388 @Test 389 public void onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference()390 onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference() { 391 mAudioManager.setMode(AudioManager.MODE_NORMAL); 392 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 393 .thenReturn(true); 394 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 395 mCachedDevices.clear(); 396 397 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 398 mCachedBluetoothDevice, 399 BluetoothProfile.STATE_CONNECTED, 400 BluetoothProfile.LE_AUDIO); 401 402 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 403 } 404 405 @Test onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference()406 public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() { 407 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 408 when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class))) 409 .thenReturn(true); 410 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 411 mCachedDevices.clear(); 412 413 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 414 mCachedBluetoothDevice, 415 BluetoothProfile.STATE_CONNECTED, 416 BluetoothProfile.LE_AUDIO); 417 418 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 419 } 420 421 @Test onProfileConnectionStateChanged_deviceDisconnected_removePreference()422 public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() { 423 mBluetoothDeviceUpdater.onProfileConnectionStateChanged( 424 mCachedBluetoothDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP); 425 426 verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); 427 } 428 429 @Test onClick_Preference_setActive()430 public void onClick_Preference_setActive() { 431 mBluetoothDeviceUpdater.onPreferenceClick(mPreference); 432 433 verify(mDevicePreferenceCallback).onDeviceClick(mPreference); 434 } 435 } 436