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