1 /* 2 * Copyright (C) 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.server.telecom.tests; 18 19 import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE1; 20 import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE2; 21 import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE3; 22 import static com.android.server.telecom.tests.BluetoothRouteManagerTest.executeRoutingAction; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.mockito.ArgumentMatchers.any; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.ArgumentMatchers.nullable; 28 import static org.mockito.Mockito.clearInvocations; 29 import static org.mockito.Mockito.doAnswer; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.bluetooth.BluetoothAdapter; 35 import android.bluetooth.BluetoothDevice; 36 import android.bluetooth.BluetoothHeadset; 37 import android.bluetooth.BluetoothHearingAid; 38 import android.bluetooth.BluetoothLeAudio; 39 import android.bluetooth.BluetoothProfile; 40 import android.bluetooth.BluetoothStatusCodes; 41 import android.content.ContentResolver; 42 import android.telecom.Log; 43 44 import androidx.test.filters.SmallTest; 45 46 import com.android.internal.os.SomeArgs; 47 import com.android.server.telecom.CallAudioCommunicationDeviceTracker; 48 import com.android.server.telecom.TelecomSystem; 49 import com.android.server.telecom.Timeouts; 50 import com.android.server.telecom.bluetooth.BluetoothDeviceManager; 51 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 52 53 import org.junit.After; 54 import org.junit.Before; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 import org.junit.runners.Parameterized; 58 import org.mockito.Mock; 59 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.Collection; 63 import java.util.Collections; 64 import java.util.List; 65 import java.util.stream.Collectors; 66 67 @RunWith(Parameterized.class) 68 public class BluetoothRouteTransitionTests extends TelecomTestCase { 69 private enum ListenerUpdate { 70 DEVICE_LIST_CHANGED, ACTIVE_DEVICE_PRESENT, ACTIVE_DEVICE_GONE, 71 AUDIO_CONNECTING, AUDIO_CONNECTED, AUDIO_DISCONNECTED, UNEXPECTED_STATE_CHANGE 72 } 73 74 private static class BluetoothRouteTestParametersBuilder { 75 private String name; 76 private String initialBluetoothState; 77 private BluetoothDevice initialDevice; 78 private BluetoothDevice audioOnDevice; 79 private int messageType; 80 private BluetoothDevice messageDevice; 81 private ListenerUpdate[] expectedListenerUpdates; 82 private int expectedBluetoothInteraction; 83 private BluetoothDevice expectedConnectionDevice; 84 private String expectedFinalStateName; 85 private BluetoothDevice[] connectedDevices; 86 // the active device as returned by BluetoothAdapter#getActiveDevices 87 private BluetoothDevice activeDevice = null; 88 private List<BluetoothDevice> hearingAidBtDevices = Collections.emptyList(); 89 private List<BluetoothDevice> leAudioDevices = Collections.emptyList(); 90 setName(String name)91 public BluetoothRouteTestParametersBuilder setName(String name) { 92 this.name = name; 93 return this; 94 } 95 setInitialBluetoothState( String initialBluetoothState)96 public BluetoothRouteTestParametersBuilder setInitialBluetoothState( 97 String initialBluetoothState) { 98 this.initialBluetoothState = initialBluetoothState; 99 return this; 100 } 101 setInitialDevice(BluetoothDevice initialDevice)102 public BluetoothRouteTestParametersBuilder setInitialDevice(BluetoothDevice 103 initialDevice) { 104 this.initialDevice = initialDevice; 105 return this; 106 } 107 setMessageType(int messageType)108 public BluetoothRouteTestParametersBuilder setMessageType(int messageType) { 109 this.messageType = messageType; 110 return this; 111 } 112 setMessageDevice(BluetoothDevice messageDevice)113 public BluetoothRouteTestParametersBuilder setMessageDevice(BluetoothDevice messageDevice) { 114 this.messageDevice = messageDevice; 115 return this; 116 } 117 setExpectedListenerUpdates( ListenerUpdate... expectedListenerUpdates)118 public BluetoothRouteTestParametersBuilder setExpectedListenerUpdates( 119 ListenerUpdate... expectedListenerUpdates) { 120 this.expectedListenerUpdates = expectedListenerUpdates; 121 return this; 122 } 123 setExpectedBluetoothInteraction( int expectedBluetoothInteraction)124 public BluetoothRouteTestParametersBuilder setExpectedBluetoothInteraction( 125 int expectedBluetoothInteraction) { 126 this.expectedBluetoothInteraction = expectedBluetoothInteraction; 127 return this; 128 } 129 setExpectedConnectionDevice( BluetoothDevice expectedConnectionDevice)130 public BluetoothRouteTestParametersBuilder setExpectedConnectionDevice( 131 BluetoothDevice expectedConnectionDevice) { 132 this.expectedConnectionDevice = expectedConnectionDevice; 133 return this; 134 } 135 setExpectedFinalStateName( String expectedFinalStateName)136 public BluetoothRouteTestParametersBuilder setExpectedFinalStateName( 137 String expectedFinalStateName) { 138 this.expectedFinalStateName = expectedFinalStateName; 139 return this; 140 } 141 setConnectedDevices( BluetoothDevice... connectedDevices)142 public BluetoothRouteTestParametersBuilder setConnectedDevices( 143 BluetoothDevice... connectedDevices) { 144 this.connectedDevices = connectedDevices; 145 return this; 146 } 147 setAudioOnDevice(BluetoothDevice device)148 public BluetoothRouteTestParametersBuilder setAudioOnDevice(BluetoothDevice device) { 149 this.audioOnDevice = device; 150 return this; 151 } 152 setActiveDevice(BluetoothDevice device)153 public BluetoothRouteTestParametersBuilder setActiveDevice(BluetoothDevice device) { 154 this.activeDevice = device; 155 return this; 156 } 157 setHearingAidBtDevices( List<BluetoothDevice> hearingAidBtDevices)158 public BluetoothRouteTestParametersBuilder setHearingAidBtDevices( 159 List<BluetoothDevice> hearingAidBtDevices) { 160 this.hearingAidBtDevices = hearingAidBtDevices; 161 return this; 162 } 163 setLeAudioDevices( List<BluetoothDevice> leAudioDevices)164 public BluetoothRouteTestParametersBuilder setLeAudioDevices( 165 List<BluetoothDevice> leAudioDevices) { 166 this.leAudioDevices = leAudioDevices; 167 return this; 168 } 169 build()170 public BluetoothRouteTestParameters build() { 171 return new BluetoothRouteTestParameters(name, 172 initialBluetoothState, 173 initialDevice, 174 messageType, 175 expectedListenerUpdates, 176 expectedBluetoothInteraction, 177 expectedConnectionDevice, 178 expectedFinalStateName, 179 connectedDevices, 180 messageDevice, 181 audioOnDevice, 182 activeDevice, 183 hearingAidBtDevices, 184 leAudioDevices); 185 186 } 187 } 188 189 private static class BluetoothRouteTestParameters { 190 public String name; 191 public String initialBluetoothState; // One of the state names or prefixes from BRM. 192 public BluetoothDevice initialDevice; // null if we start from AudioOff 193 public BluetoothDevice audioOnDevice; // The device (if any) that is active 194 public int messageType; // Any of the commands from the state machine 195 public BluetoothDevice messageDevice; // The device that should be specified in the message. 196 public ListenerUpdate[] expectedListenerUpdates; // what the listener should expect. 197 // NONE, CONNECT, CONNECT_SWITCH_DEVICE or DISCONNECT 198 public int expectedBluetoothInteraction; 199 public BluetoothDevice expectedConnectionDevice; // Expected device to connect to. 200 public String expectedFinalStateName; // Expected name of the final state. 201 public BluetoothDevice[] connectedDevices; // array of connected devices 202 // the active device as returned by BluetoothAdapter#getActiveDevices 203 private BluetoothDevice activeDevice = null; 204 private List<BluetoothDevice> hearingAidBtDevices; 205 private List<BluetoothDevice> leAudioDevices; 206 BluetoothRouteTestParameters(String name, String initialBluetoothState, BluetoothDevice initialDevice, int messageType, ListenerUpdate[] expectedListenerUpdates, int expectedBluetoothInteraction, BluetoothDevice expectedConnectionDevice, String expectedFinalStateName, BluetoothDevice[] connectedDevices, BluetoothDevice messageDevice, BluetoothDevice audioOnDevice, BluetoothDevice activeDevice, List<BluetoothDevice> hearingAidBtDevices, List<BluetoothDevice> leAudioDevices)207 public BluetoothRouteTestParameters(String name, String initialBluetoothState, 208 BluetoothDevice initialDevice, int messageType, ListenerUpdate[] 209 expectedListenerUpdates, int expectedBluetoothInteraction, BluetoothDevice 210 expectedConnectionDevice, String expectedFinalStateName, 211 BluetoothDevice[] connectedDevices, BluetoothDevice messageDevice, 212 BluetoothDevice audioOnDevice, BluetoothDevice activeDevice, 213 List<BluetoothDevice> hearingAidBtDevices, List<BluetoothDevice> leAudioDevices) { 214 this.name = name; 215 this.initialBluetoothState = initialBluetoothState; 216 this.initialDevice = initialDevice; 217 this.messageType = messageType; 218 this.expectedListenerUpdates = expectedListenerUpdates; 219 this.expectedBluetoothInteraction = expectedBluetoothInteraction; 220 this.expectedConnectionDevice = expectedConnectionDevice; 221 this.expectedFinalStateName = expectedFinalStateName; 222 this.connectedDevices = connectedDevices; 223 this.messageDevice = messageDevice; 224 this.audioOnDevice = audioOnDevice; 225 this.activeDevice = activeDevice; 226 this.hearingAidBtDevices = hearingAidBtDevices; 227 this.leAudioDevices = leAudioDevices; 228 } 229 230 @Override toString()231 public String toString() { 232 String expectedListenerUpdatesStr = expectedListenerUpdates == null ? "" 233 : Arrays.stream(expectedListenerUpdates).map(ListenerUpdate::name) 234 .collect(Collectors.joining(",")); 235 return "BluetoothRouteTestParameters{" + 236 "name='" + name + '\'' + 237 ", initialBluetoothState='" + initialBluetoothState + '\'' + 238 ", initialDevice=" + initialDevice + 239 ", messageType=" + messageType + 240 ", messageDevice='" + messageDevice + '\'' + 241 ", expectedListenerUpdate='" + expectedListenerUpdatesStr + '\'' + 242 ", expectedBluetoothInteraction=" + expectedBluetoothInteraction + 243 ", expectedConnectionDevice='" + expectedConnectionDevice + '\'' + 244 ", expectedFinalStateName='" + expectedFinalStateName + '\'' + 245 ", connectedDevices=" + Arrays.toString(connectedDevices) + 246 ", activeDevice='" + activeDevice + '\'' + 247 ", hearingAidBtDevices ='" + hearingAidBtDevices + '\'' + 248 ", leAudioDevices ='" + leAudioDevices + '\'' + 249 '}'; 250 } 251 } 252 253 private static final int NONE = 1; 254 private static final int CONNECT = 2; 255 private static final int DISCONNECT = 3; 256 private static final int CONNECT_SWITCH_DEVICE = 4; 257 258 private static final int TEST_TIMEOUT = 1000; 259 260 private final BluetoothRouteTestParameters mParams; 261 @Mock private BluetoothDeviceManager mDeviceManager; 262 @Mock private BluetoothAdapter mBluetoothAdapter; 263 @Mock private BluetoothHeadset mBluetoothHeadset; 264 @Mock private BluetoothHearingAid mBluetoothHearingAid; 265 @Mock private BluetoothLeAudio mBluetoothLeAudio; 266 @Mock private Timeouts.Adapter mTimeoutsAdapter; 267 @Mock private BluetoothRouteManager.BluetoothStateListener mListener; 268 @Mock private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker; 269 270 @Override 271 @Before setUp()272 public void setUp() throws Exception { 273 super.setUp(); 274 when(mDeviceManager.getBluetoothAdapter()).thenReturn(mBluetoothAdapter); 275 } 276 277 @Override 278 @After tearDown()279 public void tearDown() throws Exception { 280 super.tearDown(); 281 } 282 BluetoothRouteTransitionTests(BluetoothRouteTestParameters params)283 public BluetoothRouteTransitionTests(BluetoothRouteTestParameters params) { 284 mParams = params; 285 } 286 287 @Test 288 @SmallTest testTransitions()289 public void testTransitions() { 290 BluetoothRouteManager sm = setupStateMachine( 291 mParams.initialBluetoothState, mParams.initialDevice); 292 293 int deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEADSET; 294 if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) { 295 deviceType = BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID; 296 } else if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) { 297 deviceType = BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO; 298 } 299 300 setupConnectedDevices(mParams.connectedDevices, 301 mParams.audioOnDevice, mParams.activeDevice); 302 sm.setActiveDeviceCacheForTesting(mParams.activeDevice, deviceType); 303 if (mParams.initialDevice != null) { 304 doAnswer(invocation -> { 305 SomeArgs args = SomeArgs.obtain(); 306 args.arg1 = Log.createSubsession(); 307 args.arg2 = mParams.initialDevice.getAddress(); 308 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) 309 .thenReturn(Arrays.asList((BluetoothDevice) null)); 310 sm.sendMessage(BluetoothRouteManager.BT_AUDIO_LOST, args); 311 return BluetoothStatusCodes.SUCCESS; 312 }).when(mDeviceManager).disconnectAudio(); 313 } 314 315 // Go through the utility methods for these two messages 316 if (mParams.messageType == BluetoothRouteManager.NEW_DEVICE_CONNECTED) { 317 sm.onDeviceAdded(mParams.messageDevice.getAddress()); 318 sm.onActiveDeviceChanged(mParams.messageDevice, deviceType); 319 } else if (mParams.messageType == BluetoothRouteManager.LOST_DEVICE) { 320 sm.onActiveDeviceChanged(null, deviceType); 321 if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) { 322 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEARING_AID))) 323 .thenReturn(Arrays.asList(null, null)); 324 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.LE_AUDIO))) 325 .thenReturn(mParams.leAudioDevices.stream() 326 .filter(device -> device != mParams.messageDevice) 327 .collect(Collectors.toList())); 328 } else { 329 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) 330 .thenReturn(Arrays.asList((BluetoothDevice) null)); 331 } 332 sm.onDeviceLost(mParams.messageDevice.getAddress()); 333 } else { 334 executeRoutingAction(sm, mParams.messageType, 335 mParams.messageDevice == null ? null : mParams.messageDevice.getAddress()); 336 } 337 338 waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT); 339 waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT); 340 waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT); 341 assertEquals(mParams.expectedFinalStateName, sm.getCurrentState().getName()); 342 343 for (ListenerUpdate lu : mParams.expectedListenerUpdates) { 344 switch (lu) { 345 case DEVICE_LIST_CHANGED: 346 verify(mListener).onBluetoothDeviceListChanged(); 347 break; 348 case ACTIVE_DEVICE_PRESENT: 349 verify(mListener).onBluetoothActiveDevicePresent(); 350 break; 351 case ACTIVE_DEVICE_GONE: 352 verify(mListener).onBluetoothActiveDeviceGone(); 353 break; 354 case AUDIO_CONNECTING: 355 verify(mListener).onBluetoothAudioConnecting(); 356 break; 357 case AUDIO_CONNECTED: 358 verify(mListener).onBluetoothAudioConnected(); 359 break; 360 case AUDIO_DISCONNECTED: 361 verify(mListener).onBluetoothAudioDisconnected(); 362 break; 363 } 364 } 365 366 switch (mParams.expectedBluetoothInteraction) { 367 case NONE: 368 verify(mDeviceManager, never()).connectAudio(nullable(String.class), 369 any(boolean.class)); 370 break; 371 case CONNECT: 372 verify(mDeviceManager).connectAudio(mParams.expectedConnectionDevice.getAddress(), 373 false); 374 verify(mDeviceManager, never()).disconnectAudio(); 375 break; 376 case CONNECT_SWITCH_DEVICE: 377 verify(mDeviceManager).disconnectAudio(); 378 verify(mDeviceManager).connectAudio(mParams.expectedConnectionDevice.getAddress(), 379 true); 380 break; 381 case DISCONNECT: 382 verify(mDeviceManager, never()).connectAudio(nullable(String.class), 383 any(boolean.class)); 384 verify(mDeviceManager).disconnectAudio(); 385 break; 386 } 387 388 sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT); 389 sm.quitNow(); 390 } 391 setupConnectedDevices(BluetoothDevice[] devices, BluetoothDevice audioOnDevice, BluetoothDevice activeDevice)392 private void setupConnectedDevices(BluetoothDevice[] devices, 393 BluetoothDevice audioOnDevice, BluetoothDevice activeDevice) { 394 when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length); 395 when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices)); 396 when(mBluetoothHeadset.getConnectedDevices()).thenReturn(Arrays.asList(devices)); 397 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) 398 .thenReturn(Arrays.asList(activeDevice)); 399 when(mBluetoothHeadset.getAudioState(nullable(BluetoothDevice.class))) 400 .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 401 if (audioOnDevice != null) { 402 when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) 403 .thenReturn(Arrays.asList(audioOnDevice)); 404 when(mBluetoothHeadset.getAudioState(audioOnDevice)) 405 .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED); 406 } 407 } 408 setupStateMachine(String initialState, BluetoothDevice initialDevice)409 private BluetoothRouteManager setupStateMachine(String initialState, 410 BluetoothDevice initialDevice) { 411 resetMocks(); 412 when(mDeviceManager.getBluetoothHeadset()).thenReturn(mBluetoothHeadset); 413 when(mDeviceManager.getBluetoothHearingAid()).thenReturn(mBluetoothHearingAid); 414 when(mDeviceManager.getLeAudioService()).thenReturn(mBluetoothLeAudio); 415 when(mDeviceManager.connectAudio(nullable(String.class), any(boolean.class))) 416 .thenReturn(true); 417 when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis( 418 nullable(ContentResolver.class))).thenReturn(100000L); 419 when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis( 420 nullable(ContentResolver.class))).thenReturn(100000L); 421 BluetoothRouteManager sm = new BluetoothRouteManager(mContext, 422 new TelecomSystem.SyncRoot() { }, mDeviceManager, 423 mTimeoutsAdapter, mCommunicationDeviceTracker, mFeatureFlags, 424 mContext.getMainLooper()); 425 sm.setListener(mListener); 426 sm.setInitialStateForTesting(initialState, initialDevice); 427 waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT); 428 resetMocks(); 429 return sm; 430 } 431 resetMocks()432 private void resetMocks() { 433 clearInvocations(mDeviceManager, mListener, mBluetoothHeadset, mTimeoutsAdapter); 434 } 435 436 @Parameterized.Parameters(name = "{0}") generateTestCases()437 public static Collection<BluetoothRouteTestParameters> generateTestCases() { 438 List<BluetoothRouteTestParameters> result = new ArrayList<>(); 439 result.add(new BluetoothRouteTestParametersBuilder() 440 .setName("New device connected while audio off") 441 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 442 .setInitialDevice(null) 443 .setConnectedDevices(DEVICE1) 444 .setMessageType(BluetoothRouteManager.NEW_DEVICE_CONNECTED) 445 .setMessageDevice(DEVICE1) 446 .setExpectedListenerUpdates(ListenerUpdate.DEVICE_LIST_CHANGED, 447 ListenerUpdate.ACTIVE_DEVICE_PRESENT) 448 .setExpectedBluetoothInteraction(NONE) 449 .setExpectedConnectionDevice(null) 450 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 451 .build()); 452 453 result.add(new BluetoothRouteTestParametersBuilder() 454 .setName("Nonspecific connection request while audio off with BT-active device") 455 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 456 .setInitialDevice(null) 457 .setConnectedDevices(DEVICE2, DEVICE1) 458 .setActiveDevice(DEVICE1) 459 .setMessageType(BluetoothRouteManager.CONNECT_BT) 460 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING) 461 .setExpectedBluetoothInteraction(CONNECT) 462 .setExpectedConnectionDevice(DEVICE1) 463 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX 464 + ":" + DEVICE1) 465 .build()); 466 467 result.add(new BluetoothRouteTestParametersBuilder() 468 .setName("Connection to a device succeeds after pending") 469 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 470 .setInitialDevice(DEVICE2) 471 .setAudioOnDevice(DEVICE2) 472 .setConnectedDevices(DEVICE2, DEVICE1) 473 .setMessageType(BluetoothRouteManager.BT_AUDIO_IS_ON) 474 .setMessageDevice(DEVICE2) 475 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED) 476 .setExpectedBluetoothInteraction(NONE) 477 .setExpectedConnectionDevice(null) 478 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX 479 + ":" + DEVICE2) 480 .build()); 481 482 result.add(new BluetoothRouteTestParametersBuilder() 483 .setName("Device loses BT audio but remains connected. No fallback.") 484 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 485 .setInitialDevice(DEVICE2) 486 .setConnectedDevices(DEVICE2) 487 .setMessageType(BluetoothRouteManager.BT_AUDIO_LOST) 488 .setMessageDevice(DEVICE2) 489 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED) 490 .setExpectedBluetoothInteraction(NONE) 491 .setExpectedConnectionDevice(null) 492 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 493 .build()); 494 495 result.add(new BluetoothRouteTestParametersBuilder() 496 .setName("Device loses BT audio but remains connected." 497 + " No fallback even though other devices available.") 498 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 499 .setInitialDevice(DEVICE2) 500 .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3) 501 .setMessageType(BluetoothRouteManager.BT_AUDIO_LOST) 502 .setMessageDevice(DEVICE2) 503 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED) 504 .setExpectedBluetoothInteraction(NONE) 505 .setExpectedConnectionDevice(null) 506 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 507 .build()); 508 509 result.add(new BluetoothRouteTestParametersBuilder() 510 .setName("Switch the device that audio is being routed to") 511 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 512 .setInitialDevice(DEVICE2) 513 .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3) 514 .setMessageType(BluetoothRouteManager.CONNECT_BT) 515 .setMessageDevice(DEVICE3) 516 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING) 517 .setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE) 518 .setExpectedConnectionDevice(DEVICE3) 519 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX 520 + ":" + DEVICE3) 521 .build()); 522 523 result.add(new BluetoothRouteTestParametersBuilder() 524 .setName("Switch to another device before first device has connected") 525 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 526 .setInitialDevice(DEVICE2) 527 .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3) 528 .setMessageType(BluetoothRouteManager.CONNECT_BT) 529 .setMessageDevice(DEVICE3) 530 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING) 531 .setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE) 532 .setExpectedConnectionDevice(DEVICE3) 533 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX 534 + ":" + DEVICE3) 535 .build()); 536 537 result.add(new BluetoothRouteTestParametersBuilder() 538 .setName("Device gets disconnected while active. No fallback.") 539 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 540 .setInitialDevice(DEVICE2) 541 .setActiveDevice(DEVICE2) 542 .setConnectedDevices() 543 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 544 .setMessageDevice(DEVICE2) 545 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 546 ListenerUpdate.DEVICE_LIST_CHANGED, ListenerUpdate.ACTIVE_DEVICE_GONE) 547 .setExpectedBluetoothInteraction(NONE) 548 .setExpectedConnectionDevice(null) 549 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 550 .build()); 551 552 result.add(new BluetoothRouteTestParametersBuilder() 553 .setName("Device gets disconnected while active." 554 + " No fallback even though other devices available.") 555 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 556 .setInitialDevice(DEVICE2) 557 .setConnectedDevices(DEVICE3) 558 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 559 .setMessageDevice(DEVICE2) 560 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 561 ListenerUpdate.DEVICE_LIST_CHANGED) 562 .setExpectedBluetoothInteraction(NONE) 563 .setExpectedConnectionDevice(null) 564 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 565 .build()); 566 567 result.add(new BluetoothRouteTestParametersBuilder() 568 .setName("Connection to DEVICE2 times out but device 1 still connected.") 569 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 570 .setInitialDevice(DEVICE2) 571 .setConnectedDevices(DEVICE2, DEVICE1) 572 .setAudioOnDevice(DEVICE1) 573 .setMessageType(BluetoothRouteManager.CONNECTION_TIMEOUT) 574 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED) 575 .setExpectedBluetoothInteraction(NONE) 576 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX 577 + ":" + DEVICE1) 578 .build()); 579 580 result.add(new BluetoothRouteTestParametersBuilder() 581 .setName("DEVICE1 somehow becomes active when DEVICE2 is still pending.") 582 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 583 .setInitialDevice(DEVICE2) 584 .setConnectedDevices(DEVICE2, DEVICE1) 585 .setAudioOnDevice(DEVICE1) 586 .setMessageType(BluetoothRouteManager.BT_AUDIO_IS_ON) 587 .setMessageDevice(DEVICE1) 588 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED) 589 .setExpectedBluetoothInteraction(NONE) 590 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX 591 + ":" + DEVICE1) 592 .build()); 593 594 result.add(new BluetoothRouteTestParametersBuilder() 595 .setName("Device gets disconnected while pending." 596 + " No fallback even though other devices available.") 597 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 598 .setInitialDevice(DEVICE2) 599 .setConnectedDevices(DEVICE3) 600 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 601 .setMessageDevice(DEVICE2) 602 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 603 ListenerUpdate.DEVICE_LIST_CHANGED) 604 .setExpectedBluetoothInteraction(NONE) 605 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 606 .build()); 607 608 result.add(new BluetoothRouteTestParametersBuilder() 609 .setName("Audio disconnect comes with a null device") 610 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 611 .setInitialDevice(DEVICE2) 612 .setConnectedDevices(DEVICE2) 613 .setMessageType(BluetoothRouteManager.BT_AUDIO_LOST) 614 .setMessageDevice(null) 615 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED) 616 .setExpectedBluetoothInteraction(NONE) 617 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 618 .build()); 619 620 result.add(new BluetoothRouteTestParametersBuilder() 621 .setName("Device gets disconnected while pending. No fallback.") 622 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 623 .setInitialDevice(DEVICE2) 624 .setConnectedDevices() 625 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 626 .setMessageDevice(DEVICE2) 627 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 628 ListenerUpdate.DEVICE_LIST_CHANGED) 629 .setExpectedBluetoothInteraction(NONE) 630 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 631 .build()); 632 633 result.add(new BluetoothRouteTestParametersBuilder() 634 .setName("Device gets audio-off while in another device's audio on state") 635 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 636 .setInitialDevice(DEVICE2) 637 .setConnectedDevices(DEVICE2, DEVICE1) 638 .setMessageType(BluetoothRouteManager.BT_AUDIO_LOST) 639 .setMessageDevice(DEVICE1) 640 .setExpectedListenerUpdates(ListenerUpdate.UNEXPECTED_STATE_CHANGE) 641 .setExpectedBluetoothInteraction(NONE) 642 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX 643 + ":" + DEVICE2) 644 .build()); 645 646 result.add(new BluetoothRouteTestParametersBuilder() 647 .setName("Audio routing requests BT disconnection while a device is active") 648 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 649 .setInitialDevice(DEVICE2) 650 .setConnectedDevices(DEVICE2, DEVICE3) 651 .setMessageType(BluetoothRouteManager.DISCONNECT_BT) 652 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED) 653 .setExpectedBluetoothInteraction(DISCONNECT) 654 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 655 .build()); 656 657 result.add(new BluetoothRouteTestParametersBuilder() 658 .setName("Audio routing requests BT disconnection while a device is pending") 659 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX) 660 .setInitialDevice(DEVICE2) 661 .setConnectedDevices(DEVICE2, DEVICE3) 662 .setMessageType(BluetoothRouteManager.DISCONNECT_BT) 663 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED) 664 .setExpectedBluetoothInteraction(DISCONNECT) 665 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 666 .build()); 667 668 result.add(new BluetoothRouteTestParametersBuilder() 669 .setName("Bluetooth turns itself on.") 670 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 671 .setInitialDevice(null) 672 .setConnectedDevices(DEVICE2, DEVICE3) 673 .setMessageType(BluetoothRouteManager.BT_AUDIO_IS_ON) 674 .setMessageDevice(DEVICE3) 675 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED) 676 .setExpectedBluetoothInteraction(NONE) 677 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX 678 + ":" + DEVICE3) 679 .build()); 680 681 result.add(new BluetoothRouteTestParametersBuilder() 682 .setName("Hearing aid device disconnects with headset present") 683 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 684 .setInitialDevice(DEVICE2) 685 .setConnectedDevices(DEVICE2, DEVICE3) 686 .setHearingAidBtDevices(Collections.singletonList(DEVICE2)) 687 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 688 .setMessageDevice(DEVICE2) 689 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 690 ListenerUpdate.DEVICE_LIST_CHANGED) 691 .setExpectedBluetoothInteraction(NONE) 692 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 693 .build()); 694 695 result.add(new BluetoothRouteTestParametersBuilder() 696 .setName("connect BT to an already active device when in audio off.") 697 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 698 .setAudioOnDevice(DEVICE2) 699 .setActiveDevice(DEVICE2) 700 .setConnectedDevices(DEVICE2, DEVICE3) 701 .setHearingAidBtDevices(Collections.singletonList(DEVICE2)) 702 .setMessageType(BluetoothRouteManager.CONNECT_BT) 703 .setMessageDevice(DEVICE2) 704 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED) 705 .setExpectedBluetoothInteraction(NONE) 706 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX 707 + ":" + DEVICE2) 708 .build()); 709 710 result.add(new BluetoothRouteTestParametersBuilder() 711 .setName("le audio device disconnects with hearing aid present") 712 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 713 .setInitialDevice(DEVICE2) 714 .setConnectedDevices(DEVICE2, DEVICE3) 715 .setLeAudioDevices(Collections.singletonList(DEVICE2)) 716 .setHearingAidBtDevices(Collections.singletonList(DEVICE3)) 717 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 718 .setMessageDevice(DEVICE2) 719 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 720 ListenerUpdate.DEVICE_LIST_CHANGED) 721 .setExpectedBluetoothInteraction(NONE) 722 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 723 .build()); 724 725 result.add(new BluetoothRouteTestParametersBuilder() 726 .setName("le audio device disconnects with another one connected") 727 .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX) 728 .setInitialDevice(DEVICE1) 729 .setConnectedDevices(DEVICE1, DEVICE2, DEVICE3) 730 .setHearingAidBtDevices(Collections.singletonList(DEVICE3)) 731 .setLeAudioDevices(Arrays.asList(DEVICE1, DEVICE2)) 732 .setMessageType(BluetoothRouteManager.LOST_DEVICE) 733 .setMessageDevice(DEVICE1) 734 .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED, 735 ListenerUpdate.DEVICE_LIST_CHANGED) 736 .setExpectedBluetoothInteraction(NONE) 737 .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME) 738 .build()); 739 740 return result; 741 } 742 } 743