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 17 package com.android.bluetooth.a2dp; 18 19 import static org.mockito.Mockito.*; 20 21 import android.bluetooth.BluetoothA2dp; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothCodecConfig; 24 import android.bluetooth.BluetoothCodecStatus; 25 import android.bluetooth.BluetoothDevice; 26 import android.bluetooth.BluetoothProfile; 27 import android.bluetooth.BluetoothUuid; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.os.Looper; 33 import android.os.ParcelUuid; 34 35 import androidx.test.InstrumentationRegistry; 36 import androidx.test.filters.MediumTest; 37 import androidx.test.rule.ServiceTestRule; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.bluetooth.R; 41 import com.android.bluetooth.TestUtils; 42 import com.android.bluetooth.btservice.AdapterService; 43 import com.android.bluetooth.btservice.storage.DatabaseManager; 44 45 import org.junit.After; 46 import org.junit.Assert; 47 import org.junit.Assume; 48 import org.junit.Before; 49 import org.junit.Rule; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 55 import java.util.List; 56 import java.util.concurrent.BlockingQueue; 57 import java.util.concurrent.LinkedBlockingQueue; 58 import java.util.concurrent.TimeoutException; 59 60 @MediumTest 61 @RunWith(AndroidJUnit4.class) 62 public class A2dpServiceTest { 63 private static final int MAX_CONNECTED_AUDIO_DEVICES = 5; 64 65 private BluetoothAdapter mAdapter; 66 private Context mTargetContext; 67 private A2dpService mA2dpService; 68 private BluetoothDevice mTestDevice; 69 private static final int TIMEOUT_MS = 1000; // 1s 70 71 private BroadcastReceiver mA2dpIntentReceiver; 72 private final BlockingQueue<Intent> mConnectionStateChangedQueue = new LinkedBlockingQueue<>(); 73 private final BlockingQueue<Intent> mAudioStateChangedQueue = new LinkedBlockingQueue<>(); 74 private final BlockingQueue<Intent> mCodecConfigChangedQueue = new LinkedBlockingQueue<>(); 75 76 @Mock private AdapterService mAdapterService; 77 @Mock private A2dpNativeInterface mA2dpNativeInterface; 78 @Mock private DatabaseManager mDatabaseManager; 79 80 @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); 81 82 @Before setUp()83 public void setUp() throws Exception { 84 mTargetContext = InstrumentationRegistry.getTargetContext(); 85 Assume.assumeTrue("Ignore test when A2dpService is not enabled", 86 mTargetContext.getResources().getBoolean(R.bool.profile_supported_a2dp)); 87 // Set up mocks and test assets 88 MockitoAnnotations.initMocks(this); 89 90 if (Looper.myLooper() == null) { 91 Looper.prepare(); 92 } 93 94 TestUtils.setAdapterService(mAdapterService); 95 doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices(); 96 doReturn(true, false).when(mAdapterService).isStartedProfile(anyString()); 97 doReturn(false).when(mAdapterService).isQuietModeEnabled(); 98 doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); 99 100 mAdapter = BluetoothAdapter.getDefaultAdapter(); 101 102 startService(); 103 mA2dpService.mA2dpNativeInterface = mA2dpNativeInterface; 104 105 // Override the timeout value to speed up the test 106 A2dpStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s 107 108 // Set up the Connection State Changed receiver 109 IntentFilter filter = new IntentFilter(); 110 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 111 filter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 112 filter.addAction(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); 113 mA2dpIntentReceiver = new A2dpIntentReceiver(); 114 mTargetContext.registerReceiver(mA2dpIntentReceiver, filter); 115 116 // Get a device for testing 117 mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); 118 doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService) 119 .getBondState(any(BluetoothDevice.class)); 120 doReturn(new ParcelUuid[]{BluetoothUuid.A2DP_SINK}).when(mAdapterService) 121 .getRemoteUuids(any(BluetoothDevice.class)); 122 } 123 124 @After tearDown()125 public void tearDown() throws Exception { 126 if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_a2dp)) { 127 return; 128 } 129 stopService(); 130 mTargetContext.unregisterReceiver(mA2dpIntentReceiver); 131 mConnectionStateChangedQueue.clear(); 132 mAudioStateChangedQueue.clear(); 133 mCodecConfigChangedQueue.clear(); 134 TestUtils.clearAdapterService(mAdapterService); 135 } 136 startService()137 private void startService() throws TimeoutException { 138 TestUtils.startService(mServiceRule, A2dpService.class); 139 mA2dpService = A2dpService.getA2dpService(); 140 Assert.assertNotNull(mA2dpService); 141 } 142 stopService()143 private void stopService() throws TimeoutException { 144 TestUtils.stopService(mServiceRule, A2dpService.class); 145 mA2dpService = A2dpService.getA2dpService(); 146 Assert.assertNull(mA2dpService); 147 } 148 149 private class A2dpIntentReceiver extends BroadcastReceiver { 150 @Override onReceive(Context context, Intent intent)151 public void onReceive(Context context, Intent intent) { 152 if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 153 try { 154 mConnectionStateChangedQueue.put(intent); 155 } catch (InterruptedException e) { 156 Assert.fail("Cannot add Intent to the Connection State queue: " 157 + e.getMessage()); 158 } 159 } 160 if (BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED.equals(intent.getAction())) { 161 try { 162 mAudioStateChangedQueue.put(intent); 163 } catch (InterruptedException e) { 164 Assert.fail("Cannot add Intent to the Audio State queue: " + e.getMessage()); 165 } 166 } 167 if (BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED.equals(intent.getAction())) { 168 try { 169 mCodecConfigChangedQueue.put(intent); 170 } catch (InterruptedException e) { 171 Assert.fail("Cannot add Intent to the Codec Config queue: " + e.getMessage()); 172 } 173 } 174 } 175 } 176 verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)177 private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, 178 int newState, int prevState) { 179 Intent intent = TestUtils.waitForIntent(timeoutMs, mConnectionStateChangedQueue); 180 Assert.assertNotNull(intent); 181 Assert.assertEquals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, 182 intent.getAction()); 183 Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 184 Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 185 Assert.assertEquals(prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 186 -1)); 187 } 188 verifyNoConnectionStateIntent(int timeoutMs)189 private void verifyNoConnectionStateIntent(int timeoutMs) { 190 Intent intent = TestUtils.waitForNoIntent(timeoutMs, mConnectionStateChangedQueue); 191 Assert.assertNull(intent); 192 } 193 verifyAudioStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)194 private void verifyAudioStateIntent(int timeoutMs, BluetoothDevice device, 195 int newState, int prevState) { 196 Intent intent = TestUtils.waitForIntent(timeoutMs, mAudioStateChangedQueue); 197 Assert.assertNotNull(intent); 198 Assert.assertEquals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED, intent.getAction()); 199 Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 200 Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 201 Assert.assertEquals(prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 202 -1)); 203 } 204 verifyNoAudioStateIntent(int timeoutMs)205 private void verifyNoAudioStateIntent(int timeoutMs) { 206 Intent intent = TestUtils.waitForNoIntent(timeoutMs, mAudioStateChangedQueue); 207 Assert.assertNull(intent); 208 } 209 verifyCodecConfigIntent(int timeoutMs, BluetoothDevice device, BluetoothCodecStatus codecStatus)210 private void verifyCodecConfigIntent(int timeoutMs, BluetoothDevice device, 211 BluetoothCodecStatus codecStatus) { 212 Intent intent = TestUtils.waitForIntent(timeoutMs, mCodecConfigChangedQueue); 213 Assert.assertNotNull(intent); 214 Assert.assertEquals(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED, intent.getAction()); 215 Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 216 Assert.assertEquals(codecStatus, 217 intent.getParcelableExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS)); 218 } 219 verifyNoCodecConfigIntent(int timeoutMs)220 private void verifyNoCodecConfigIntent(int timeoutMs) { 221 Intent intent = TestUtils.waitForNoIntent(timeoutMs, mCodecConfigChangedQueue); 222 Assert.assertNull(intent); 223 } 224 225 /** 226 * Test getting A2DP Service: getA2dpService() 227 */ 228 @Test testGetA2dpService()229 public void testGetA2dpService() { 230 Assert.assertEquals(mA2dpService, A2dpService.getA2dpService()); 231 } 232 233 /** 234 * Test stop A2DP Service 235 */ 236 @Test testStopA2dpService()237 public void testStopA2dpService() { 238 // Prepare: connect and set active device 239 doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class)); 240 connectDevice(mTestDevice); 241 Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice)); 242 verify(mA2dpNativeInterface).setActiveDevice(mTestDevice); 243 // A2DP Service is already running: test stop(). Note: must be done on the main thread. 244 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 245 public void run() { 246 Assert.assertTrue(mA2dpService.stop()); 247 } 248 }); 249 // Verify that setActiveDevice(null) was called during shutdown 250 verify(mA2dpNativeInterface).setActiveDevice(null); 251 // Try to restart the service. Note: must be done on the main thread. 252 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 253 public void run() { 254 Assert.assertTrue(mA2dpService.start()); 255 } 256 }); 257 } 258 259 /** 260 * Test get priority for BluetoothDevice 261 */ 262 @Test testGetPriority()263 public void testGetPriority() { 264 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 265 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 266 Assert.assertEquals("Initial device priority", 267 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, 268 mA2dpService.getConnectionPolicy(mTestDevice)); 269 270 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 271 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 272 Assert.assertEquals("Setting device priority to PRIORITY_OFF", 273 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, 274 mA2dpService.getConnectionPolicy(mTestDevice)); 275 276 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 277 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 278 Assert.assertEquals("Setting device priority to PRIORITY_ON", 279 BluetoothProfile.CONNECTION_POLICY_ALLOWED, 280 mA2dpService.getConnectionPolicy(mTestDevice)); 281 } 282 283 /** 284 * Test okToConnect method using various test cases 285 */ 286 @Test testOkToConnect()287 public void testOkToConnect() { 288 int badPriorityValue = 1024; 289 int badBondState = 42; 290 testOkToConnectCase(mTestDevice, 291 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 292 testOkToConnectCase(mTestDevice, 293 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 294 testOkToConnectCase(mTestDevice, 295 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 296 testOkToConnectCase(mTestDevice, 297 BluetoothDevice.BOND_NONE, badPriorityValue, false); 298 testOkToConnectCase(mTestDevice, 299 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 300 testOkToConnectCase(mTestDevice, 301 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 302 testOkToConnectCase(mTestDevice, 303 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 304 testOkToConnectCase(mTestDevice, 305 BluetoothDevice.BOND_BONDING, badPriorityValue, false); 306 testOkToConnectCase(mTestDevice, 307 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true); 308 testOkToConnectCase(mTestDevice, 309 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 310 testOkToConnectCase(mTestDevice, 311 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_ALLOWED, true); 312 testOkToConnectCase(mTestDevice, 313 BluetoothDevice.BOND_BONDED, badPriorityValue, false); 314 testOkToConnectCase(mTestDevice, 315 badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 316 testOkToConnectCase(mTestDevice, 317 badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 318 testOkToConnectCase(mTestDevice, 319 badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 320 testOkToConnectCase(mTestDevice, 321 badBondState, badPriorityValue, false); 322 } 323 324 325 /** 326 * Test that an outgoing connection to device that does not have A2DP Sink UUID is rejected 327 */ 328 @Test testOutgoingConnectMissingAudioSinkUuid()329 public void testOutgoingConnectMissingAudioSinkUuid() { 330 // Update the device priority so okToConnect() returns true 331 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 332 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 333 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 334 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 335 336 // Return AudioSource UUID instead of AudioSink 337 doReturn(new ParcelUuid[]{BluetoothUuid.A2DP_SOURCE}).when(mAdapterService) 338 .getRemoteUuids(any(BluetoothDevice.class)); 339 340 // Send a connect request 341 Assert.assertFalse("Connect expected to fail", mA2dpService.connect(mTestDevice)); 342 } 343 344 /** 345 * Test that an outgoing connection to device with PRIORITY_OFF is rejected 346 */ 347 @Test testOutgoingConnectPriorityOff()348 public void testOutgoingConnectPriorityOff() { 349 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 350 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 351 352 // Set the device priority to PRIORITY_OFF so connect() should fail 353 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 354 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 355 356 // Send a connect request 357 Assert.assertFalse("Connect expected to fail", mA2dpService.connect(mTestDevice)); 358 } 359 360 /** 361 * Test that an outgoing connection times out 362 */ 363 @Test testOutgoingConnectTimeout()364 public void testOutgoingConnectTimeout() { 365 // Update the device priority so okToConnect() returns true 366 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 367 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 368 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 369 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 370 371 // Send a connect request 372 Assert.assertTrue("Connect failed", mA2dpService.connect(mTestDevice)); 373 374 // Verify the connection state broadcast, and that we are in Connecting state 375 verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_CONNECTING, 376 BluetoothProfile.STATE_DISCONNECTED); 377 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 378 mA2dpService.getConnectionState(mTestDevice)); 379 380 // Verify the connection state broadcast, and that we are in Disconnected state 381 verifyConnectionStateIntent(A2dpStateMachine.sConnectTimeoutMs * 2, 382 mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 383 BluetoothProfile.STATE_CONNECTING); 384 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 385 mA2dpService.getConnectionState(mTestDevice)); 386 } 387 388 /** 389 * Test that an outgoing connection/disconnection succeeds 390 */ 391 @Test testOutgoingConnectDisconnectSuccess()392 public void testOutgoingConnectDisconnectSuccess() { 393 A2dpStackEvent connCompletedEvent; 394 395 // Update the device priority so okToConnect() returns true 396 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 397 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 398 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 399 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 400 401 // Send a connect request 402 Assert.assertTrue("Connect failed", mA2dpService.connect(mTestDevice)); 403 404 // Verify the connection state broadcast, and that we are in Connecting state 405 verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_CONNECTING, 406 BluetoothProfile.STATE_DISCONNECTED); 407 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 408 mA2dpService.getConnectionState(mTestDevice)); 409 410 // Send a message to trigger connection completed 411 connCompletedEvent = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 412 connCompletedEvent.device = mTestDevice; 413 connCompletedEvent.valueInt = A2dpStackEvent.CONNECTION_STATE_CONNECTED; 414 mA2dpService.messageFromNative(connCompletedEvent); 415 416 // Verify the connection state broadcast, and that we are in Connected state 417 verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_CONNECTED, 418 BluetoothProfile.STATE_CONNECTING); 419 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 420 mA2dpService.getConnectionState(mTestDevice)); 421 422 // Verify the list of connected devices 423 Assert.assertTrue(mA2dpService.getConnectedDevices().contains(mTestDevice)); 424 425 // Send a disconnect request 426 Assert.assertTrue("Disconnect failed", mA2dpService.disconnect(mTestDevice)); 427 428 // Verify the connection state broadcast, and that we are in Disconnecting state 429 verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_DISCONNECTING, 430 BluetoothProfile.STATE_CONNECTED); 431 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING, 432 mA2dpService.getConnectionState(mTestDevice)); 433 434 // Send a message to trigger disconnection completed 435 connCompletedEvent = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 436 connCompletedEvent.device = mTestDevice; 437 connCompletedEvent.valueInt = A2dpStackEvent.CONNECTION_STATE_DISCONNECTED; 438 mA2dpService.messageFromNative(connCompletedEvent); 439 440 // Verify the connection state broadcast, and that we are in Disconnected state 441 verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 442 BluetoothProfile.STATE_DISCONNECTING); 443 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 444 mA2dpService.getConnectionState(mTestDevice)); 445 446 // Verify the list of connected devices 447 Assert.assertFalse(mA2dpService.getConnectedDevices().contains(mTestDevice)); 448 } 449 450 /** 451 * Test that an outgoing connection/disconnection succeeds 452 */ 453 @Test testMaxConnectDevices()454 public void testMaxConnectDevices() { 455 A2dpStackEvent connCompletedEvent; 456 BluetoothDevice[] testDevices = new BluetoothDevice[MAX_CONNECTED_AUDIO_DEVICES]; 457 BluetoothDevice extraTestDevice; 458 459 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 460 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 461 462 // Prepare and connect all test devices 463 for (int i = 0; i < MAX_CONNECTED_AUDIO_DEVICES; i++) { 464 BluetoothDevice testDevice = TestUtils.getTestDevice(mAdapter, i); 465 testDevices[i] = testDevice; 466 when(mDatabaseManager.getProfileConnectionPolicy(testDevice, BluetoothProfile.A2DP)) 467 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 468 // Send a connect request 469 Assert.assertTrue("Connect failed", mA2dpService.connect(testDevice)); 470 // Verify the connection state broadcast, and that we are in Connecting state 471 verifyConnectionStateIntent(TIMEOUT_MS, testDevice, BluetoothProfile.STATE_CONNECTING, 472 BluetoothProfile.STATE_DISCONNECTED); 473 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 474 mA2dpService.getConnectionState(testDevice)); 475 // Send a message to trigger connection completed 476 connCompletedEvent = 477 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 478 connCompletedEvent.device = testDevice; 479 connCompletedEvent.valueInt = A2dpStackEvent.CONNECTION_STATE_CONNECTED; 480 mA2dpService.messageFromNative(connCompletedEvent); 481 482 // Verify the connection state broadcast, and that we are in Connected state 483 verifyConnectionStateIntent(TIMEOUT_MS, testDevice, BluetoothProfile.STATE_CONNECTED, 484 BluetoothProfile.STATE_CONNECTING); 485 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 486 mA2dpService.getConnectionState(testDevice)); 487 // Verify the list of connected devices 488 Assert.assertTrue(mA2dpService.getConnectedDevices().contains(testDevice)); 489 } 490 491 // Prepare and connect the extra test device. The connect request should fail 492 extraTestDevice = TestUtils.getTestDevice(mAdapter, MAX_CONNECTED_AUDIO_DEVICES); 493 when(mDatabaseManager.getProfileConnectionPolicy(extraTestDevice, BluetoothProfile.A2DP)) 494 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 495 // Send a connect request 496 Assert.assertFalse("Connect expected to fail", mA2dpService.connect(extraTestDevice)); 497 } 498 499 /** 500 * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING A2DP stack events 501 * will create a state machine. 502 */ 503 @Test testCreateStateMachineStackEvents()504 public void testCreateStateMachineStackEvents() { 505 A2dpStackEvent stackEvent; 506 507 // Update the device priority so okToConnect() returns true 508 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 509 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 510 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 511 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 512 513 // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine should be created 514 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING, 515 BluetoothProfile.STATE_DISCONNECTED); 516 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 517 mA2dpService.getConnectionState(mTestDevice)); 518 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 519 520 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 521 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 522 BluetoothProfile.STATE_CONNECTING); 523 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 524 mA2dpService.getConnectionState(mTestDevice)); 525 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 526 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 527 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 528 529 // A2DP stack event: CONNECTION_STATE_CONNECTED - state machine should be created 530 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTED, 531 BluetoothProfile.STATE_DISCONNECTED); 532 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 533 mA2dpService.getConnectionState(mTestDevice)); 534 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 535 536 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 537 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 538 BluetoothProfile.STATE_CONNECTED); 539 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 540 mA2dpService.getConnectionState(mTestDevice)); 541 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 542 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 543 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 544 545 // A2DP stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created 546 generateUnexpectedConnectionMessageFromNative(mTestDevice, 547 BluetoothProfile.STATE_DISCONNECTING, 548 BluetoothProfile.STATE_DISCONNECTED); 549 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 550 mA2dpService.getConnectionState(mTestDevice)); 551 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 552 553 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created 554 generateUnexpectedConnectionMessageFromNative(mTestDevice, 555 BluetoothProfile.STATE_DISCONNECTED, 556 BluetoothProfile.STATE_DISCONNECTED); 557 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 558 mA2dpService.getConnectionState(mTestDevice)); 559 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 560 } 561 562 /** 563 * Test that EVENT_TYPE_AUDIO_STATE_CHANGED and EVENT_TYPE_CODEC_CONFIG_CHANGED events 564 * are processed. 565 */ 566 @Test testProcessAudioStateChangedCodecConfigChangedEvents()567 public void testProcessAudioStateChangedCodecConfigChangedEvents() { 568 A2dpStackEvent stackEvent; 569 BluetoothCodecConfig codecConfigSbc = 570 new BluetoothCodecConfig( 571 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, 572 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, 573 BluetoothCodecConfig.SAMPLE_RATE_44100, 574 BluetoothCodecConfig.BITS_PER_SAMPLE_16, 575 BluetoothCodecConfig.CHANNEL_MODE_STEREO, 576 0, 0, 0, 0); // Codec-specific fields 577 BluetoothCodecConfig codecConfig = codecConfigSbc; 578 BluetoothCodecConfig[] codecsLocalCapabilities = new BluetoothCodecConfig[1]; 579 BluetoothCodecConfig[] codecsSelectableCapabilities = new BluetoothCodecConfig[1]; 580 codecsLocalCapabilities[0] = codecConfigSbc; 581 codecsSelectableCapabilities[0] = codecConfigSbc; 582 BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(codecConfig, 583 codecsLocalCapabilities, 584 codecsSelectableCapabilities); 585 586 // Update the device priority so okToConnect() returns true 587 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 588 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 589 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 590 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 591 592 // A2DP stack event: EVENT_TYPE_AUDIO_STATE_CHANGED - state machine should not be created 593 generateUnexpectedAudioMessageFromNative(mTestDevice, A2dpStackEvent.AUDIO_STATE_STARTED, 594 BluetoothA2dp.STATE_PLAYING, 595 BluetoothA2dp.STATE_NOT_PLAYING); 596 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 597 mA2dpService.getConnectionState(mTestDevice)); 598 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 599 600 // A2DP stack event: EVENT_TYPE_CODEC_CONFIG_CHANGED - state machine should not be created 601 generateUnexpectedCodecMessageFromNative(mTestDevice, codecStatus); 602 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 603 mA2dpService.getConnectionState(mTestDevice)); 604 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 605 606 // A2DP stack event: CONNECTION_STATE_CONNECTED - state machine should be created 607 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTED, 608 BluetoothProfile.STATE_DISCONNECTED); 609 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 610 mA2dpService.getConnectionState(mTestDevice)); 611 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 612 613 // A2DP stack event: EVENT_TYPE_AUDIO_STATE_CHANGED - Intent broadcast should be generated 614 // NOTE: The first message (STATE_PLAYING -> STATE_NOT_PLAYING) is generated internally 615 // by the state machine when Connected, and needs to be extracted first before generating 616 // the actual message from native. 617 verifyAudioStateIntent(TIMEOUT_MS, mTestDevice, BluetoothA2dp.STATE_NOT_PLAYING, 618 BluetoothA2dp.STATE_PLAYING); 619 generateAudioMessageFromNative(mTestDevice, 620 A2dpStackEvent.AUDIO_STATE_STARTED, 621 BluetoothA2dp.STATE_PLAYING, 622 BluetoothA2dp.STATE_NOT_PLAYING); 623 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 624 mA2dpService.getConnectionState(mTestDevice)); 625 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 626 627 // A2DP stack event: EVENT_TYPE_CODEC_CONFIG_CHANGED - Intent broadcast should be generated 628 generateCodecMessageFromNative(mTestDevice, codecStatus); 629 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 630 mA2dpService.getConnectionState(mTestDevice)); 631 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 632 633 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 634 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 635 BluetoothProfile.STATE_CONNECTED); 636 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 637 mA2dpService.getConnectionState(mTestDevice)); 638 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 639 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 640 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 641 } 642 643 /** 644 * Test that a state machine in DISCONNECTED state is removed only after the device is unbond. 645 */ 646 @Test testDeleteStateMachineUnbondEvents()647 public void testDeleteStateMachineUnbondEvents() { 648 A2dpStackEvent stackEvent; 649 650 // Update the device priority so okToConnect() returns true 651 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 652 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 653 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 654 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 655 656 // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine should be created 657 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING, 658 BluetoothProfile.STATE_DISCONNECTED); 659 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 660 mA2dpService.getConnectionState(mTestDevice)); 661 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 662 // Device unbond - state machine is not removed 663 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 664 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 665 666 // A2DP stack event: CONNECTION_STATE_CONNECTED - state machine is not removed 667 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_BONDED); 668 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTED, 669 BluetoothProfile.STATE_CONNECTING); 670 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 671 mA2dpService.getConnectionState(mTestDevice)); 672 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 673 // Device unbond - state machine is not removed 674 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 675 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 676 677 // A2DP stack event: CONNECTION_STATE_DISCONNECTING - state machine is not removed 678 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_BONDED); 679 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTING, 680 BluetoothProfile.STATE_CONNECTED); 681 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING, 682 mA2dpService.getConnectionState(mTestDevice)); 683 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 684 // Device unbond - state machine is not removed 685 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 686 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 687 688 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed 689 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_BONDED); 690 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 691 BluetoothProfile.STATE_DISCONNECTING); 692 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 693 mA2dpService.getConnectionState(mTestDevice)); 694 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 695 // Device unbond - state machine is removed 696 mA2dpService.bondStateChanged(mTestDevice, BluetoothDevice.BOND_NONE); 697 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 698 } 699 700 /** 701 * Test that a CONNECTION_STATE_DISCONNECTED A2DP stack event will remove the state machine 702 * only if the device is unbond. 703 */ 704 @Test testDeleteStateMachineDisconnectEvents()705 public void testDeleteStateMachineDisconnectEvents() { 706 A2dpStackEvent stackEvent; 707 708 // Update the device priority so okToConnect() returns true 709 when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP)) 710 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 711 doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); 712 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); 713 714 // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine should be created 715 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING, 716 BluetoothProfile.STATE_DISCONNECTED); 717 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 718 mA2dpService.getConnectionState(mTestDevice)); 719 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 720 721 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed 722 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 723 BluetoothProfile.STATE_CONNECTING); 724 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 725 mA2dpService.getConnectionState(mTestDevice)); 726 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 727 728 // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine remains 729 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING, 730 BluetoothProfile.STATE_DISCONNECTED); 731 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 732 mA2dpService.getConnectionState(mTestDevice)); 733 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 734 735 // Device bond state marked as unbond - state machine is not removed 736 doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService) 737 .getBondState(any(BluetoothDevice.class)); 738 Assert.assertTrue(mA2dpService.getDevices().contains(mTestDevice)); 739 740 // A2DP stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed 741 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 742 BluetoothProfile.STATE_CONNECTING); 743 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 744 mA2dpService.getConnectionState(mTestDevice)); 745 Assert.assertFalse(mA2dpService.getDevices().contains(mTestDevice)); 746 } 747 748 /** 749 * Test that whether active device been removed after enable silence mode 750 */ 751 @Test testSetSilenceMode()752 public void testSetSilenceMode() { 753 BluetoothDevice otherDevice = mAdapter.getRemoteDevice("05:04:03:02:01:00"); 754 connectDevice(mTestDevice); 755 connectDevice(otherDevice); 756 doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class)); 757 doReturn(true).when(mA2dpNativeInterface).setSilenceDevice(any(BluetoothDevice.class), 758 anyBoolean()); 759 760 // Test whether active device been removed after enable silence mode. 761 Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice)); 762 Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice()); 763 Assert.assertTrue(mA2dpService.setSilenceMode(mTestDevice, true)); 764 verify(mA2dpNativeInterface).setSilenceDevice(mTestDevice, true); 765 Assert.assertNull(mA2dpService.getActiveDevice()); 766 767 // Test whether active device been resumeed after disable silence mode. 768 Assert.assertTrue(mA2dpService.setSilenceMode(mTestDevice, false)); 769 verify(mA2dpNativeInterface).setSilenceDevice(mTestDevice, false); 770 Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice()); 771 772 // Test that active device should not be changed when silence a non-active device 773 Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice)); 774 Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice()); 775 Assert.assertTrue(mA2dpService.setSilenceMode(otherDevice, true)); 776 verify(mA2dpNativeInterface).setSilenceDevice(otherDevice, true); 777 Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice()); 778 779 // Test that active device should not be changed when another device exits silence mode 780 Assert.assertTrue(mA2dpService.setSilenceMode(otherDevice, false)); 781 verify(mA2dpNativeInterface).setSilenceDevice(otherDevice, false); 782 Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice()); 783 } 784 785 /** 786 * Test that whether updateOptionalCodecsSupport() method is working as intended 787 * when a Bluetooth device is connected with A2DP. 788 */ 789 @Test testUpdateOptionalCodecsSupport()790 public void testUpdateOptionalCodecsSupport() { 791 int verifySupportTime = 0; 792 int verifyNotSupportTime = 0; 793 int verifyEnabledTime = 0; 794 // Test for device supports optional codec 795 testUpdateOptionalCodecsSupportCase( 796 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN, true, 797 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN, 798 ++verifySupportTime, verifyNotSupportTime, ++verifyEnabledTime); 799 testUpdateOptionalCodecsSupportCase( 800 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN, true, 801 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED, 802 ++verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 803 testUpdateOptionalCodecsSupportCase( 804 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN, true, 805 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED, 806 ++verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 807 testUpdateOptionalCodecsSupportCase( 808 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED, true, 809 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN, 810 verifySupportTime, verifyNotSupportTime, ++verifyEnabledTime); 811 testUpdateOptionalCodecsSupportCase( 812 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED, true, 813 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED, 814 verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 815 testUpdateOptionalCodecsSupportCase( 816 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED, true, 817 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED, 818 verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 819 testUpdateOptionalCodecsSupportCase( 820 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED, true, 821 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN, 822 ++verifySupportTime, verifyNotSupportTime, ++verifyEnabledTime); 823 testUpdateOptionalCodecsSupportCase( 824 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED, true, 825 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED, 826 ++verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 827 testUpdateOptionalCodecsSupportCase( 828 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED, true, 829 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED, 830 ++verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 831 832 // Test for device not supports optional codec 833 testUpdateOptionalCodecsSupportCase( 834 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN, false, 835 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN, 836 verifySupportTime, ++verifyNotSupportTime, verifyEnabledTime); 837 testUpdateOptionalCodecsSupportCase( 838 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN, false, 839 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED, 840 verifySupportTime, ++verifyNotSupportTime, verifyEnabledTime); 841 testUpdateOptionalCodecsSupportCase( 842 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN, false, 843 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED, 844 verifySupportTime, ++verifyNotSupportTime, verifyEnabledTime); 845 testUpdateOptionalCodecsSupportCase( 846 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED, false, 847 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN, 848 verifySupportTime, ++verifyNotSupportTime, verifyEnabledTime); 849 testUpdateOptionalCodecsSupportCase( 850 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED, false, 851 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED, 852 verifySupportTime, ++verifyNotSupportTime, verifyEnabledTime); 853 testUpdateOptionalCodecsSupportCase( 854 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED, false, 855 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED, 856 verifySupportTime, ++verifyNotSupportTime, verifyEnabledTime); 857 testUpdateOptionalCodecsSupportCase( 858 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED, false, 859 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN, 860 verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 861 testUpdateOptionalCodecsSupportCase( 862 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED, false, 863 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED, 864 verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 865 testUpdateOptionalCodecsSupportCase( 866 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED, false, 867 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED, 868 verifySupportTime, verifyNotSupportTime, verifyEnabledTime); 869 } 870 connectDevice(BluetoothDevice device)871 private void connectDevice(BluetoothDevice device) { 872 connectDeviceWithCodecStatus(device, null); 873 } 874 connectDeviceWithCodecStatus(BluetoothDevice device, BluetoothCodecStatus codecStatus)875 private void connectDeviceWithCodecStatus(BluetoothDevice device, 876 BluetoothCodecStatus codecStatus) { 877 A2dpStackEvent connCompletedEvent; 878 879 List<BluetoothDevice> prevConnectedDevices = mA2dpService.getConnectedDevices(); 880 881 // Update the device priority so okToConnect() returns true 882 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP)) 883 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 884 doReturn(true).when(mA2dpNativeInterface).connectA2dp(device); 885 doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(device); 886 doReturn(true).when(mA2dpNativeInterface).setCodecConfigPreference( 887 any(BluetoothDevice.class), any(BluetoothCodecConfig[].class)); 888 889 // Send a connect request 890 Assert.assertTrue("Connect failed", mA2dpService.connect(device)); 891 892 // Verify the connection state broadcast, and that we are in Connecting state 893 verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTING, 894 BluetoothProfile.STATE_DISCONNECTED); 895 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 896 mA2dpService.getConnectionState(device)); 897 898 if (codecStatus != null) { 899 generateCodecMessageFromNative(device, codecStatus); 900 } 901 902 // Send a message to trigger connection completed 903 connCompletedEvent = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 904 connCompletedEvent.device = device; 905 connCompletedEvent.valueInt = A2dpStackEvent.CONNECTION_STATE_CONNECTED; 906 mA2dpService.messageFromNative(connCompletedEvent); 907 908 // Verify the connection state broadcast, and that we are in Connected state 909 verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTED, 910 BluetoothProfile.STATE_CONNECTING); 911 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 912 mA2dpService.getConnectionState(device)); 913 914 // Verify that the device is in the list of connected devices 915 Assert.assertTrue(mA2dpService.getConnectedDevices().contains(device)); 916 // Verify the list of previously connected devices 917 for (BluetoothDevice prevDevice : prevConnectedDevices) { 918 Assert.assertTrue(mA2dpService.getConnectedDevices().contains(prevDevice)); 919 } 920 } 921 generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)922 private void generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, 923 int oldConnectionState) { 924 A2dpStackEvent stackEvent = 925 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 926 stackEvent.device = device; 927 stackEvent.valueInt = newConnectionState; 928 mA2dpService.messageFromNative(stackEvent); 929 // Verify the connection state broadcast 930 verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState); 931 } 932 generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)933 private void generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, 934 int newConnectionState, 935 int oldConnectionState) { 936 A2dpStackEvent stackEvent = 937 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 938 stackEvent.device = device; 939 stackEvent.valueInt = newConnectionState; 940 mA2dpService.messageFromNative(stackEvent); 941 // Verify the connection state broadcast 942 verifyNoConnectionStateIntent(TIMEOUT_MS); 943 } 944 generateAudioMessageFromNative(BluetoothDevice device, int audioStackEvent, int newAudioState, int oldAudioState)945 private void generateAudioMessageFromNative(BluetoothDevice device, int audioStackEvent, 946 int newAudioState, int oldAudioState) { 947 A2dpStackEvent stackEvent = 948 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 949 stackEvent.device = device; 950 stackEvent.valueInt = audioStackEvent; 951 mA2dpService.messageFromNative(stackEvent); 952 // Verify the audio state broadcast 953 verifyAudioStateIntent(TIMEOUT_MS, device, newAudioState, oldAudioState); 954 } 955 generateUnexpectedAudioMessageFromNative(BluetoothDevice device, int audioStackEvent, int newAudioState, int oldAudioState)956 private void generateUnexpectedAudioMessageFromNative(BluetoothDevice device, 957 int audioStackEvent, int newAudioState, 958 int oldAudioState) { 959 A2dpStackEvent stackEvent = 960 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 961 stackEvent.device = device; 962 stackEvent.valueInt = audioStackEvent; 963 mA2dpService.messageFromNative(stackEvent); 964 // Verify the audio state broadcast 965 verifyNoAudioStateIntent(TIMEOUT_MS); 966 } 967 generateCodecMessageFromNative(BluetoothDevice device, BluetoothCodecStatus codecStatus)968 private void generateCodecMessageFromNative(BluetoothDevice device, 969 BluetoothCodecStatus codecStatus) { 970 A2dpStackEvent stackEvent = 971 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); 972 stackEvent.device = device; 973 stackEvent.codecStatus = codecStatus; 974 mA2dpService.messageFromNative(stackEvent); 975 // Verify the codec status broadcast 976 verifyCodecConfigIntent(TIMEOUT_MS, device, codecStatus); 977 } 978 generateUnexpectedCodecMessageFromNative(BluetoothDevice device, BluetoothCodecStatus codecStatus)979 private void generateUnexpectedCodecMessageFromNative(BluetoothDevice device, 980 BluetoothCodecStatus codecStatus) { 981 A2dpStackEvent stackEvent = 982 new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); 983 stackEvent.device = device; 984 stackEvent.codecStatus = codecStatus; 985 mA2dpService.messageFromNative(stackEvent); 986 // Verify the codec status broadcast 987 verifyNoCodecConfigIntent(TIMEOUT_MS); 988 } 989 990 /** 991 * Helper function to test okToConnect() method. 992 * 993 * @param device test device 994 * @param bondState bond state value, could be invalid 995 * @param priority value, could be invalid, coudl be invalid 996 * @param expected expected result from okToConnect() 997 */ testOkToConnectCase(BluetoothDevice device, int bondState, int priority, boolean expected)998 private void testOkToConnectCase(BluetoothDevice device, int bondState, int priority, 999 boolean expected) { 1000 doReturn(bondState).when(mAdapterService).getBondState(device); 1001 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP)) 1002 .thenReturn(priority); 1003 1004 // Test when the AdapterService is in non-quiet mode: the result should not depend 1005 // on whether the connection request is outgoing or incoming. 1006 doReturn(false).when(mAdapterService).isQuietModeEnabled(); 1007 Assert.assertEquals(expected, mA2dpService.okToConnect(device, true)); // Outgoing 1008 Assert.assertEquals(expected, mA2dpService.okToConnect(device, false)); // Incoming 1009 1010 // Test when the AdapterService is in quiet mode: the result should always be 1011 // false when the connection request is incoming. 1012 doReturn(true).when(mAdapterService).isQuietModeEnabled(); 1013 Assert.assertEquals(expected, mA2dpService.okToConnect(device, true)); // Outgoing 1014 Assert.assertEquals(false, mA2dpService.okToConnect(device, false)); // Incoming 1015 } 1016 1017 /** 1018 * Helper function to test updateOptionalCodecsSupport() method 1019 * 1020 * @param previousSupport previous optional codec support status 1021 * @param support new optional codec support status 1022 * @param previousEnabled previous optional codec enable status 1023 * @param verifySupportTime verify times of optional codec set to support 1024 * @param verifyNotSupportTime verify times of optional codec set to not support 1025 * @param verifyEnabledTime verify times of optional codec set to enabled 1026 */ testUpdateOptionalCodecsSupportCase(int previousSupport, boolean support, int previousEnabled, int verifySupportTime, int verifyNotSupportTime, int verifyEnabledTime)1027 private void testUpdateOptionalCodecsSupportCase(int previousSupport, boolean support, 1028 int previousEnabled, int verifySupportTime, int verifyNotSupportTime, 1029 int verifyEnabledTime) { 1030 doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class)); 1031 1032 BluetoothCodecConfig codecConfigSbc = 1033 new BluetoothCodecConfig( 1034 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, 1035 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, 1036 BluetoothCodecConfig.SAMPLE_RATE_44100, 1037 BluetoothCodecConfig.BITS_PER_SAMPLE_16, 1038 BluetoothCodecConfig.CHANNEL_MODE_STEREO, 1039 0, 0, 0, 0); // Codec-specific fields 1040 BluetoothCodecConfig codecConfigAac = 1041 new BluetoothCodecConfig( 1042 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, 1043 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, 1044 BluetoothCodecConfig.SAMPLE_RATE_44100, 1045 BluetoothCodecConfig.BITS_PER_SAMPLE_16, 1046 BluetoothCodecConfig.CHANNEL_MODE_STEREO, 1047 0, 0, 0, 0); // Codec-specific fields 1048 1049 BluetoothCodecConfig[] codecsLocalCapabilities; 1050 BluetoothCodecConfig[] codecsSelectableCapabilities; 1051 if (support) { 1052 codecsLocalCapabilities = new BluetoothCodecConfig[2]; 1053 codecsSelectableCapabilities = new BluetoothCodecConfig[2]; 1054 codecsLocalCapabilities[0] = codecConfigSbc; 1055 codecsLocalCapabilities[1] = codecConfigAac; 1056 codecsSelectableCapabilities[0] = codecConfigSbc; 1057 codecsSelectableCapabilities[1] = codecConfigAac; 1058 } else { 1059 codecsLocalCapabilities = new BluetoothCodecConfig[1]; 1060 codecsSelectableCapabilities = new BluetoothCodecConfig[1]; 1061 codecsLocalCapabilities[0] = codecConfigSbc; 1062 codecsSelectableCapabilities[0] = codecConfigSbc; 1063 } 1064 BluetoothCodecConfig[] badCodecsSelectableCapabilities; 1065 badCodecsSelectableCapabilities = new BluetoothCodecConfig[1]; 1066 badCodecsSelectableCapabilities[0] = codecConfigAac; 1067 1068 BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(codecConfigSbc, 1069 codecsLocalCapabilities, codecsSelectableCapabilities); 1070 BluetoothCodecStatus badCodecStatus = new BluetoothCodecStatus(codecConfigAac, 1071 codecsLocalCapabilities, badCodecsSelectableCapabilities); 1072 1073 when(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice)) 1074 .thenReturn(previousSupport); 1075 when(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice)) 1076 .thenReturn(previousEnabled); 1077 1078 // Generate connection request from native with bad codec status 1079 connectDeviceWithCodecStatus(mTestDevice, badCodecStatus); 1080 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 1081 BluetoothProfile.STATE_CONNECTED); 1082 1083 // Generate connection request from native with good codec status 1084 connectDeviceWithCodecStatus(mTestDevice, codecStatus); 1085 generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, 1086 BluetoothProfile.STATE_CONNECTED); 1087 1088 // Check optional codec status is set properly 1089 verify(mDatabaseManager, times(verifyNotSupportTime)).setA2dpSupportsOptionalCodecs( 1090 mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); 1091 verify(mDatabaseManager, times(verifySupportTime)).setA2dpSupportsOptionalCodecs( 1092 mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); 1093 verify(mDatabaseManager, times(verifyEnabledTime)).setA2dpOptionalCodecsEnabled( 1094 mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); 1095 } 1096 } 1097