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