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