CallAudioRouteStateMachineTest.java revision f62630a57de0d52be2bdbc92a9bf8f305cc0892d
1/* 2 * Copyright (C) 2015 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 17package com.android.server.telecom.tests; 18 19import android.content.Context; 20import android.media.AudioManager; 21import android.media.IAudioService; 22import android.telecom.CallAudioState; 23 24import com.android.server.telecom.BluetoothManager; 25import com.android.server.telecom.Call; 26import com.android.server.telecom.CallAudioManager; 27import com.android.server.telecom.CallAudioRouteStateMachine; 28import com.android.server.telecom.CallsManager; 29import com.android.server.telecom.ConnectionServiceWrapper; 30import com.android.server.telecom.StatusBarNotifier; 31import com.android.server.telecom.WiredHeadsetManager; 32 33import org.mockito.ArgumentCaptor; 34import org.mockito.Mock; 35import org.mockito.MockitoAnnotations; 36import org.mockito.invocation.InvocationOnMock; 37import org.mockito.stubbing.Answer; 38 39import java.util.ArrayList; 40import java.util.List; 41 42import static org.mockito.Matchers.any; 43import static org.mockito.Matchers.same; 44import static org.mockito.Mockito.doAnswer; 45import static org.mockito.Mockito.doNothing; 46import static org.mockito.Mockito.never; 47import static org.mockito.Mockito.reset; 48import static org.mockito.Mockito.timeout; 49import static org.mockito.Mockito.verify; 50import static org.mockito.Mockito.when; 51 52 53public class CallAudioRouteStateMachineTest extends TelecomTestCase { 54 private static final int NONE = 0; 55 private static final int ON = 1; 56 private static final int OFF = 2; 57 58 private class TestParameters { 59 public String name; 60 public int initialRoute; 61 public int availableRoutes; // may excl. speakerphone, because that's always available 62 public int speakerInteraction; // one of NONE, ON, or OFF 63 public int bluetoothInteraction; // one of NONE, ON, or OFF 64 public int action; 65 public int expectedRoute; 66 public int expectedAvailableRoutes; // also may exclude the speakerphone. 67 68 public TestParameters(String name, int initialRoute, int availableRoutes, int 69 speakerInteraction, int bluetoothInteraction, int action, int expectedRoute, int 70 expectedAvailableRoutes) { 71 this.name = name; 72 this.initialRoute = initialRoute; 73 this.availableRoutes = availableRoutes; 74 this.speakerInteraction = speakerInteraction; 75 this.bluetoothInteraction = bluetoothInteraction; 76 this.action = action; 77 this.expectedRoute = expectedRoute; 78 this.expectedAvailableRoutes = expectedAvailableRoutes; 79 } 80 81 @Override 82 public String toString() { 83 return "TestParameters{" + 84 "name='" + name + '\'' + 85 ", initialRoute=" + initialRoute + 86 ", availableRoutes=" + availableRoutes + 87 ", speakerInteraction=" + speakerInteraction + 88 ", bluetoothInteraction=" + bluetoothInteraction + 89 ", action=" + action + 90 ", expectedRoute=" + expectedRoute + 91 ", expectedAvailableRoutes=" + expectedAvailableRoutes + 92 '}'; 93 } 94 } 95 96 @Mock CallsManager mockCallsManager; 97 @Mock BluetoothManager mockBluetoothManager; 98 @Mock IAudioService mockAudioService; 99 @Mock ConnectionServiceWrapper mockConnectionServiceWrapper; 100 @Mock WiredHeadsetManager mockWiredHeadsetManager; 101 @Mock StatusBarNotifier mockStatusBarNotifier; 102 @Mock Call fakeCall; 103 104 private CallAudioManager.AudioServiceFactory mAudioServiceFactory; 105 private static final int TEST_TIMEOUT = 200; 106 private AudioManager mockAudioManager; 107 @Override 108 public void setUp() throws Exception { 109 super.setUp(); 110 MockitoAnnotations.initMocks(this); 111 mContext = mComponentContextFixture.getTestDouble().getApplicationContext(); 112 mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 113 114 mAudioServiceFactory = new CallAudioManager.AudioServiceFactory() { 115 @Override 116 public IAudioService getAudioService() { 117 return mockAudioService; 118 } 119 }; 120 121 when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall); 122 when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper); 123 when(fakeCall.isAlive()).thenReturn(true); 124 doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class), 125 any(CallAudioState.class)); 126 } 127 128 public void testStateMachineTransitionsWithFocus() throws Throwable { 129 List<TestParameters> paramList = generateTransitionTests(); 130 for (TestParameters params : paramList) { 131 try { 132 runParametrizedTestCaseWithFocus(params); 133 } catch (Throwable e) { 134 String newMessage = "Failed at parameters: \n" + params.toString() + '\n' 135 + e.getMessage(); 136 throw(new Throwable(newMessage, e)); 137 } 138 } 139 } 140 141 public void testStateMachineTransitionsWithoutFocus() throws Throwable { 142 List<TestParameters> paramList = generateTransitionTests(); 143 for (TestParameters params : paramList) { 144 try { 145 runParametrizedTestCaseWithoutFocus(params); 146 } catch (Throwable e) { 147 String newMessage = "Failed at parameters: \n" + params.toString() + '\n' 148 + e.getMessage(); 149 throw(new Throwable(newMessage, e)); 150 } 151 } 152 } 153 154 public void testSpeakerPersistence() { 155 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 156 mContext, 157 mockCallsManager, 158 mockBluetoothManager, 159 mockWiredHeadsetManager, 160 mockStatusBarNotifier, 161 mAudioServiceFactory); 162 163 when(mockBluetoothManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); 164 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(true); 165 when(mockAudioManager.isSpeakerphoneOn()).thenReturn(true); 166 doAnswer(new Answer() { 167 @Override 168 public Object answer(InvocationOnMock invocation) throws Throwable { 169 Object[] args = invocation.getArguments(); 170 when(mockAudioManager.isSpeakerphoneOn()).thenReturn((Boolean) args[0]); 171 return null; 172 } 173 }).when(mockAudioManager).setSpeakerphoneOn(any(Boolean.class)); 174 CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER, 175 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); 176 stateMachine.initialize(initState); 177 178 stateMachine.sendMessage(CallAudioRouteStateMachine.SWITCH_FOCUS, 179 CallAudioRouteStateMachine.HAS_FOCUS); 180 stateMachine.sendMessage(CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET); 181 CallAudioState expectedMiddleState = new CallAudioState(false, 182 CallAudioState.ROUTE_WIRED_HEADSET, 183 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER); 184 verifyNewSystemCallAudioState(initState, expectedMiddleState); 185 resetMocks(); 186 187 stateMachine.sendMessage(CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET); 188 verifyNewSystemCallAudioState(expectedMiddleState, initState); 189 } 190 191 public void testInitializationWithNoHeadsetNoBluetooth() { 192 when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(false); 193 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(false); 194 195 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 196 mContext, 197 mockCallsManager, 198 mockBluetoothManager, 199 mockWiredHeadsetManager, 200 mockStatusBarNotifier, 201 mAudioServiceFactory); 202 stateMachine.initialize(); 203 CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, 204 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); 205 assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); 206 } 207 208 public void testInitializationWithHeadsetNoBluetooth() { 209 when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(true); 210 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(false); 211 212 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 213 mContext, 214 mockCallsManager, 215 mockBluetoothManager, 216 mockWiredHeadsetManager, 217 mockStatusBarNotifier, 218 mAudioServiceFactory); 219 stateMachine.initialize(); 220 CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET, 221 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER); 222 assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); 223 } 224 225 public void testInitializationWithHeadsetAndBluetooth() { 226 when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(true); 227 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(true); 228 229 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 230 mContext, 231 mockCallsManager, 232 mockBluetoothManager, 233 mockWiredHeadsetManager, 234 mockStatusBarNotifier, 235 mAudioServiceFactory); 236 stateMachine.initialize(); 237 CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH, 238 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER 239 | CallAudioState.ROUTE_BLUETOOTH); 240 assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); 241 } 242 243 public void testInitializationWithBluetoothNoHeadset() { 244 when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(false); 245 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(true); 246 247 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 248 mContext, 249 mockCallsManager, 250 mockBluetoothManager, 251 mockWiredHeadsetManager, 252 mockStatusBarNotifier, 253 mAudioServiceFactory); 254 stateMachine.initialize(); 255 CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH, 256 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER 257 | CallAudioState.ROUTE_BLUETOOTH); 258 assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); 259 } 260 261 private List<TestParameters> generateTransitionTests() { 262 List<TestParameters> params = new ArrayList<>(); 263 params.add(new TestParameters( 264 "Connect headset during earpiece", // name 265 CallAudioState.ROUTE_EARPIECE, // initialRoute 266 CallAudioState.ROUTE_EARPIECE, // availableRoutes 267 NONE, // speakerInteraction 268 NONE, // bluetoothInteraction 269 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action 270 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute 271 CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes 272 )); 273 274 params.add(new TestParameters( 275 "Connect headset during bluetooth", // name 276 CallAudioState.ROUTE_BLUETOOTH, // initialRoute 277 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes 278 NONE, // speakerInteraction 279 OFF, // bluetoothInteraction 280 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action 281 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute 282 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH // expectedAvail 283 )); 284 285 params.add(new TestParameters( 286 "Connect headset during speakerphone", // name 287 CallAudioState.ROUTE_SPEAKER, // initialRoute 288 CallAudioState.ROUTE_EARPIECE, // availableRoutes 289 OFF, // speakerInteraction 290 NONE, // bluetoothInteraction 291 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action 292 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute 293 CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes 294 )); 295 296 params.add(new TestParameters( 297 "Disconnect headset during headset", // name 298 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute 299 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes 300 NONE, // speakerInteraction 301 NONE, // bluetoothInteraction 302 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action 303 CallAudioState.ROUTE_EARPIECE, // expectedRoute 304 CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes 305 )); 306 307 params.add(new TestParameters( 308 "Disconnect headset during headset with bluetooth available", // name 309 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute 310 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 311 NONE, // speakerInteraction 312 NONE, // bluetoothInteraction 313 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action 314 CallAudioState.ROUTE_EARPIECE, // expectedRoute 315 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR 316 )); 317 318 params.add(new TestParameters( 319 "Disconnect headset during bluetooth", // name 320 CallAudioState.ROUTE_BLUETOOTH, // initialRoute 321 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 322 NONE, // speakerInteraction 323 NONE, // bluetoothInteraction 324 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action 325 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 326 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR 327 )); 328 329 params.add(new TestParameters( 330 "Disconnect headset during speakerphone", // name 331 CallAudioState.ROUTE_SPEAKER, // initialRoute 332 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes 333 NONE, // speakerInteraction 334 NONE, // bluetoothInteraction 335 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action 336 CallAudioState.ROUTE_SPEAKER, // expectedRoute 337 CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes 338 )); 339 340 params.add(new TestParameters( 341 "Disconnect headset during speakerphone with bluetooth available", // name 342 CallAudioState.ROUTE_SPEAKER, // initialRoute 343 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 344 NONE, // speakerInteraction 345 NONE, // bluetoothInteraction 346 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action 347 CallAudioState.ROUTE_SPEAKER, // expectedRoute 348 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR 349 )); 350 351 params.add(new TestParameters( 352 "Connect bluetooth during earpiece", // name 353 CallAudioState.ROUTE_EARPIECE, // initialRoute 354 CallAudioState.ROUTE_EARPIECE, // availableRoutes 355 NONE, // speakerInteraction 356 ON, // bluetoothInteraction 357 CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action 358 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 359 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE // expectedAvailableR 360 )); 361 362 params.add(new TestParameters( 363 "Connect bluetooth during wired headset", // name 364 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute 365 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes 366 NONE, // speakerInteraction 367 ON, // bluetoothInteraction 368 CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action 369 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 370 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET // expectedAvail 371 )); 372 373 params.add(new TestParameters( 374 "Connect bluetooth during speakerphone", // name 375 CallAudioState.ROUTE_SPEAKER, // initialRoute 376 CallAudioState.ROUTE_EARPIECE, // availableRoutes 377 OFF, // speakerInteraction 378 ON, // bluetoothInteraction 379 CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action 380 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 381 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE // expectedAvailableR 382 )); 383 384 params.add(new TestParameters( 385 "Disconnect bluetooth during bluetooth without headset in", // name 386 CallAudioState.ROUTE_BLUETOOTH, // initialRoute 387 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes 388 NONE, // speakerInteraction 389 OFF, // bluetoothInteraction 390 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action 391 CallAudioState.ROUTE_EARPIECE, // expectedRoute 392 CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes 393 )); 394 395 params.add(new TestParameters( 396 "Disconnect bluetooth during bluetooth with headset in", // name 397 CallAudioState.ROUTE_BLUETOOTH, // initialRoute 398 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 399 NONE, // speakerInteraction 400 OFF, // bluetoothInteraction 401 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action 402 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute 403 CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes 404 )); 405 406 params.add(new TestParameters( 407 "Disconnect bluetooth during speakerphone", // name 408 CallAudioState.ROUTE_SPEAKER, // initialRoute 409 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 410 NONE, // speakerInteraction 411 NONE, // bluetoothInteraction 412 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action 413 CallAudioState.ROUTE_SPEAKER, // expectedRoute 414 CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes 415 )); 416 417 params.add(new TestParameters( 418 "Disconnect bluetooth during earpiece", // name 419 CallAudioState.ROUTE_EARPIECE, // initialRoute 420 CallAudioState.ROUTE_EARPIECE| CallAudioState.ROUTE_BLUETOOTH, // availableRoutes 421 NONE, // speakerInteraction 422 NONE, // bluetoothInteraction 423 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action 424 CallAudioState.ROUTE_EARPIECE, // expectedRoute 425 CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes 426 )); 427 428 params.add(new TestParameters( 429 "Switch to speakerphone from earpiece", // name 430 CallAudioState.ROUTE_EARPIECE, // initialRoute 431 CallAudioState.ROUTE_EARPIECE, // availableRoutes 432 ON, // speakerInteraction 433 NONE, // bluetoothInteraction 434 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action 435 CallAudioState.ROUTE_SPEAKER, // expectedRoute 436 CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes 437 )); 438 439 params.add(new TestParameters( 440 "Switch to speakerphone from headset", // name 441 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute 442 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes 443 ON, // speakerInteraction 444 NONE, // bluetoothInteraction 445 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action 446 CallAudioState.ROUTE_SPEAKER, // expectedRoute 447 CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes 448 )); 449 450 params.add(new TestParameters( 451 "Switch to speakerphone from bluetooth", // name 452 CallAudioState.ROUTE_BLUETOOTH, // initialRoute 453 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 454 ON, // speakerInteraction 455 OFF, // bluetoothInteraction 456 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action 457 CallAudioState.ROUTE_SPEAKER, // expectedRoute 458 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH // expectedAvail 459 )); 460 461 params.add(new TestParameters( 462 "Switch to earpiece from bluetooth", // name 463 CallAudioState.ROUTE_BLUETOOTH, // initialRoute 464 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes 465 NONE, // speakerInteraction 466 OFF, // bluetoothInteraction 467 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action 468 CallAudioState.ROUTE_EARPIECE, // expectedRoute 469 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR 470 )); 471 472 params.add(new TestParameters( 473 "Switch to earpiece from speakerphone", // name 474 CallAudioState.ROUTE_SPEAKER, // initialRoute 475 CallAudioState.ROUTE_EARPIECE, // availableRoutes 476 OFF, // speakerInteraction 477 NONE, // bluetoothInteraction 478 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action 479 CallAudioState.ROUTE_EARPIECE, // expectedRoute 480 CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes 481 )); 482 483 params.add(new TestParameters( 484 "Switch to bluetooth from speakerphone", // name 485 CallAudioState.ROUTE_SPEAKER, // initialRoute 486 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes 487 OFF, // speakerInteraction 488 ON, // bluetoothInteraction 489 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action 490 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 491 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR 492 )); 493 494 params.add(new TestParameters( 495 "Switch to bluetooth from earpiece", // name 496 CallAudioState.ROUTE_EARPIECE, // initialRoute 497 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes 498 NONE, // speakerInteraction 499 ON, // bluetoothInteraction 500 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action 501 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 502 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR 503 )); 504 505 params.add(new TestParameters( 506 "Switch to bluetooth from wired headset", // name 507 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute 508 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou 509 NONE, // speakerInteraction 510 ON, // bluetoothInteraction 511 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action 512 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute 513 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH // expectedAvail 514 )); 515 516 return params; 517 } 518 519 private void runParametrizedTestCaseWithFocus(TestParameters params) { 520 resetMocks(); 521 522 // Construct a fresh state machine on every case 523 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 524 mContext, 525 mockCallsManager, 526 mockBluetoothManager, 527 mockWiredHeadsetManager, 528 mockStatusBarNotifier, 529 mAudioServiceFactory); 530 531 // Set up bluetooth and speakerphone state 532 when(mockBluetoothManager.isBluetoothAudioConnectedOrPending()).thenReturn( 533 params.initialRoute == CallAudioState.ROUTE_BLUETOOTH); 534 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn( 535 (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0 536 || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0); 537 when(mockAudioManager.isSpeakerphoneOn()).thenReturn( 538 params.initialRoute == CallAudioState.ROUTE_SPEAKER); 539 540 // Set the initial CallAudioState object 541 CallAudioState initState = new CallAudioState(false, 542 params.initialRoute, (params.availableRoutes | CallAudioState.ROUTE_SPEAKER)); 543 stateMachine.initialize(initState); 544 // Make the state machine have focus so that we actually do something 545 stateMachine.sendMessage(CallAudioRouteStateMachine.SWITCH_FOCUS, 546 CallAudioRouteStateMachine.HAS_FOCUS); 547 stateMachine.sendMessage(params.action); 548 549 // Verify interactions with the speakerphone and bluetooth systems 550 switch(params.bluetoothInteraction) { 551 case NONE: 552 verify(mockBluetoothManager, never()).disconnectBluetoothAudio(); 553 verify(mockBluetoothManager, never()).connectBluetoothAudio(); 554 break; 555 case ON: 556 verify(mockBluetoothManager, timeout(TEST_TIMEOUT)).connectBluetoothAudio(); 557 verify(mockBluetoothManager, never()).disconnectBluetoothAudio(); 558 break; 559 case OFF: 560 verify(mockBluetoothManager, never()).connectBluetoothAudio(); 561 verify(mockBluetoothManager, timeout(TEST_TIMEOUT)).disconnectBluetoothAudio(); 562 } 563 564 switch (params.speakerInteraction) { 565 case NONE: 566 verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class)); 567 break; 568 case ON: // fall through 569 case OFF: 570 verify(mockAudioManager, timeout(TEST_TIMEOUT)).setSpeakerphoneOn( 571 params.speakerInteraction == ON); 572 } 573 574 // Verify the end state 575 CallAudioState expectedState = new CallAudioState(false, params.expectedRoute, 576 params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER); 577 verifyNewSystemCallAudioState(initState, expectedState); 578 } 579 580 private void runParametrizedTestCaseWithoutFocus(TestParameters params) { 581 resetMocks(); 582 583 // Construct a fresh state machine on every case 584 CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( 585 mContext, 586 mockCallsManager, 587 mockBluetoothManager, 588 mockWiredHeadsetManager, 589 mockStatusBarNotifier, 590 mAudioServiceFactory); 591 592 // Set up bluetooth and speakerphone state 593 when(mockBluetoothManager.isBluetoothAvailable()).thenReturn( 594 (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0 595 || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0); 596 when(mockAudioManager.isSpeakerphoneOn()).thenReturn( 597 params.initialRoute == CallAudioState.ROUTE_SPEAKER); 598 599 // Set the initial CallAudioState object 600 CallAudioState initState = new CallAudioState(false, 601 params.initialRoute, (params.availableRoutes | CallAudioState.ROUTE_SPEAKER)); 602 stateMachine.initialize(initState); 603 // Omit the focus-getting statement 604 stateMachine.sendMessage(params.action); 605 try { 606 Thread.sleep(100L); 607 } catch (InterruptedException e) { 608 // Just a pause to make sure the state machine handler thread has a chance to update 609 // its state. Do nothing. 610 } 611 612 // Verify that no substantive interactions have taken place with the rest of the system 613 verify(mockBluetoothManager, never()).disconnectBluetoothAudio(); 614 verify(mockBluetoothManager, never()).connectBluetoothAudio(); 615 verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class)); 616 verify(mockCallsManager, never()).onCallAudioStateChanged(any(CallAudioState.class), 617 any(CallAudioState.class)); 618 verify(mockConnectionServiceWrapper, never()).onCallAudioStateChanged( 619 any(Call.class), any(CallAudioState.class)); 620 621 // Verify the end state 622 CallAudioState expectedState = new CallAudioState(false, params.expectedRoute, 623 params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER); 624 assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); 625 } 626 627 private void verifyNewSystemCallAudioState(CallAudioState expectedOldState, 628 CallAudioState expectedNewState) { 629 ArgumentCaptor<CallAudioState> oldStateCaptor = ArgumentCaptor.forClass( 630 CallAudioState.class); 631 ArgumentCaptor<CallAudioState> newStateCaptor1 = ArgumentCaptor.forClass( 632 CallAudioState.class); 633 ArgumentCaptor<CallAudioState> newStateCaptor2 = ArgumentCaptor.forClass( 634 CallAudioState.class); 635 verify(mockCallsManager, timeout(TEST_TIMEOUT).atLeastOnce()).onCallAudioStateChanged( 636 oldStateCaptor.capture(), newStateCaptor1.capture()); 637 verify(mockConnectionServiceWrapper, timeout(TEST_TIMEOUT).atLeastOnce()) 638 .onCallAudioStateChanged(same(fakeCall), newStateCaptor2.capture()); 639 640 assertTrue(oldStateCaptor.getValue().equals(expectedOldState)); 641 assertTrue(newStateCaptor1.getValue().equals(expectedNewState)); 642 assertTrue(newStateCaptor2.getValue().equals(expectedNewState)); 643 } 644 645 private void resetMocks() { 646 reset(mockAudioManager, mockBluetoothManager, mockCallsManager, 647 mockConnectionServiceWrapper); 648 when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall); 649 doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class), 650 any(CallAudioState.class)); 651 } 652}