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