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 17 package com.android.bluetooth.btservice; 18 19 import static org.mockito.Mockito.*; 20 21 import android.bluetooth.BluetoothA2dp; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothHeadset; 25 import android.bluetooth.BluetoothProfile; 26 import android.bluetooth.BluetoothUuid; 27 import android.content.BroadcastReceiver; 28 import android.content.Intent; 29 import android.os.HandlerThread; 30 import android.os.ParcelUuid; 31 import android.support.test.filters.MediumTest; 32 import android.support.test.runner.AndroidJUnit4; 33 34 import com.android.bluetooth.TestUtils; 35 import com.android.bluetooth.a2dp.A2dpService; 36 import com.android.bluetooth.hfp.HeadsetService; 37 38 import org.junit.After; 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.mockito.Mock; 43 import org.mockito.MockitoAnnotations; 44 45 import java.util.ArrayList; 46 import java.util.Collections; 47 48 @MediumTest 49 @RunWith(AndroidJUnit4.class) 50 public class PhonePolicyTest { 51 private static final int MAX_CONNECTED_AUDIO_DEVICES = 5; 52 private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250; 53 private static final int CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS = 1000; 54 private static final int CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS = 55 CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS * 3 / 2; 56 57 private HandlerThread mHandlerThread; 58 private BluetoothAdapter mAdapter; 59 private PhonePolicy mPhonePolicy; 60 61 @Mock private AdapterService mAdapterService; 62 @Mock private ServiceFactory mServiceFactory; 63 @Mock private HeadsetService mHeadsetService; 64 @Mock private A2dpService mA2dpService; 65 66 @Before setUp()67 public void setUp() throws Exception { 68 MockitoAnnotations.initMocks(this); 69 // Stub A2DP and HFP 70 when(mHeadsetService.connect(any(BluetoothDevice.class))).thenReturn(true); 71 when(mA2dpService.connect(any(BluetoothDevice.class))).thenReturn(true); 72 // Prepare the TestUtils 73 TestUtils.setAdapterService(mAdapterService); 74 // Configure the maximum connected audio devices 75 doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices(); 76 // Setup the mocked factory to return mocked services 77 doReturn(mHeadsetService).when(mServiceFactory).getHeadsetService(); 78 doReturn(mA2dpService).when(mServiceFactory).getA2dpService(); 79 // Start handler thread for this test 80 mHandlerThread = new HandlerThread("PhonePolicyTestHandlerThread"); 81 mHandlerThread.start(); 82 // Mock the looper 83 doReturn(mHandlerThread.getLooper()).when(mAdapterService).getMainLooper(); 84 // Tell the AdapterService that it is a mock (see isMock documentation) 85 doReturn(true).when(mAdapterService).isMock(); 86 // Must be called to initialize services 87 mAdapter = BluetoothAdapter.getDefaultAdapter(); 88 PhonePolicy.sConnectOtherProfilesTimeoutMillis = CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS; 89 mPhonePolicy = new PhonePolicy(mAdapterService, mServiceFactory); 90 } 91 92 @After tearDown()93 public void tearDown() throws Exception { 94 mHandlerThread.quit(); 95 TestUtils.clearAdapterService(mAdapterService); 96 } 97 98 /** 99 * Test that when new UUIDs are refreshed for a device then we set the priorities for various 100 * profiles accurately. The following profiles should have ON priorities: 101 * A2DP, HFP, HID and PAN 102 */ 103 @Test testProcessInitProfilePriorities()104 public void testProcessInitProfilePriorities() { 105 BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0); 106 // Mock the HeadsetService to return undefined priority 107 when(mHeadsetService.getPriority(device)).thenReturn(BluetoothProfile.PRIORITY_UNDEFINED); 108 109 // Mock the A2DP service to return undefined priority 110 when(mA2dpService.getPriority(device)).thenReturn(BluetoothProfile.PRIORITY_UNDEFINED); 111 112 // Inject an event for UUIDs updated for a remote device with only HFP enabled 113 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 114 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 115 ParcelUuid[] uuids = new ParcelUuid[2]; 116 uuids[0] = BluetoothUuid.Handsfree; 117 uuids[1] = BluetoothUuid.AudioSink; 118 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids); 119 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 120 121 // Check that the priorities of the devices for preferred profiles are set to ON 122 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(eq(device), 123 eq(BluetoothProfile.PRIORITY_ON)); 124 verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(eq(device), 125 eq(BluetoothProfile.PRIORITY_ON)); 126 } 127 128 /** 129 * Test that when the adapter is turned ON then we call autoconnect on devices that have HFP and 130 * A2DP enabled. NOTE that the assumption is that we have already done the pairing previously 131 * and hence the priorities for the device is already set to AUTO_CONNECT over HFP and A2DP (as 132 * part of post pairing process). 133 */ 134 @Test testAdapterOnAutoConnect()135 public void testAdapterOnAutoConnect() { 136 // Return desired values from the mocked object(s) 137 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 138 when(mAdapterService.isQuietModeEnabled()).thenReturn(false); 139 140 // Return a list of bonded devices (just one) 141 BluetoothDevice[] bondedDevices = new BluetoothDevice[1]; 142 bondedDevices[0] = TestUtils.getTestDevice(mAdapter, 0); 143 when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices); 144 145 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP 146 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 147 BluetoothProfile.PRIORITY_AUTO_CONNECT); 148 when(mA2dpService.getPriority(bondedDevices[0])).thenReturn( 149 BluetoothProfile.PRIORITY_AUTO_CONNECT); 150 151 // Inject an event that the adapter is turned on. 152 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); 153 intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON); 154 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 155 156 // Check that we got a request to connect over HFP and A2DP 157 verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(bondedDevices[0])); 158 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(bondedDevices[0])); 159 } 160 161 /** 162 * Test that when an auto connect device is disconnected, its priority is set to ON if its 163 * original priority is auto connect 164 */ 165 @Test testDisconnectNoAutoConnect()166 public void testDisconnectNoAutoConnect() { 167 // Return desired values from the mocked object(s) 168 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 169 when(mAdapterService.isQuietModeEnabled()).thenReturn(false); 170 171 // Return a list of bonded devices (just one) 172 BluetoothDevice[] bondedDevices = new BluetoothDevice[4]; 173 bondedDevices[0] = TestUtils.getTestDevice(mAdapter, 0); 174 bondedDevices[1] = TestUtils.getTestDevice(mAdapter, 1); 175 bondedDevices[2] = TestUtils.getTestDevice(mAdapter, 2); 176 bondedDevices[3] = TestUtils.getTestDevice(mAdapter, 3); 177 when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices); 178 179 // Make all devices auto connect 180 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 181 BluetoothProfile.PRIORITY_AUTO_CONNECT); 182 when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn( 183 BluetoothProfile.PRIORITY_AUTO_CONNECT); 184 when(mHeadsetService.getPriority(bondedDevices[2])).thenReturn( 185 BluetoothProfile.PRIORITY_AUTO_CONNECT); 186 when(mHeadsetService.getPriority(bondedDevices[3])).thenReturn( 187 BluetoothProfile.PRIORITY_OFF); 188 189 // Make one of the device active 190 Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 191 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]); 192 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 193 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 194 195 // All other disconnected device's priority is set to ON, except disabled ones 196 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[0], 197 BluetoothProfile.PRIORITY_ON); 198 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[1], 199 BluetoothProfile.PRIORITY_ON); 200 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[2], 201 BluetoothProfile.PRIORITY_ON); 202 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[0], 203 BluetoothProfile.PRIORITY_AUTO_CONNECT); 204 verify(mHeadsetService, never()).setPriority(eq(bondedDevices[3]), anyInt()); 205 when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn( 206 BluetoothProfile.PRIORITY_ON); 207 when(mHeadsetService.getPriority(bondedDevices[2])).thenReturn( 208 BluetoothProfile.PRIORITY_ON); 209 210 // Make another device active 211 when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn( 212 BluetoothProfile.STATE_CONNECTED); 213 intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 214 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[1]); 215 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 216 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 217 218 // This device should be set to auto connect while the first device is reset to ON 219 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).setPriority( 220 bondedDevices[0], BluetoothProfile.PRIORITY_ON); 221 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[1], 222 BluetoothProfile.PRIORITY_AUTO_CONNECT); 223 verify(mHeadsetService, never()).setPriority(eq(bondedDevices[3]), anyInt()); 224 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 225 BluetoothProfile.PRIORITY_ON); 226 when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn( 227 BluetoothProfile.PRIORITY_AUTO_CONNECT); 228 229 // Set active device to null 230 when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn( 231 BluetoothProfile.STATE_DISCONNECTED); 232 intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 233 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, (BluetoothDevice) null); 234 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 235 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 236 237 // This should not have any effect 238 verify(mHeadsetService, after(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).setPriority( 239 bondedDevices[1], BluetoothProfile.PRIORITY_ON); 240 241 // Make the current active device fail to connect 242 when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn( 243 BluetoothProfile.STATE_DISCONNECTED); 244 when(mA2dpService.getConnectionState(bondedDevices[1])).thenReturn( 245 BluetoothProfile.STATE_DISCONNECTED); 246 intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 247 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[1]); 248 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 249 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); 250 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 251 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 252 253 // This device should be set to ON 254 verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).setPriority( 255 bondedDevices[1], BluetoothProfile.PRIORITY_ON); 256 257 // Verify that we are not setting priorities to random devices and values 258 verify(mHeadsetService, times(7)).setPriority(any(BluetoothDevice.class), anyInt()); 259 } 260 261 /** 262 * Test that we will try to re-connect to a profile on a device if an attempt failed previously. 263 * This is to add robustness to the connection mechanism 264 */ 265 @Test testReconnectOnPartialConnect()266 public void testReconnectOnPartialConnect() { 267 // Return a list of bonded devices (just one) 268 BluetoothDevice[] bondedDevices = new BluetoothDevice[1]; 269 bondedDevices[0] = TestUtils.getTestDevice(mAdapter, 0); 270 when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices); 271 272 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles are 273 // auto-connectable. 274 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 275 BluetoothProfile.PRIORITY_AUTO_CONNECT); 276 when(mA2dpService.getPriority(bondedDevices[0])).thenReturn( 277 BluetoothProfile.PRIORITY_AUTO_CONNECT); 278 279 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 280 281 // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP 282 // To enable that we need to make sure that HeadsetService returns the device as list of 283 // connected devices 284 ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>(); 285 hsConnectedDevices.add(bondedDevices[0]); 286 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 287 // Also the A2DP should say that its not connected for same device 288 when(mA2dpService.getConnectionState(bondedDevices[0])).thenReturn( 289 BluetoothProfile.STATE_DISCONNECTED); 290 291 // We send a connection successful for one profile since the re-connect *only* works if we 292 // have already connected successfully over one of the profiles 293 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 294 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]); 295 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 296 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 297 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 298 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 299 300 // Check that we get a call to A2DP connect 301 verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect( 302 eq(bondedDevices[0])); 303 } 304 305 /** 306 * Test that a second device will auto-connect if there is already one connected device. 307 * 308 * Even though we currently only set one device to be auto connect. The consumer of the auto 309 * connect property works independently so that we will connect to all devices that are in 310 * auto connect mode. 311 */ 312 @Test testAutoConnectMultipleDevices()313 public void testAutoConnectMultipleDevices() { 314 final int kMaxTestDevices = 3; 315 BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices]; 316 ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>(); 317 ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>(); 318 BluetoothDevice a2dpNotConnectedDevice1 = null; 319 BluetoothDevice a2dpNotConnectedDevice2 = null; 320 321 for (int i = 0; i < kMaxTestDevices; i++) { 322 BluetoothDevice testDevice = TestUtils.getTestDevice(mAdapter, i); 323 testDevices[i] = testDevice; 324 325 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles 326 // are auto-connectable. 327 when(mHeadsetService.getPriority(testDevice)).thenReturn( 328 BluetoothProfile.PRIORITY_AUTO_CONNECT); 329 when(mA2dpService.getPriority(testDevice)).thenReturn( 330 BluetoothProfile.PRIORITY_AUTO_CONNECT); 331 // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP 332 // To enable that we need to make sure that HeadsetService returns the device as list 333 // of connected devices. 334 hsConnectedDevices.add(testDevice); 335 // Connect A2DP for all devices except the last one 336 if (i < (kMaxTestDevices - 2)) { 337 a2dpConnectedDevices.add(testDevice); 338 } 339 } 340 a2dpNotConnectedDevice1 = hsConnectedDevices.get(kMaxTestDevices - 1); 341 a2dpNotConnectedDevice2 = hsConnectedDevices.get(kMaxTestDevices - 2); 342 343 when(mAdapterService.getBondedDevices()).thenReturn(testDevices); 344 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 345 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 346 when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices); 347 // Two of the A2DP devices are not connected 348 when(mA2dpService.getConnectionState(a2dpNotConnectedDevice1)).thenReturn( 349 BluetoothProfile.STATE_DISCONNECTED); 350 when(mA2dpService.getConnectionState(a2dpNotConnectedDevice2)).thenReturn( 351 BluetoothProfile.STATE_DISCONNECTED); 352 353 // We send a connection successful for one profile since the re-connect *only* works if we 354 // have already connected successfully over one of the profiles 355 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 356 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, a2dpNotConnectedDevice1); 357 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 358 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 359 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 360 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 361 362 // We send a connection successful for one profile since the re-connect *only* works if we 363 // have already connected successfully over one of the profiles 364 intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 365 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, a2dpNotConnectedDevice2); 366 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 367 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 368 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 369 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 370 371 // Check that we get a call to A2DP connect 372 verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect( 373 eq(a2dpNotConnectedDevice1)); 374 verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect( 375 eq(a2dpNotConnectedDevice2)); 376 } 377 378 /** 379 * Test that the connect priority of all devices are set as appropriate if there is one 380 * connected device. 381 * - The HFP and A2DP connect priority for connected devices is set to 382 * BluetoothProfile.PRIORITY_AUTO_CONNECT 383 * - The HFP and A2DP connect priority for bonded devices is set to 384 * BluetoothProfile.PRIORITY_ON 385 */ 386 @Test testSetPriorityMultipleDevices()387 public void testSetPriorityMultipleDevices() { 388 // testDevices[0] - connected for both HFP and A2DP 389 // testDevices[1] - connected only for HFP - will auto-connect for A2DP 390 // testDevices[2] - connected only for A2DP - will auto-connect for HFP 391 // testDevices[3] - not connected 392 final int kMaxTestDevices = 4; 393 BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices]; 394 ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>(); 395 ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>(); 396 397 for (int i = 0; i < kMaxTestDevices; i++) { 398 BluetoothDevice testDevice = TestUtils.getTestDevice(mAdapter, i); 399 testDevices[i] = testDevice; 400 401 // Connect HFP and A2DP for each device as appropriate. 402 // Return PRIORITY_AUTO_CONNECT only for testDevices[0] 403 if (i == 0) { 404 hsConnectedDevices.add(testDevice); 405 a2dpConnectedDevices.add(testDevice); 406 when(mHeadsetService.getPriority(testDevice)).thenReturn( 407 BluetoothProfile.PRIORITY_AUTO_CONNECT); 408 when(mA2dpService.getPriority(testDevice)).thenReturn( 409 BluetoothProfile.PRIORITY_AUTO_CONNECT); 410 } 411 if (i == 1) { 412 hsConnectedDevices.add(testDevice); 413 when(mHeadsetService.getPriority(testDevice)).thenReturn( 414 BluetoothProfile.PRIORITY_ON); 415 when(mA2dpService.getPriority(testDevice)).thenReturn(BluetoothProfile.PRIORITY_ON); 416 } 417 if (i == 2) { 418 a2dpConnectedDevices.add(testDevice); 419 when(mHeadsetService.getPriority(testDevice)).thenReturn( 420 BluetoothProfile.PRIORITY_ON); 421 when(mA2dpService.getPriority(testDevice)).thenReturn(BluetoothProfile.PRIORITY_ON); 422 } 423 if (i == 3) { 424 // Device not connected 425 when(mHeadsetService.getPriority(testDevice)).thenReturn( 426 BluetoothProfile.PRIORITY_ON); 427 when(mA2dpService.getPriority(testDevice)).thenReturn(BluetoothProfile.PRIORITY_ON); 428 } 429 } 430 when(mAdapterService.getBondedDevices()).thenReturn(testDevices); 431 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 432 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 433 when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices); 434 // Some of the devices are not connected 435 // testDevices[0] - connected for both HFP and A2DP 436 when(mHeadsetService.getConnectionState(testDevices[0])).thenReturn( 437 BluetoothProfile.STATE_CONNECTED); 438 when(mA2dpService.getConnectionState(testDevices[0])).thenReturn( 439 BluetoothProfile.STATE_CONNECTED); 440 // testDevices[1] - connected only for HFP - will auto-connect for A2DP 441 when(mHeadsetService.getConnectionState(testDevices[1])).thenReturn( 442 BluetoothProfile.STATE_CONNECTED); 443 when(mA2dpService.getConnectionState(testDevices[1])).thenReturn( 444 BluetoothProfile.STATE_DISCONNECTED); 445 // testDevices[2] - connected only for A2DP - will auto-connect for HFP 446 when(mHeadsetService.getConnectionState(testDevices[2])).thenReturn( 447 BluetoothProfile.STATE_DISCONNECTED); 448 when(mA2dpService.getConnectionState(testDevices[2])).thenReturn( 449 BluetoothProfile.STATE_CONNECTED); 450 // testDevices[3] - not connected 451 when(mHeadsetService.getConnectionState(testDevices[3])).thenReturn( 452 BluetoothProfile.STATE_DISCONNECTED); 453 when(mA2dpService.getConnectionState(testDevices[3])).thenReturn( 454 BluetoothProfile.STATE_DISCONNECTED); 455 456 // Get the broadcast receiver to inject events 457 BroadcastReceiver injector = mPhonePolicy.getBroadcastReceiver(); 458 459 // Generate connection state changed for HFP for testDevices[1] and trigger 460 // auto-connect for A2DP. 461 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 462 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, testDevices[1]); 463 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 464 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 465 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 466 injector.onReceive(null /* context */, intent); 467 // Check that we get a call to A2DP connect 468 verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect( 469 eq(testDevices[1])); 470 471 // testDevices[1] auto-connect completed for A2DP 472 a2dpConnectedDevices.add(testDevices[1]); 473 when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices); 474 when(mA2dpService.getConnectionState(testDevices[1])).thenReturn( 475 BluetoothProfile.STATE_CONNECTED); 476 477 // Check the connect priorities for all devices 478 // - testDevices[0] - connected for HFP and A2DP: setPriority() should not be called 479 // - testDevices[1] - connection state changed for HFP should no longer trigger auto 480 // connect priority change since it is now triggered by A2DP active 481 // device change intent 482 // - testDevices[2] - connected for A2DP: setPriority() should not be called 483 // - testDevices[3] - not connected for HFP nor A2DP: setPriority() should not be called 484 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[0]), anyInt()); 485 verify(mA2dpService, times(0)).setPriority(eq(testDevices[0]), anyInt()); 486 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[1]), 487 eq(BluetoothProfile.PRIORITY_AUTO_CONNECT)); 488 verify(mA2dpService, times(0)).setPriority(eq(testDevices[1]), anyInt()); 489 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[2]), anyInt()); 490 verify(mA2dpService, times(0)).setPriority(eq(testDevices[2]), anyInt()); 491 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[3]), anyInt()); 492 verify(mA2dpService, times(0)).setPriority(eq(testDevices[3]), anyInt()); 493 clearInvocations(mHeadsetService, mA2dpService); 494 495 // Generate connection state changed for A2DP for testDevices[2] and trigger 496 // auto-connect for HFP. 497 intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 498 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, testDevices[2]); 499 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 500 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 501 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 502 injector.onReceive(null /* context */, intent); 503 // Check that we get a call to HFP connect 504 verify(mHeadsetService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect( 505 eq(testDevices[2])); 506 507 // testDevices[2] auto-connect completed for HFP 508 hsConnectedDevices.add(testDevices[2]); 509 when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices); 510 when(mHeadsetService.getConnectionState(testDevices[2])).thenReturn( 511 BluetoothProfile.STATE_CONNECTED); 512 513 // Check the connect priorities for all devices 514 // - testDevices[0] - connected for HFP and A2DP: setPriority() should not be called 515 // - testDevices[1] - connected for HFP and A2DP: setPriority() should not be called 516 // - testDevices[2] - connection state changed for A2DP should no longer trigger auto 517 // connect priority change since it is now triggered by A2DP 518 // active device change intent 519 // - testDevices[3] - not connected for HFP nor A2DP: setPriority() should not be called 520 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[0]), anyInt()); 521 verify(mA2dpService, times(0)).setPriority(eq(testDevices[0]), anyInt()); 522 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[1]), anyInt()); 523 verify(mA2dpService, times(0)).setPriority(eq(testDevices[1]), anyInt()); 524 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[2]), anyInt()); 525 verify(mA2dpService, times(0)).setPriority(eq(testDevices[2]), 526 eq(BluetoothProfile.PRIORITY_AUTO_CONNECT)); 527 verify(mHeadsetService, times(0)).setPriority(eq(testDevices[3]), anyInt()); 528 verify(mA2dpService, times(0)).setPriority(eq(testDevices[3]), anyInt()); 529 clearInvocations(mHeadsetService, mA2dpService); 530 } 531 532 /** 533 * Test that we will not try to reconnect on a profile if all the connections failed 534 */ 535 @Test testNoReconnectOnNoConnect()536 public void testNoReconnectOnNoConnect() { 537 // Return a list of bonded devices (just one) 538 BluetoothDevice[] bondedDevices = new BluetoothDevice[1]; 539 bondedDevices[0] = TestUtils.getTestDevice(mAdapter, 0); 540 when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices); 541 542 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles are 543 // auto-connectable. 544 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 545 BluetoothProfile.PRIORITY_AUTO_CONNECT); 546 when(mA2dpService.getPriority(bondedDevices[0])).thenReturn( 547 BluetoothProfile.PRIORITY_AUTO_CONNECT); 548 549 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 550 551 // Return an empty list simulating that the above connection successful was nullified 552 when(mHeadsetService.getConnectedDevices()).thenReturn(Collections.emptyList()); 553 when(mA2dpService.getConnectedDevices()).thenReturn(Collections.emptyList()); 554 555 // Both A2DP and HFP should say this device is not connected, except for the intent 556 when(mA2dpService.getConnectionState(bondedDevices[0])).thenReturn( 557 BluetoothProfile.STATE_DISCONNECTED); 558 when(mHeadsetService.getConnectionState(bondedDevices[0])).thenReturn( 559 BluetoothProfile.STATE_DISCONNECTED); 560 561 // We send a connection successful for one profile since the re-connect *only* works if we 562 // have already connected successfully over one of the profiles 563 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 564 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]); 565 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 566 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 567 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 568 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 569 570 // Check that we don't get any calls to reconnect 571 verify(mA2dpService, after(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS).never()).connect( 572 eq(bondedDevices[0])); 573 verify(mHeadsetService, never()).connect(eq(bondedDevices[0])); 574 } 575 576 /** 577 * Test that we will not try to reconnect on a profile if all the connections failed 578 * with multiple devices 579 */ 580 @Test testNoReconnectOnNoConnect_MultiDevice()581 public void testNoReconnectOnNoConnect_MultiDevice() { 582 // Return a list of bonded devices (just one) 583 BluetoothDevice[] bondedDevices = new BluetoothDevice[2]; 584 bondedDevices[0] = TestUtils.getTestDevice(mAdapter, 0); 585 bondedDevices[1] = TestUtils.getTestDevice(mAdapter, 1); 586 when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices); 587 588 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles are 589 // auto-connectable. 590 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 591 BluetoothProfile.PRIORITY_AUTO_CONNECT); 592 when(mA2dpService.getPriority(bondedDevices[0])).thenReturn( 593 BluetoothProfile.PRIORITY_AUTO_CONNECT); 594 when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn( 595 BluetoothProfile.PRIORITY_AUTO_CONNECT); 596 when(mA2dpService.getPriority(bondedDevices[1])).thenReturn( 597 BluetoothProfile.PRIORITY_AUTO_CONNECT); 598 599 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 600 601 // Return an a list with only the second device as connected 602 when(mHeadsetService.getConnectedDevices()).thenReturn( 603 Collections.singletonList(bondedDevices[1])); 604 when(mA2dpService.getConnectedDevices()).thenReturn( 605 Collections.singletonList(bondedDevices[1])); 606 607 // Both A2DP and HFP should say this device is not connected, except for the intent 608 when(mA2dpService.getConnectionState(bondedDevices[0])).thenReturn( 609 BluetoothProfile.STATE_DISCONNECTED); 610 when(mHeadsetService.getConnectionState(bondedDevices[0])).thenReturn( 611 BluetoothProfile.STATE_DISCONNECTED); 612 when(mA2dpService.getConnectionState(bondedDevices[1])).thenReturn( 613 BluetoothProfile.STATE_CONNECTED); 614 when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn( 615 BluetoothProfile.STATE_CONNECTED); 616 617 // We send a connection successful for one profile since the re-connect *only* works if we 618 // have already connected successfully over one of the profiles 619 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 620 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]); 621 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 622 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 623 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 624 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 625 626 // Check that we don't get any calls to reconnect 627 verify(mA2dpService, after(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS).never()).connect( 628 eq(bondedDevices[0])); 629 verify(mHeadsetService, never()).connect(eq(bondedDevices[0])); 630 } 631 632 /** 633 * Test that we will try to connect to other profiles of a device if it is partially connected 634 */ 635 @Test testReconnectOnPartialConnect_MultiDevice()636 public void testReconnectOnPartialConnect_MultiDevice() { 637 // Return a list of bonded devices (just one) 638 BluetoothDevice[] bondedDevices = new BluetoothDevice[2]; 639 bondedDevices[0] = TestUtils.getTestDevice(mAdapter, 0); 640 bondedDevices[1] = TestUtils.getTestDevice(mAdapter, 1); 641 when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices); 642 643 // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles are 644 // auto-connectable. 645 when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn( 646 BluetoothProfile.PRIORITY_AUTO_CONNECT); 647 when(mA2dpService.getPriority(bondedDevices[0])).thenReturn( 648 BluetoothProfile.PRIORITY_AUTO_CONNECT); 649 when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn( 650 BluetoothProfile.PRIORITY_AUTO_CONNECT); 651 when(mA2dpService.getPriority(bondedDevices[1])).thenReturn( 652 BluetoothProfile.PRIORITY_AUTO_CONNECT); 653 654 when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); 655 656 // Return an a list with only the second device as connected 657 when(mHeadsetService.getConnectedDevices()).thenReturn( 658 Collections.singletonList(bondedDevices[1])); 659 when(mA2dpService.getConnectedDevices()).thenReturn(Collections.emptyList()); 660 661 // Both A2DP and HFP should say this device is not connected, except for the intent 662 when(mA2dpService.getConnectionState(bondedDevices[0])).thenReturn( 663 BluetoothProfile.STATE_DISCONNECTED); 664 when(mHeadsetService.getConnectionState(bondedDevices[0])).thenReturn( 665 BluetoothProfile.STATE_DISCONNECTED); 666 when(mA2dpService.getConnectionState(bondedDevices[1])).thenReturn( 667 BluetoothProfile.STATE_DISCONNECTED); 668 when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn( 669 BluetoothProfile.STATE_CONNECTED); 670 671 // We send a connection successful for one profile since the re-connect *only* works if we 672 // have already connected successfully over one of the profiles 673 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 674 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[1]); 675 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 676 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 677 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 678 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 679 680 // Check that we don't get any calls to reconnect 681 verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect( 682 eq(bondedDevices[1])); 683 } 684 685 /** 686 * Test that a device with no supported uuids is initialized properly and does not crash the 687 * stack 688 */ 689 @Test testNoSupportedUuids()690 public void testNoSupportedUuids() { 691 // Mock the HeadsetService to return undefined priority 692 BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0); 693 when(mHeadsetService.getPriority(device)).thenReturn(BluetoothProfile.PRIORITY_UNDEFINED); 694 695 // Mock the A2DP service to return undefined priority 696 when(mA2dpService.getPriority(device)).thenReturn(BluetoothProfile.PRIORITY_UNDEFINED); 697 698 // Inject an event for UUIDs updated for a remote device with only HFP enabled 699 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 700 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 701 702 // Put no UUIDs 703 mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent); 704 705 // Check that we do not crash and not call any setPriority methods 706 verify(mHeadsetService, 707 after(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS).never()).setPriority(eq(device), 708 eq(BluetoothProfile.PRIORITY_ON)); 709 verify(mA2dpService, never()).setPriority(eq(device), eq(BluetoothProfile.PRIORITY_ON)); 710 } 711 } 712