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