• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.telecom.tests;
18 
19 import android.app.NotificationManager;
20 import android.content.Context;
21 import android.media.AudioManager;
22 import android.media.IAudioService;
23 import android.telecom.CallAudioState;
24 import android.test.suitebuilder.annotation.LargeTest;
25 import android.test.suitebuilder.annotation.MediumTest;
26 import android.test.suitebuilder.annotation.SmallTest;
27 
28 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
29 import com.android.server.telecom.Call;
30 import com.android.server.telecom.CallAudioModeStateMachine;
31 import com.android.server.telecom.CallAudioRouteStateMachine;
32 import com.android.server.telecom.CallsManager;
33 import com.android.server.telecom.ConnectionServiceWrapper;
34 import com.android.server.telecom.CallAudioManager;
35 import com.android.server.telecom.StatusBarNotifier;
36 import com.android.server.telecom.TelecomSystem;
37 import com.android.server.telecom.WiredHeadsetManager;
38 
39 import org.mockito.ArgumentCaptor;
40 import org.mockito.Mock;
41 import org.mockito.MockitoAnnotations;
42 import org.mockito.invocation.InvocationOnMock;
43 import org.mockito.stubbing.Answer;
44 
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Objects;
48 
49 import static org.mockito.Matchers.any;
50 import static org.mockito.Matchers.anyInt;
51 import static org.mockito.Matchers.eq;
52 import static org.mockito.Matchers.same;
53 import static org.mockito.Mockito.doAnswer;
54 import static org.mockito.Mockito.doNothing;
55 import static org.mockito.Mockito.doReturn;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.never;
58 import static org.mockito.Mockito.reset;
59 import static org.mockito.Mockito.timeout;
60 import static org.mockito.Mockito.times;
61 import static org.mockito.Mockito.verify;
62 import static org.mockito.Mockito.when;
63 
64 
65 public class CallAudioRouteStateMachineTest
66         extends StateMachineTestBase<CallAudioRouteStateMachine> {
67     private static final int NONE = 0;
68     private static final int ON = 1;
69     private static final int OFF = 2;
70 
71     static class RoutingTestParameters extends TestParameters {
72         public String name;
73         public int initialRoute;
74         public int availableRoutes; // may excl. speakerphone, because that's always available
75         public int speakerInteraction; // one of NONE, ON, or OFF
76         public int bluetoothInteraction; // one of NONE, ON, or OFF
77         public int action;
78         public int expectedRoute;
79         public int expectedAvailableRoutes; // also may exclude the speakerphone.
80         public boolean doesDeviceSupportEarpiece; // set to false in the case of Wear devices
81         public boolean shouldRunWithFocus;
82 
83         public int callSupportedRoutes = CallAudioState.ROUTE_ALL;
84 
RoutingTestParameters(String name, int initialRoute, int availableRoutes, int speakerInteraction, int bluetoothInteraction, int action, int expectedRoute, int expectedAvailableRoutes, boolean doesDeviceSupportEarpiece, boolean shouldRunWithFocus)85         public RoutingTestParameters(String name, int initialRoute,
86                 int availableRoutes, int speakerInteraction,
87                 int bluetoothInteraction, int action, int expectedRoute,
88                 int expectedAvailableRoutes, boolean doesDeviceSupportEarpiece,
89                 boolean shouldRunWithFocus) {
90             this.name = name;
91             this.initialRoute = initialRoute;
92             this.availableRoutes = availableRoutes;
93             this.speakerInteraction = speakerInteraction;
94             this.bluetoothInteraction = bluetoothInteraction;
95             this.action = action;
96             this.expectedRoute = expectedRoute;
97             this.expectedAvailableRoutes = expectedAvailableRoutes;
98             this.doesDeviceSupportEarpiece = doesDeviceSupportEarpiece;
99             this.shouldRunWithFocus = shouldRunWithFocus;
100         }
101 
setCallSupportedRoutes(int routes)102         public RoutingTestParameters setCallSupportedRoutes(int routes) {
103             callSupportedRoutes = routes;
104             return this;
105         }
106 
107         @Override
toString()108         public String toString() {
109             return "RoutingTestParameters{" +
110                     "name='" + name + '\'' +
111                     ", initialRoute=" + initialRoute +
112                     ", availableRoutes=" + availableRoutes +
113                     ", speakerInteraction=" + speakerInteraction +
114                     ", bluetoothInteraction=" + bluetoothInteraction +
115                     ", action=" + action +
116                     ", expectedRoute=" + expectedRoute +
117                     ", expectedAvailableRoutes=" + expectedAvailableRoutes +
118                     ", doesDeviceSupportEarpiece=" + doesDeviceSupportEarpiece +
119                     ", shouldRunWithFocus=" + shouldRunWithFocus +
120                     '}';
121         }
122     }
123 
124     @Mock CallsManager mockCallsManager;
125     @Mock BluetoothRouteManager mockBluetoothRouteManager;
126     @Mock IAudioService mockAudioService;
127     @Mock ConnectionServiceWrapper mockConnectionServiceWrapper;
128     @Mock WiredHeadsetManager mockWiredHeadsetManager;
129     @Mock StatusBarNotifier mockStatusBarNotifier;
130     @Mock Call fakeCall;
131 
132     private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
133     private static final int TEST_TIMEOUT = 500;
134     private AudioManager mockAudioManager;
135     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
136 
137     @Override
setUp()138     public void setUp() throws Exception {
139         super.setUp();
140         MockitoAnnotations.initMocks(this);
141         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
142         mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
143 
144         mAudioServiceFactory = new CallAudioManager.AudioServiceFactory() {
145             @Override
146             public IAudioService getAudioService() {
147                 return mockAudioService;
148             }
149         };
150 
151         when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
152         when(mockCallsManager.getLock()).thenReturn(mLock);
153         when(mockCallsManager.hasVideoCall()).thenReturn(false);
154         when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
155         when(fakeCall.isAlive()).thenReturn(true);
156         when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL);
157 
158         doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
159                 any(CallAudioState.class));
160     }
161 
162     @LargeTest
testStateMachineTransitionsWithFocus()163     public void testStateMachineTransitionsWithFocus() throws Throwable {
164         List<RoutingTestParameters> paramList = generateTransitionTests(true);
165         parametrizedTestStateMachine(paramList);
166     }
167 
168     @LargeTest
testStateMachineTransitionsWithoutFocus()169     public void testStateMachineTransitionsWithoutFocus() throws Throwable {
170         List<RoutingTestParameters> paramList = generateTransitionTests(false);
171         parametrizedTestStateMachine(paramList);
172     }
173 
174     @MediumTest
testSpeakerPersistence()175     public void testSpeakerPersistence() {
176         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
177                 mContext,
178                 mockCallsManager,
179                 mockBluetoothRouteManager,
180                 mockWiredHeadsetManager,
181                 mockStatusBarNotifier,
182                 mAudioServiceFactory,
183                 true);
184 
185         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
186         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
187         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(true);
188         doAnswer(new Answer() {
189             @Override
190             public Object answer(InvocationOnMock invocation) throws Throwable {
191                 Object[] args = invocation.getArguments();
192                 when(mockAudioManager.isSpeakerphoneOn()).thenReturn((Boolean) args[0]);
193                 return null;
194             }
195         }).when(mockAudioManager).setSpeakerphoneOn(any(Boolean.class));
196         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
197                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);
198         stateMachine.initialize(initState);
199 
200         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
201                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
202         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET);
203         CallAudioState expectedMiddleState = new CallAudioState(false,
204                 CallAudioState.ROUTE_WIRED_HEADSET,
205                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
206         waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
207         waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
208         verifyNewSystemCallAudioState(initState, expectedMiddleState);
209         resetMocks(true);
210 
211         stateMachine.sendMessageWithSessionInfo(
212                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET);
213         verifyNewSystemCallAudioState(expectedMiddleState, initState);
214     }
215 
216     @MediumTest
testUserBluetoothSwitchOff()217     public void testUserBluetoothSwitchOff() {
218         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
219                 mContext,
220                 mockCallsManager,
221                 mockBluetoothRouteManager,
222                 mockWiredHeadsetManager,
223                 mockStatusBarNotifier,
224                 mAudioServiceFactory,
225                 true);
226 
227         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
228         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
229         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(true);
230 
231         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
232                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
233         stateMachine.initialize(initState);
234 
235         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
236                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
237         stateMachine.sendMessageWithSessionInfo(
238                 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE);
239         CallAudioState expectedEndState = new CallAudioState(false,
240                 CallAudioState.ROUTE_EARPIECE,
241                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
242 
243         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
244         verifyNewSystemCallAudioState(initState, expectedEndState);
245         resetMocks(false);
246         stateMachine.sendMessageWithSessionInfo(
247                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH);
248         stateMachine.sendMessageWithSessionInfo(
249                 CallAudioRouteStateMachine.CONNECT_BLUETOOTH);
250 
251         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
252         assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState());
253     }
254 
255     @MediumTest
testBluetoothRinging()256     public void testBluetoothRinging() {
257         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
258                 mContext,
259                 mockCallsManager,
260                 mockBluetoothRouteManager,
261                 mockWiredHeadsetManager,
262                 mockStatusBarNotifier,
263                 mAudioServiceFactory,
264                 true);
265 
266         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
267         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
268         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
269 
270         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
271                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
272         stateMachine.initialize(initState);
273 
274         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
275                 CallAudioRouteStateMachine.RINGING_FOCUS);
276         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
277 
278         verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
279 
280         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
281                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
282         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
283         verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(null);
284     }
285 
286     @MediumTest
testConnectBluetoothDuringRinging()287     public void testConnectBluetoothDuringRinging() {
288         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
289                 mContext,
290                 mockCallsManager,
291                 mockBluetoothRouteManager,
292                 mockWiredHeadsetManager,
293                 mockStatusBarNotifier,
294                 mAudioServiceFactory,
295                 true);
296 
297         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
298         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(false);
299         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
300         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
301                 CallAudioState.ROUTE_EARPIECE);
302         stateMachine.initialize(initState);
303 
304         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
305                 CallAudioRouteStateMachine.RINGING_FOCUS);
306 
307         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
308         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.CONNECT_BLUETOOTH);
309         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
310 
311         verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
312         CallAudioState expectedEndState = new CallAudioState(false,
313                 CallAudioState.ROUTE_BLUETOOTH,
314                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
315         verifyNewSystemCallAudioState(initState, expectedEndState);
316 
317         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
318                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
319         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
320         verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(null);
321     }
322 
323     @SmallTest
testInitializationWithEarpieceNoHeadsetNoBluetooth()324     public void testInitializationWithEarpieceNoHeadsetNoBluetooth() {
325         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
326                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);
327         initializationTestHelper(expectedState, true);
328     }
329 
330     @SmallTest
testInitializationWithEarpieceAndHeadsetNoBluetooth()331     public void testInitializationWithEarpieceAndHeadsetNoBluetooth() {
332         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET,
333                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
334         initializationTestHelper(expectedState, true);
335     }
336 
337     @SmallTest
testInitializationWithEarpieceAndHeadsetAndBluetooth()338     public void testInitializationWithEarpieceAndHeadsetAndBluetooth() {
339         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
340                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER
341                 | CallAudioState.ROUTE_BLUETOOTH);
342         initializationTestHelper(expectedState, true);
343     }
344 
345     @SmallTest
testInitializationWithEarpieceAndBluetoothNoHeadset()346     public void testInitializationWithEarpieceAndBluetoothNoHeadset() {
347         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
348                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
349                         | CallAudioState.ROUTE_BLUETOOTH);
350         initializationTestHelper(expectedState, true);
351     }
352 
353     @SmallTest
testInitializationWithNoEarpieceNoHeadsetNoBluetooth()354     public void testInitializationWithNoEarpieceNoHeadsetNoBluetooth() {
355         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
356                 CallAudioState.ROUTE_SPEAKER);
357         initializationTestHelper(expectedState, false);
358     }
359 
360     @SmallTest
testInitializationWithHeadsetNoBluetoothNoEarpiece()361     public void testInitializationWithHeadsetNoBluetoothNoEarpiece() {
362         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET,
363                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
364         initializationTestHelper(expectedState, false);
365     }
366 
367     @SmallTest
testInitializationWithHeadsetAndBluetoothNoEarpiece()368     public void testInitializationWithHeadsetAndBluetoothNoEarpiece() {
369         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
370                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER
371                 | CallAudioState.ROUTE_BLUETOOTH);
372         initializationTestHelper(expectedState, false);
373     }
374 
375     @SmallTest
testInitializationWithBluetoothNoHeadsetNoEarpiece()376     public void testInitializationWithBluetoothNoHeadsetNoEarpiece() {
377         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
378                 CallAudioState.ROUTE_SPEAKER | CallAudioState.ROUTE_BLUETOOTH);
379         initializationTestHelper(expectedState, false);
380     }
381 
initializationTestHelper(CallAudioState expectedState, boolean doesDeviceSupportEarpiece)382     private void initializationTestHelper(CallAudioState expectedState,
383             boolean doesDeviceSupportEarpiece) {
384         when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(
385                 (expectedState.getSupportedRouteMask() & CallAudioState.ROUTE_WIRED_HEADSET) != 0);
386         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
387                 (expectedState.getSupportedRouteMask() & CallAudioState.ROUTE_BLUETOOTH) != 0);
388 
389         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
390                 mContext,
391                 mockCallsManager,
392                 mockBluetoothRouteManager,
393                 mockWiredHeadsetManager,
394                 mockStatusBarNotifier,
395                 mAudioServiceFactory,
396                 doesDeviceSupportEarpiece);
397         stateMachine.initialize();
398         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
399     }
400 
generateTransitionTests(boolean shouldRunWithFocus)401     private List<RoutingTestParameters> generateTransitionTests(boolean shouldRunWithFocus) {
402         List<RoutingTestParameters> params = new ArrayList<>();
403         params.add(new RoutingTestParameters(
404                 "Connect headset during earpiece", // name
405                 CallAudioState.ROUTE_EARPIECE, // initialRoute
406                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
407                 NONE, // speakerInteraction
408                 NONE, // bluetoothInteraction
409                 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
410                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
411                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
412                 true, // doesDeviceSupportEarpiece
413                 shouldRunWithFocus
414         ));
415 
416         params.add(new RoutingTestParameters(
417                 "Connect headset during bluetooth", // name
418                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
419                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
420                 NONE, // speakerInteraction
421                 OFF, // bluetoothInteraction
422                 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
423                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
424                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
425                 true, // doesDeviceSupportEarpiece
426                 shouldRunWithFocus
427         ));
428 
429         params.add(new RoutingTestParameters(
430                 "Connect headset during speakerphone", // name
431                 CallAudioState.ROUTE_SPEAKER, // initialRoute
432                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
433                 OFF, // speakerInteraction
434                 NONE, // bluetoothInteraction
435                 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
436                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
437                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
438                 true, // doesDeviceSupportEarpiece
439                 shouldRunWithFocus
440         ));
441 
442         params.add(new RoutingTestParameters(
443                 "Disconnect headset during headset", // name
444                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
445                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
446                 NONE, // speakerInteraction
447                 NONE, // bluetoothInteraction
448                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
449                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
450                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
451                 true, // doesDeviceSupportEarpiece
452                 shouldRunWithFocus
453         ));
454 
455         params.add(new RoutingTestParameters(
456                 "Disconnect headset during headset with bluetooth available", // name
457                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
458                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
459                 NONE, // speakerInteraction
460                 NONE, // bluetoothInteraction
461                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
462                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
463                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
464                 true, // doesDeviceSupportEarpiece
465                 shouldRunWithFocus
466         ));
467 
468         params.add(new RoutingTestParameters(
469                 "Disconnect headset during bluetooth", // name
470                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
471                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
472                 NONE, // speakerInteraction
473                 NONE, // bluetoothInteraction
474                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
475                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
476                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
477                 true, // doesDeviceSupportEarpiece
478                 shouldRunWithFocus
479         ));
480 
481         params.add(new RoutingTestParameters(
482                 "Disconnect headset during speakerphone", // name
483                 CallAudioState.ROUTE_SPEAKER, // initialRoute
484                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
485                 NONE, // speakerInteraction
486                 NONE, // bluetoothInteraction
487                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
488                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
489                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
490                 true, // doesDeviceSupportEarpiece
491                 shouldRunWithFocus
492         ));
493 
494         params.add(new RoutingTestParameters(
495                 "Disconnect headset during speakerphone with bluetooth available", // name
496                 CallAudioState.ROUTE_SPEAKER, // initialRoute
497                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
498                 NONE, // speakerInteraction
499                 NONE, // bluetoothInteraction
500                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
501                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
502                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
503                 true, // doesDeviceSupportEarpiece
504                 shouldRunWithFocus
505         ));
506 
507         params.add(new RoutingTestParameters(
508                 "Connect bluetooth during earpiece", // name
509                 CallAudioState.ROUTE_EARPIECE, // initialRoute
510                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
511                 NONE, // speakerInteraction
512                 ON, // bluetoothInteraction
513                 CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
514                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
515                 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
516                 true, // doesDeviceSupportEarpiece
517                 shouldRunWithFocus
518         ));
519 
520         params.add(new RoutingTestParameters(
521                 "Connect bluetooth during wired headset", // name
522                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
523                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
524                 NONE, // speakerInteraction
525                 ON, // bluetoothInteraction
526                 CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
527                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
528                 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvai
529                 true, // doesDeviceSupportEarpiece
530                 shouldRunWithFocus
531         ));
532 
533         params.add(new RoutingTestParameters(
534                 "Connect bluetooth during speakerphone", // name
535                 CallAudioState.ROUTE_SPEAKER, // initialRoute
536                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
537                 OFF, // speakerInteraction
538                 ON, // bluetoothInteraction
539                 CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
540                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
541                 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
542                 true, // doesDeviceSupportEarpiece
543                 shouldRunWithFocus
544         ));
545 
546         params.add(new RoutingTestParameters(
547                 "Disconnect bluetooth during bluetooth without headset in", // name
548                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
549                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
550                 NONE, // speakerInteraction
551                 OFF, // bluetoothInteraction
552                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
553                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
554                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
555                 true, // doesDeviceSupportEarpiece
556                 shouldRunWithFocus
557         ));
558 
559         params.add(new RoutingTestParameters(
560                 "Disconnect bluetooth during bluetooth without headset in, priority mode ", // name
561                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
562                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
563                 NONE, // speakerInteraction
564                 OFF, // bluetoothInteraction
565                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
566                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
567                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
568                 true, // doesDeviceSupportEarpiece
569                 shouldRunWithFocus
570         ));
571 
572         params.add(new RoutingTestParameters(
573                 "Disconnect bluetooth during bluetooth with headset in", // name
574                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
575                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
576                 NONE, // speakerInteraction
577                 OFF, // bluetoothInteraction
578                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
579                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
580                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
581                 true, // doesDeviceSupportEarpiece
582                 shouldRunWithFocus
583         ));
584 
585         params.add(new RoutingTestParameters(
586                 "Disconnect bluetooth during speakerphone", // name
587                 CallAudioState.ROUTE_SPEAKER, // initialRoute
588                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
589                 NONE, // speakerInteraction
590                 NONE, // bluetoothInteraction
591                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
592                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
593                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
594                 true, // doesDeviceSupportEarpiece
595                 shouldRunWithFocus
596         ));
597 
598         params.add(new RoutingTestParameters(
599                 "Disconnect bluetooth during earpiece", // name
600                 CallAudioState.ROUTE_EARPIECE, // initialRoute
601                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
602                 NONE, // speakerInteraction
603                 NONE, // bluetoothInteraction
604                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
605                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
606                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
607                 true, // doesDeviceSupportEarpiece
608                 shouldRunWithFocus
609         ));
610 
611         params.add(new RoutingTestParameters(
612                 "Switch to speakerphone from earpiece", // name
613                 CallAudioState.ROUTE_EARPIECE, // initialRoute
614                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
615                 ON, // speakerInteraction
616                 NONE, // bluetoothInteraction
617                 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
618                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
619                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
620                 true, // doesDeviceSupportEarpiece
621                 shouldRunWithFocus
622         ));
623 
624         params.add(new RoutingTestParameters(
625                 "Switch to speakerphone from headset", // name
626                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
627                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
628                 ON, // speakerInteraction
629                 NONE, // bluetoothInteraction
630                 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
631                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
632                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
633                 true, // doesDeviceSupportEarpiece
634                 shouldRunWithFocus
635         ));
636 
637         params.add(new RoutingTestParameters(
638                 "Switch to speakerphone from bluetooth", // name
639                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
640                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
641                 ON, // speakerInteraction
642                 OFF, // bluetoothInteraction
643                 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
644                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
645                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
646                 true, // doesDeviceSupportEarpiece
647                 shouldRunWithFocus
648         ));
649 
650         params.add(new RoutingTestParameters(
651                 "Switch to earpiece from bluetooth", // name
652                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
653                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
654                 NONE, // speakerInteraction
655                 OFF, // bluetoothInteraction
656                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
657                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
658                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
659                 true, // doesDeviceSupportEarpiece
660                 shouldRunWithFocus
661         ));
662 
663         params.add(new RoutingTestParameters(
664                 "Switch to earpiece from speakerphone", // name
665                 CallAudioState.ROUTE_SPEAKER, // initialRoute
666                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
667                 OFF, // speakerInteraction
668                 NONE, // bluetoothInteraction
669                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
670                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
671                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
672                 true, // doesDeviceSupportEarpiece
673                 shouldRunWithFocus
674         ));
675 
676         params.add(new RoutingTestParameters(
677                 "Switch to earpiece from speakerphone, priority notifications", // name
678                 CallAudioState.ROUTE_SPEAKER, // initialRoute
679                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
680                 OFF, // speakerInteraction
681                 NONE, // bluetoothInteraction
682                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
683                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
684                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
685                 true, // doesDeviceSupportEarpiece
686                 shouldRunWithFocus
687         ));
688 
689         params.add(new RoutingTestParameters(
690                 "Switch to earpiece from speakerphone, silent mode", // name
691                 CallAudioState.ROUTE_SPEAKER, // initialRoute
692                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
693                 OFF, // speakerInteraction
694                 NONE, // bluetoothInteraction
695                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
696                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
697                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
698                 true, // doesDeviceSupportEarpiece
699                 shouldRunWithFocus
700         ));
701 
702         params.add(new RoutingTestParameters(
703                 "Switch to bluetooth from speakerphone", // name
704                 CallAudioState.ROUTE_SPEAKER, // initialRoute
705                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
706                 OFF, // speakerInteraction
707                 ON, // bluetoothInteraction
708                 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
709                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
710                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
711                 true, // doesDeviceSupportEarpiece
712                 shouldRunWithFocus
713         ));
714 
715         params.add(new RoutingTestParameters(
716                 "Switch to bluetooth from earpiece", // name
717                 CallAudioState.ROUTE_EARPIECE, // initialRoute
718                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
719                 NONE, // speakerInteraction
720                 ON, // bluetoothInteraction
721                 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
722                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
723                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
724                 true, // doesDeviceSupportEarpiece
725                 shouldRunWithFocus
726         ));
727 
728         params.add(new RoutingTestParameters(
729                 "Switch to bluetooth from wired headset", // name
730                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
731                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
732                 NONE, // speakerInteraction
733                 ON, // bluetoothInteraction
734                 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
735                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
736                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
737                 true, // doesDeviceSupportEarpiece
738                 shouldRunWithFocus
739         ));
740 
741         params.add(new RoutingTestParameters(
742                 "Switch from bluetooth to wired/earpiece when neither are available", // name
743                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
744                 CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
745                 ON, // speakerInteraction
746                 OFF, // bluetoothInteraction
747                 CallAudioRouteStateMachine.SWITCH_BASELINE_ROUTE, // action
748                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
749                 CallAudioState.ROUTE_BLUETOOTH, // expectedAvailableRoutes
750                 false, // doesDeviceSupportEarpiece
751                 shouldRunWithFocus
752         ));
753 
754         params.add(new RoutingTestParameters(
755                 "Disconnect wired headset when device does not support earpiece", // name
756                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
757                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
758                 ON, // speakerInteraction
759                 NONE, // bluetoothInteraction
760                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
761                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
762                 CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
763                 false, // doesDeviceSupportEarpiece
764                 shouldRunWithFocus
765         ));
766 
767         params.add(new RoutingTestParameters(
768                 "Disconnect wired headset when call doesn't support earpiece", // name
769                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
770                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
771                 ON, // speakerInteraction
772                 NONE, // bluetoothInteraction
773                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
774                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
775                 CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
776                 true, // doesDeviceSupportEarpiece
777                 shouldRunWithFocus
778         ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
779 
780         params.add(new RoutingTestParameters(
781                 "Disconnect bluetooth when call does not support earpiece", // name
782                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
783                 CallAudioState.ROUTE_BLUETOOTH,  // availableRoutes
784                 ON, // speakerInteraction
785                 OFF, // bluetoothInteraction
786                 CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
787                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
788                 CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
789                 true, // doesDeviceSupportEarpiece
790                 shouldRunWithFocus
791         ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
792 
793         return params;
794     }
795 
796     @Override
runParametrizedTestCase(TestParameters _params)797     protected void runParametrizedTestCase(TestParameters _params) throws Throwable {
798         RoutingTestParameters params = (RoutingTestParameters) _params;
799         if (params.shouldRunWithFocus) {
800             runParametrizedTestCaseWithFocus(params);
801         } else {
802             runParametrizedTestCaseWithoutFocus(params);
803         }
804     }
805 
runParametrizedTestCaseWithFocus(final RoutingTestParameters params)806     private void runParametrizedTestCaseWithFocus(final RoutingTestParameters params)
807             throws Throwable {
808         resetMocks(true);
809 
810         // Construct a fresh state machine on every case
811         final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
812                 mContext,
813                 mockCallsManager,
814                 mockBluetoothRouteManager,
815                 mockWiredHeadsetManager,
816                 mockStatusBarNotifier,
817                 mAudioServiceFactory,
818                 params.doesDeviceSupportEarpiece);
819 
820         setupMocksForParams(params);
821 
822         // Set the initial CallAudioState object
823         final CallAudioState initState = new CallAudioState(false,
824                 params.initialRoute, (params.availableRoutes | CallAudioState.ROUTE_SPEAKER));
825         stateMachine.initialize(initState);
826 
827         // Make the state machine have focus so that we actually do something
828         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
829                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
830         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
831 
832         // Reset mocks one more time to discard stuff from initialization
833         resetMocks(false);
834         setupMocksForParams(params);
835         stateMachine.sendMessageWithSessionInfo(params.action);
836 
837         waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
838 
839         stateMachine.quitStateMachine();
840 
841         // Verify interactions with the speakerphone and bluetooth systems
842         switch (params.bluetoothInteraction) {
843             case NONE:
844                 verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
845                 verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
846                 break;
847             case ON:
848                 verify(mockBluetoothRouteManager).connectBluetoothAudio(null);
849 
850                 verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
851                 break;
852             case OFF:
853                 verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
854                 verify(mockBluetoothRouteManager).disconnectBluetoothAudio();
855         }
856 
857         switch (params.speakerInteraction) {
858             case NONE:
859                 verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
860                 break;
861             case ON: // fall through
862             case OFF:
863                 verify(mockAudioManager).setSpeakerphoneOn(params.speakerInteraction == ON);
864         }
865 
866         // Verify the end state
867         CallAudioState expectedState = new CallAudioState(false, params.expectedRoute,
868                 params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER);
869         verifyNewSystemCallAudioState(initState, expectedState);
870     }
871 
setupMocksForParams(RoutingTestParameters params)872     private void setupMocksForParams(RoutingTestParameters params) {
873         // Set up bluetooth and speakerphone state
874         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(
875                 params.initialRoute == CallAudioState.ROUTE_BLUETOOTH);
876         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
877                 (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
878                         || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
879         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
880                 params.initialRoute == CallAudioState.ROUTE_SPEAKER);
881         when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
882     }
883 
runParametrizedTestCaseWithoutFocus(final RoutingTestParameters params)884     private void runParametrizedTestCaseWithoutFocus(final RoutingTestParameters params)
885             throws Throwable {
886         resetMocks(true);
887 
888         // Construct a fresh state machine on every case
889         final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
890                 mContext,
891                 mockCallsManager,
892                 mockBluetoothRouteManager,
893                 mockWiredHeadsetManager,
894                 mockStatusBarNotifier,
895                 mAudioServiceFactory,
896                 params.doesDeviceSupportEarpiece);
897 
898         // Set up bluetooth and speakerphone state
899         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
900                 (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
901                 || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
902         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
903                 params.initialRoute == CallAudioState.ROUTE_SPEAKER);
904         when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
905 
906         // Set the initial CallAudioState object
907         CallAudioState initState = new CallAudioState(false,
908                 params.initialRoute, (params.availableRoutes | CallAudioState.ROUTE_SPEAKER));
909         stateMachine.initialize(initState);
910         // Omit the focus-getting statement
911         stateMachine.sendMessageWithSessionInfo(params.action);
912 
913         waitForStateMachineActionCompletion(stateMachine, CallAudioModeStateMachine.RUN_RUNNABLE);
914 
915         stateMachine.quitStateMachine();
916 
917         // Verify that no substantive interactions have taken place with the
918         // rest of the system
919         verifyNoSystemAudioChanges();
920 
921         // Verify the end state
922         CallAudioState expectedState = new CallAudioState(false, params.expectedRoute,
923                 params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER);
924         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
925     }
926 
verifyNoSystemAudioChanges()927     private void verifyNoSystemAudioChanges() {
928         verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
929         verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
930         verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
931         verify(mockCallsManager, never()).onCallAudioStateChanged(any(CallAudioState.class),
932                 any(CallAudioState.class));
933         verify(mockConnectionServiceWrapper, never()).onCallAudioStateChanged(
934                 any(Call.class), any(CallAudioState.class));
935     }
936 
verifyNewSystemCallAudioState(CallAudioState expectedOldState, CallAudioState expectedNewState)937     private void verifyNewSystemCallAudioState(CallAudioState expectedOldState,
938             CallAudioState expectedNewState) {
939         ArgumentCaptor<CallAudioState> oldStateCaptor = ArgumentCaptor.forClass(
940                 CallAudioState.class);
941         ArgumentCaptor<CallAudioState> newStateCaptor1 = ArgumentCaptor.forClass(
942                 CallAudioState.class);
943         ArgumentCaptor<CallAudioState> newStateCaptor2 = ArgumentCaptor.forClass(
944                 CallAudioState.class);
945         verify(mockCallsManager, timeout(TEST_TIMEOUT).atLeastOnce()).onCallAudioStateChanged(
946                 oldStateCaptor.capture(), newStateCaptor1.capture());
947         verify(mockConnectionServiceWrapper, timeout(TEST_TIMEOUT).atLeastOnce())
948                 .onCallAudioStateChanged(same(fakeCall), newStateCaptor2.capture());
949 
950         assertTrue(oldStateCaptor.getValue().equals(expectedOldState));
951         assertTrue(newStateCaptor1.getValue().equals(expectedNewState));
952         assertTrue(newStateCaptor2.getValue().equals(expectedNewState));
953     }
954 
resetMocks(boolean resetNotificationFilter)955     private void resetMocks(boolean resetNotificationFilter) {
956         reset(mockAudioManager, mockBluetoothRouteManager, mockCallsManager,
957                 mockConnectionServiceWrapper, fakeCall);
958         when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
959         when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
960         when(fakeCall.isAlive()).thenReturn(true);
961         when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL);
962         when(mockCallsManager.getLock()).thenReturn(mLock);
963         doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
964                 any(CallAudioState.class));
965     }
966 }
967