1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.telecom.tests;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.nullable;
22 import static org.mockito.ArgumentMatchers.same;
23 import static org.mockito.Mockito.atLeastOnce;
24 import static org.mockito.Mockito.doAnswer;
25 import static org.mockito.Mockito.doNothing;
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.timeout;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.bluetooth.BluetoothDevice;
33 import android.content.Context;
34 import android.media.AudioDeviceInfo;
35 import android.media.AudioManager;
36 import android.media.IAudioService;
37 import android.os.Handler;
38 import android.os.HandlerThread;
39 import android.telecom.CallAudioState;
40 
41 import androidx.test.filters.SmallTest;
42 
43 import com.android.server.telecom.Call;
44 import com.android.server.telecom.CallAudioCommunicationDeviceTracker;
45 import com.android.server.telecom.CallAudioManager;
46 import com.android.server.telecom.CallAudioRouteStateMachine;
47 import com.android.server.telecom.CallsManager;
48 import com.android.server.telecom.ConnectionServiceWrapper;
49 import com.android.server.telecom.StatusBarNotifier;
50 import com.android.server.telecom.TelecomSystem;
51 import com.android.server.telecom.WiredHeadsetManager;
52 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
53 
54 import org.junit.After;
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 import org.junit.runners.Parameterized;
59 import org.mockito.ArgumentCaptor;
60 import org.mockito.Mock;
61 import org.mockito.Mockito;
62 import org.mockito.MockitoAnnotations;
63 
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.List;
69 import java.util.Set;
70 
71 @RunWith(Parameterized.class)
72 public class CallAudioRouteTransitionTests extends TelecomTestCase {
73     private static final int NONE = 0;
74     private static final int ON = 1;
75     private static final int OFF = 2;
76     private static final int OPTIONAL = 3;
77 
78     // This is used to simulate the first bluetooth device getting connected --
79     // it requires two messages: BT device list changed and active device present
80     private static final int SPECIAL_CONNECT_BT_ACTION = 998;
81     // Same, but for disconnection
82     private static final int SPECIAL_DISCONNECT_BT_ACTION = 999;
83 
84     static class RoutingTestParameters {
85         public String name;
86         public int initialRoute;
87         public BluetoothDevice initialBluetoothDevice = null;
88         public int availableRoutes; // may excl. speakerphone, because that's always available
89         public List<BluetoothDevice> availableBluetoothDevices = Collections.emptyList();
90         public int speakerInteraction; // one of NONE, ON, or OFF
91         public int bluetoothInteraction; // one of NONE, ON, or OFF
92         public int action;
93         public int expectedRoute;
94         public BluetoothDevice expectedBluetoothDevice = null;
95         public int expectedAvailableRoutes; // also may exclude the speakerphone.
96         public int earpieceControl; // Allows disabling the earpiece to simulate Wear or Car
97 
98         public int callSupportedRoutes = CallAudioState.ROUTE_ALL;
99 
RoutingTestParameters(String name, int initialRoute, int availableRoutes, int speakerInteraction, int bluetoothInteraction, int action, int expectedRoute, int expectedAvailableRoutes, int earpieceControl)100         public RoutingTestParameters(String name, int initialRoute,
101                 int availableRoutes, int speakerInteraction,
102                 int bluetoothInteraction, int action, int expectedRoute,
103                 int expectedAvailableRoutes, int earpieceControl) {
104             this.name = name;
105             this.initialRoute = initialRoute;
106             this.availableRoutes = availableRoutes;
107             this.speakerInteraction = speakerInteraction;
108             this.bluetoothInteraction = bluetoothInteraction;
109             this.action = action;
110             this.expectedRoute = expectedRoute;
111             this.expectedAvailableRoutes = expectedAvailableRoutes;
112             this.earpieceControl = earpieceControl;
113         }
114 
setCallSupportedRoutes(int routes)115         public RoutingTestParameters setCallSupportedRoutes(int routes) {
116             callSupportedRoutes = routes;
117             return this;
118         }
119 
setInitialBluetoothDevice(BluetoothDevice device)120         public RoutingTestParameters setInitialBluetoothDevice(BluetoothDevice device) {
121             initialBluetoothDevice = device;
122             return this;
123         }
124 
setAvailableBluetoothDevices(BluetoothDevice... devices)125         public RoutingTestParameters setAvailableBluetoothDevices(BluetoothDevice... devices) {
126             availableBluetoothDevices = Arrays.asList(devices);
127             return this;
128         }
129 
setExpectedBluetoothDevice(BluetoothDevice device)130         public RoutingTestParameters setExpectedBluetoothDevice(BluetoothDevice device) {
131             expectedBluetoothDevice = device;
132             return this;
133         }
134 
135         @Override
toString()136         public String toString() {
137             return "RoutingTestParameters{" +
138                     "name='" + name + '\'' +
139                     ", initialRoute=" + initialRoute +
140                     ", availableRoutes=" + availableRoutes +
141                     ", speakerInteraction=" + speakerInteraction +
142                     ", bluetoothInteraction=" + bluetoothInteraction +
143                     ", action=" + action +
144                     ", expectedRoute=" + expectedRoute +
145                     ", expectedAvailableRoutes=" + expectedAvailableRoutes +
146                     ", earpieceControl=" + earpieceControl +
147                     '}';
148         }
149     }
150 
151     private final RoutingTestParameters mParams;
152     @Mock CallsManager mockCallsManager;
153     @Mock BluetoothRouteManager mockBluetoothRouteManager;
154     @Mock IAudioService mockAudioService;
155     @Mock ConnectionServiceWrapper mockConnectionServiceWrapper;
156     @Mock WiredHeadsetManager mockWiredHeadsetManager;
157     @Mock StatusBarNotifier mockStatusBarNotifier;
158     @Mock Call fakeCall;
159     @Mock CallAudioManager mockCallAudioManager;
160     private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
161     private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
162     private static final int TEST_TIMEOUT = 500;
163     private AudioManager mockAudioManager;
164     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
165     private HandlerThread mHandlerThread;
166 
CallAudioRouteTransitionTests(RoutingTestParameters params)167     public CallAudioRouteTransitionTests(RoutingTestParameters params) {
168         mParams = params;
169     }
170 
171     @Override
172     @Before
setUp()173     public void setUp() throws Exception {
174         super.setUp();
175         MockitoAnnotations.initMocks(this);
176         mHandlerThread = new HandlerThread("CallAudioRouteTransitionTests");
177         mHandlerThread.start();
178         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
179         mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
180         mCommunicationDeviceTracker = new CallAudioCommunicationDeviceTracker(mContext);
181         mCommunicationDeviceTracker.setBluetoothRouteManager(mockBluetoothRouteManager);
182 
183         mAudioServiceFactory = new CallAudioManager.AudioServiceFactory() {
184             @Override
185             public IAudioService getAudioService() {
186                 return mockAudioService;
187             }
188         };
189 
190         when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
191         when(mockCallsManager.getTrackedCalls()).thenReturn(Set.of(fakeCall));
192         when(mockCallsManager.getLock()).thenReturn(mLock);
193         when(mockCallsManager.hasVideoCall()).thenReturn(false);
194         when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
195         when(fakeCall.isAlive()).thenReturn(true);
196         when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL);
197 
198         doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
199                 any(CallAudioState.class));
200     }
201 
202     @Override
203     @After
tearDown()204     public void tearDown() throws Exception {
205         mHandlerThread.quit();
206         mHandlerThread.join();
207         super.tearDown();
208     }
209 
setupMocksForParams(final CallAudioRouteStateMachine sm, RoutingTestParameters params)210     private void setupMocksForParams(final CallAudioRouteStateMachine sm,
211             RoutingTestParameters params) {
212         // Set up bluetooth and speakerphone state
213         doReturn(params.initialRoute == CallAudioState.ROUTE_BLUETOOTH)
214                 .when(mockBluetoothRouteManager).isBluetoothAudioConnectedOrPending();
215         doReturn((params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
216                 || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0)
217                 .when(mockBluetoothRouteManager).isBluetoothAvailable();
218         doReturn(params.availableBluetoothDevices)
219                 .when(mockBluetoothRouteManager).getConnectedDevices();
220         if (params.initialBluetoothDevice != null) {
221             doReturn(params.initialBluetoothDevice)
222                     .when(mockBluetoothRouteManager).getBluetoothAudioConnectedDevice();
223         }
224 
225 
226         doAnswer(invocation -> {
227             sm.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
228             return null;
229         }).when(mockBluetoothRouteManager).connectBluetoothAudio(nullable(String.class));
230 
231         // Set the speakerphone state depending on the message being sent. If it's one of the
232         // speakerphone override ones, set accordingly. Otherwise consult the initial route.
233         boolean speakerphoneOn;
234         if (params.action == CallAudioRouteStateMachine.SPEAKER_ON) {
235             speakerphoneOn = true;
236         } else if (params.action == CallAudioRouteStateMachine.SPEAKER_OFF) {
237             speakerphoneOn = false;
238         } else {
239             speakerphoneOn = params.initialRoute == CallAudioState.ROUTE_SPEAKER;
240         }
241         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(speakerphoneOn);
242 
243         when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
244     }
245 
sendActionToStateMachine(CallAudioRouteStateMachine sm)246     private void sendActionToStateMachine(CallAudioRouteStateMachine sm) {
247         switch (mParams.action) {
248             case SPECIAL_CONNECT_BT_ACTION:
249                 sm.sendMessageWithSessionInfo(
250                         CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED);
251                 sm.sendMessageWithSessionInfo(
252                         CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT);
253                 break;
254             case SPECIAL_DISCONNECT_BT_ACTION:
255                 sm.sendMessageWithSessionInfo(
256                         CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED);
257                 sm.sendMessageWithSessionInfo(
258                         CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE);
259                 break;
260             default:
261                 sm.sendMessageWithSessionInfo(mParams.action);
262                 break;
263         }
264     }
265 
266     @Test
267     @SmallTest
testActiveTransition()268     public void testActiveTransition() {
269         final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
270                 mContext,
271                 mockCallsManager,
272                 mockBluetoothRouteManager,
273                 mockWiredHeadsetManager,
274                 mockStatusBarNotifier,
275                 mAudioServiceFactory,
276                 mParams.earpieceControl,
277                 mHandlerThread.getLooper(),
278                 Runnable::run /** do async stuff sync for test purposes */,
279                 mCommunicationDeviceTracker,
280                 mFeatureFlags);
281         stateMachine.setCallAudioManager(mockCallAudioManager);
282 
283         setupMocksForParams(stateMachine, mParams);
284 
285         // Set the initial CallAudioState object
286         final CallAudioState initState = new CallAudioState(false,
287                 mParams.initialRoute, (mParams.availableRoutes | CallAudioState.ROUTE_SPEAKER),
288                 mParams.initialBluetoothDevice, mParams.availableBluetoothDevices);
289         stateMachine.initialize(initState);
290 
291         // Make the state machine have focus so that we actually do something
292         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
293                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
294         // Tell the state machine that BT is on, if that's what the mParams say.
295         if (mParams.initialRoute == CallAudioState.ROUTE_BLUETOOTH) {
296             stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
297         }
298         waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT);
299 
300         // Clear invocations on mocks to discard stuff from initialization
301         clearInvocations();
302 
303         sendActionToStateMachine(stateMachine);
304 
305         waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT);
306         waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT);
307 
308         Handler h = stateMachine.getAdapterHandler();
309         waitForHandlerAction(h, TEST_TIMEOUT);
310         stateMachine.quitStateMachine();
311 
312         // Verify interactions with the speakerphone and bluetooth systems
313         switch (mParams.bluetoothInteraction) {
314             case NONE:
315                 verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
316                 verify(mockBluetoothRouteManager, never())
317                         .connectBluetoothAudio(nullable(String.class));
318                 break;
319             case ON:
320                 if (mParams.expectedBluetoothDevice == null) {
321                     verify(mockBluetoothRouteManager, atLeastOnce()).connectBluetoothAudio(null);
322                 } else {
323                     verify(mockBluetoothRouteManager).connectBluetoothAudio(
324                             mParams.expectedBluetoothDevice.getAddress());
325                 }
326                 verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
327                 break;
328             case OFF:
329                 verify(mockBluetoothRouteManager, never())
330                         .connectBluetoothAudio(nullable(String.class));
331                 verify(mockBluetoothRouteManager).disconnectBluetoothAudio();
332                 break;
333             case OPTIONAL:
334                 // optional, don't test
335                 break;
336         }
337 
338         switch (mParams.speakerInteraction) {
339             case NONE:
340                 verify(mockAudioManager, never()).setCommunicationDevice(
341                         any(AudioDeviceInfo.class));
342                 break;
343             case ON:
344                 ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass(
345                         AudioDeviceInfo.class);
346                 verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture());
347                 assertEquals(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
348                         infoArgumentCaptor.getValue().getType());
349                 break;
350             case OFF:
351                 verify(mockAudioManager).clearCommunicationDevice();
352                 break;
353             case OPTIONAL:
354                 // optional, don't test
355                 break;
356         }
357 
358         // Verify the end state
359         CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute,
360                 mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
361                 mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
362         verifyNewSystemCallAudioState(initState, expectedState);
363     }
364 
365     @Test
366     @SmallTest
testQuiescentTransition()367     public void testQuiescentTransition() {
368         final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
369                 mContext,
370                 mockCallsManager,
371                 mockBluetoothRouteManager,
372                 mockWiredHeadsetManager,
373                 mockStatusBarNotifier,
374                 mAudioServiceFactory,
375                 mParams.earpieceControl,
376                 mHandlerThread.getLooper(),
377                 Runnable::run /** do async stuff sync for test purposes */,
378                 mCommunicationDeviceTracker,
379                 mFeatureFlags);
380         stateMachine.setCallAudioManager(mockCallAudioManager);
381 
382         // Set up bluetooth and speakerphone state
383         doReturn((mParams.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0 ||
384                 (mParams.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0)
385                 .when(mockBluetoothRouteManager).isBluetoothAvailable();
386         doReturn(mParams.availableBluetoothDevices)
387                 .when(mockBluetoothRouteManager).getConnectedDevices();
388         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
389                 mParams.initialRoute == CallAudioState.ROUTE_SPEAKER);
390         when(fakeCall.getSupportedAudioRoutes()).thenReturn(mParams.callSupportedRoutes);
391 
392         // Set the initial CallAudioState object
393         CallAudioState initState = new CallAudioState(false,
394                 mParams.initialRoute, (mParams.availableRoutes | CallAudioState.ROUTE_SPEAKER),
395                 mParams.initialBluetoothDevice, mParams.availableBluetoothDevices);
396         stateMachine.initialize(initState);
397         // Omit the focus-getting statement
398         sendActionToStateMachine(stateMachine);
399 
400         waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT);
401         waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT);
402 
403         stateMachine.quitStateMachine();
404 
405         // Verify that no substantive interactions have taken place with the
406         // rest of the system
407         verifyNoSystemAudioChanges();
408 
409         // Special case for SPEAKER_ON -- we don't expect any route transitions to happen when
410         // there are no calls, so set the expected state to the initial route.
411         int expectedRoute = (mParams.action == CallAudioRouteStateMachine.SPEAKER_ON)
412                 ? mParams.initialRoute : mParams.expectedRoute;
413         // Verify the end state
414         CallAudioState expectedState = new CallAudioState(false, expectedRoute,
415                 mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
416                 mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
417         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
418     }
419 
420     @Parameterized.Parameters(name = "{0}")
testParametersCollection()421     public static Collection<RoutingTestParameters> testParametersCollection() {
422         List<RoutingTestParameters> params = new ArrayList<>();
423 
424         params.add(new RoutingTestParameters(
425                 "Connect headset during earpiece", // name
426                 CallAudioState.ROUTE_EARPIECE, // initialRoute
427                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
428                 OPTIONAL, // speakerInteraction
429                 NONE, // bluetoothInteraction
430                 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
431                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
432                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
433                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
434         ));
435 
436         params.add(new RoutingTestParameters(
437                 "Connect headset during bluetooth", // name
438                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
439                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
440                 OPTIONAL, // speakerInteraction
441                 OFF, // bluetoothInteraction
442                 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
443                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
444                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
445                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
446         ));
447 
448         params.add(new RoutingTestParameters(
449                 "Connect headset during speakerphone", // name
450                 CallAudioState.ROUTE_SPEAKER, // initialRoute
451                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
452                 OFF, // speakerInteraction
453                 NONE, // bluetoothInteraction
454                 CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
455                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
456                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
457                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
458         ));
459 
460         params.add(new RoutingTestParameters(
461                 "Disconnect headset during headset", // name
462                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
463                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
464                 OPTIONAL, // speakerInteraction
465                 NONE, // bluetoothInteraction
466                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
467                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
468                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
469                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
470         ));
471 
472         params.add(new RoutingTestParameters(
473                 "Disconnect headset during headset with bluetooth available", // name
474                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
475                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
476                 OPTIONAL, // speakerInteraction
477                 ON, // bluetoothInteraction
478                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
479                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
480                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
481                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
482         ));
483 
484         params.add(new RoutingTestParameters(
485                 "Disconnect headset during bluetooth", // name
486                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
487                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
488                 OPTIONAL, // speakerInteraction
489                 NONE, // bluetoothInteraction
490                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
491                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
492                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
493                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
494         ));
495 
496         params.add(new RoutingTestParameters(
497                 "Disconnect headset during speakerphone", // name
498                 CallAudioState.ROUTE_SPEAKER, // initialRoute
499                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
500                 OPTIONAL, // speakerInteraction
501                 NONE, // bluetoothInteraction
502                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
503                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
504                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
505                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
506         ));
507 
508         params.add(new RoutingTestParameters(
509                 "Disconnect headset during speakerphone with bluetooth available", // name
510                 CallAudioState.ROUTE_SPEAKER, // initialRoute
511                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
512                 OPTIONAL, // speakerInteraction
513                 NONE, // bluetoothInteraction
514                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
515                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
516                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
517                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
518         ));
519 
520         params.add(new RoutingTestParameters(
521                 "Connect bluetooth during earpiece", // name
522                 CallAudioState.ROUTE_EARPIECE, // initialRoute
523                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
524                 OPTIONAL, // speakerInteraction
525                 ON, // bluetoothInteraction
526                 SPECIAL_CONNECT_BT_ACTION, // action
527                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
528                 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
529                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
530         ).setAvailableBluetoothDevices(BluetoothRouteManagerTest.DEVICE1));
531 
532         params.add(new RoutingTestParameters(
533                 "Connect bluetooth during wired headset", // name
534                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
535                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
536                 OPTIONAL, // speakerInteraction
537                 ON, // bluetoothInteraction
538                 SPECIAL_CONNECT_BT_ACTION, // action
539                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
540                 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvai
541                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
542         ).setAvailableBluetoothDevices(BluetoothRouteManagerTest.DEVICE1));
543 
544         params.add(new RoutingTestParameters(
545                 "Connect bluetooth during speakerphone", // name
546                 CallAudioState.ROUTE_SPEAKER, // initialRoute
547                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
548                 OFF, // speakerInteraction
549                 ON, // bluetoothInteraction
550                 SPECIAL_CONNECT_BT_ACTION, // action
551                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
552                 CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
553                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
554         ).setAvailableBluetoothDevices(BluetoothRouteManagerTest.DEVICE1));
555 
556         params.add(new RoutingTestParameters(
557                 "Disconnect bluetooth during bluetooth without headset in", // name
558                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
559                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
560                 OPTIONAL, // speakerInteraction
561                 NONE, // bluetoothInteraction
562                 SPECIAL_DISCONNECT_BT_ACTION, // action
563                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
564                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
565                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
566         ));
567 
568         params.add(new RoutingTestParameters(
569                 "Disconnect bluetooth during bluetooth with headset in", // name
570                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
571                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
572                 OPTIONAL, // speakerInteraction
573                 NONE, // bluetoothInteraction
574                 SPECIAL_DISCONNECT_BT_ACTION, // action
575                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
576                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
577                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
578         ));
579 
580         params.add(new RoutingTestParameters(
581                 "Disconnect bluetooth during speakerphone", // name
582                 CallAudioState.ROUTE_SPEAKER, // initialRoute
583                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
584                 OPTIONAL, // speakerInteraction
585                 NONE, // bluetoothInteraction
586                 SPECIAL_DISCONNECT_BT_ACTION, // action
587                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
588                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
589                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
590         ));
591 
592         params.add(new RoutingTestParameters(
593                 "Disconnect bluetooth during earpiece", // name
594                 CallAudioState.ROUTE_EARPIECE, // initialRoute
595                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
596                 OPTIONAL, // speakerInteraction
597                 NONE, // bluetoothInteraction
598                 SPECIAL_DISCONNECT_BT_ACTION, // action
599                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
600                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
601                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
602         ));
603 
604         params.add(new RoutingTestParameters(
605                 "Switch to speakerphone from earpiece", // name
606                 CallAudioState.ROUTE_EARPIECE, // initialRoute
607                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
608                 ON, // speakerInteraction
609                 NONE, // bluetoothInteraction
610                 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
611                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
612                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
613                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
614         ));
615 
616         params.add(new RoutingTestParameters(
617                 "Switch to speakerphone from headset", // name
618                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
619                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
620                 ON, // speakerInteraction
621                 NONE, // bluetoothInteraction
622                 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
623                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
624                 CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
625                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
626         ));
627 
628         params.add(new RoutingTestParameters(
629                 "Switch to speakerphone from bluetooth", // name
630                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
631                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
632                 ON, // speakerInteraction
633                 OFF, // bluetoothInteraction
634                 CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
635                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
636                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
637                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
638         ));
639 
640         params.add(new RoutingTestParameters(
641                 "Switch to earpiece from bluetooth", // name
642                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
643                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
644                 OPTIONAL, // speakerInteraction
645                 OFF, // bluetoothInteraction
646                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
647                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
648                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
649                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
650         ));
651 
652         params.add(new RoutingTestParameters(
653                 "Switch to earpiece from speakerphone", // name
654                 CallAudioState.ROUTE_SPEAKER, // initialRoute
655                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
656                 OFF, // speakerInteraction
657                 NONE, // bluetoothInteraction
658                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
659                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
660                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
661                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
662         ));
663 
664         params.add(new RoutingTestParameters(
665                 "Switch to earpiece from speakerphone, priority notifications", // name
666                 CallAudioState.ROUTE_SPEAKER, // initialRoute
667                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
668                 OFF, // speakerInteraction
669                 NONE, // bluetoothInteraction
670                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
671                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
672                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
673                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
674         ));
675 
676         params.add(new RoutingTestParameters(
677                 "Switch to earpiece from speakerphone, silent mode", // name
678                 CallAudioState.ROUTE_SPEAKER, // initialRoute
679                 CallAudioState.ROUTE_EARPIECE, // availableRoutes
680                 OFF, // speakerInteraction
681                 NONE, // bluetoothInteraction
682                 CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
683                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
684                 CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
685                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
686         ));
687 
688         params.add(new RoutingTestParameters(
689                 "Switch to bluetooth from speakerphone", // name
690                 CallAudioState.ROUTE_SPEAKER, // initialRoute
691                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
692                 OFF, // speakerInteraction
693                 ON, // bluetoothInteraction
694                 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
695                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
696                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
697                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
698         ));
699 
700         params.add(new RoutingTestParameters(
701                 "Switch to bluetooth from earpiece", // name
702                 CallAudioState.ROUTE_EARPIECE, // initialRoute
703                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
704                 OPTIONAL, // speakerInteraction
705                 ON, // bluetoothInteraction
706                 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
707                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
708                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
709                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
710         ));
711 
712         params.add(new RoutingTestParameters(
713                 "Switch to bluetooth from wired headset", // name
714                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
715                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
716                 OPTIONAL, // speakerInteraction
717                 ON, // bluetoothInteraction
718                 CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
719                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
720                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
721                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
722         ));
723 
724         params.add(new RoutingTestParameters(
725                 "Switch from bluetooth to wired/earpiece when neither are available", // name
726                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
727                 CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
728                 ON, // speakerInteraction
729                 OFF, // bluetoothInteraction
730                 CallAudioRouteStateMachine.SWITCH_BASELINE_ROUTE, // action
731                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
732                 CallAudioState.ROUTE_BLUETOOTH, // expectedAvailableRoutes
733                 CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED // earpieceControl
734         ));
735 
736         params.add(new RoutingTestParameters(
737                 "Disconnect wired headset when device does not support earpiece", // name
738                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
739                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
740                 ON, // speakerInteraction
741                 NONE, // bluetoothInteraction
742                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
743                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
744                 CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
745                 CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED // earpieceControl
746         ));
747 
748         params.add(new RoutingTestParameters(
749                 "Disconnect wired headset when call doesn't support earpiece", // name
750                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
751                 CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
752                 ON, // speakerInteraction
753                 NONE, // bluetoothInteraction
754                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
755                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
756                 CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
757                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
758         ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
759 
760         params.add(new RoutingTestParameters(
761                 "Disconnect bluetooth when call does not support earpiece", // name
762                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
763                 CallAudioState.ROUTE_BLUETOOTH,  // availableRoutes
764                 ON, // speakerInteraction
765                 NONE, // bluetoothInteraction
766                 SPECIAL_DISCONNECT_BT_ACTION, // action
767                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
768                 CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
769                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
770         ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
771 
772         params.add(new RoutingTestParameters(
773                 "Active device deselected during BT", // name
774                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
775                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
776                 OPTIONAL, // speakerInteraction
777                 NONE, // bluetoothInteraction
778                 CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE, // action
779                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
780                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
781                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
782         ));
783 
784         params.add(new RoutingTestParameters(
785                 "Active device selected during earpiece", // name
786                 CallAudioState.ROUTE_EARPIECE, // initialRoute
787                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
788                 OPTIONAL, // speakerInteraction
789                 ON, // bluetoothInteraction
790                 CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT, // action
791                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
792                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
793                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
794         ));
795 
796         params.add(new RoutingTestParameters(
797                 "Speakerphone turned on during earpiece", // name
798                 CallAudioState.ROUTE_EARPIECE, // initialRoute
799                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
800                 NONE, // speakerInteraction
801                 NONE, // bluetoothInteraction
802                 CallAudioRouteStateMachine.SPEAKER_ON, // action
803                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
804                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
805                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
806         ));
807 
808         params.add(new RoutingTestParameters(
809                 "Speakerphone turned on during wired headset", // name
810                 CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
811                 CallAudioState.ROUTE_EARPIECE
812                         | CallAudioState.ROUTE_BLUETOOTH
813                         | CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
814                 NONE, // speakerInteraction
815                 NONE, // bluetoothInteraction
816                 CallAudioRouteStateMachine.SPEAKER_ON, // action
817                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
818                 CallAudioState.ROUTE_EARPIECE
819                         | CallAudioState.ROUTE_BLUETOOTH
820                         | CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
821                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
822         ));
823 
824         params.add(new RoutingTestParameters(
825                 "Speakerphone turned on during bluetooth", // name
826                 CallAudioState.ROUTE_BLUETOOTH, // initialRoute
827                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
828                 NONE, // speakerInteraction
829                 OFF, // bluetoothInteraction
830                 CallAudioRouteStateMachine.SPEAKER_ON, // action
831                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
832                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
833                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
834         ));
835 
836         params.add(new RoutingTestParameters(
837                 "Speakerphone turned off externally during speaker", // name
838                 CallAudioState.ROUTE_SPEAKER, // initialRoute
839                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
840                 OFF, // speakerInteraction
841                 ON, // bluetoothInteraction
842                 CallAudioRouteStateMachine.SPEAKER_OFF, // action
843                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
844                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
845                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
846         ));
847 
848         params.add(new RoutingTestParameters(
849                 "Connect dock from earpiece", // name
850                 CallAudioState.ROUTE_EARPIECE, // initialRoute
851                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, // availableRoutes
852                 ON, // speakerInteraction
853                 NONE, // bluetoothInteraction
854                 CallAudioRouteStateMachine.CONNECT_DOCK, // action
855                 CallAudioState.ROUTE_SPEAKER, // expectedRoute
856                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, // expectedAvailRoutes
857                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
858         ));
859 
860         params.add(new RoutingTestParameters(
861                 "Disconnect dock from speaker", // name
862                 CallAudioState.ROUTE_SPEAKER, // initialRoute
863                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, // availableRoutes
864                 OFF, // speakerInteraction
865                 NONE, // bluetoothInteraction
866                 CallAudioRouteStateMachine.DISCONNECT_DOCK, // action
867                 CallAudioState.ROUTE_EARPIECE, // expectedRoute
868                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER, // expectedAvailRoutes
869                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
870         ));
871 
872         return params;
873     }
874 
verifyNewSystemCallAudioState(CallAudioState expectedOldState, CallAudioState expectedNewState)875     private void verifyNewSystemCallAudioState(CallAudioState expectedOldState,
876             CallAudioState expectedNewState) {
877         ArgumentCaptor<CallAudioState> oldStateCaptor = ArgumentCaptor.forClass(
878                 CallAudioState.class);
879         ArgumentCaptor<CallAudioState> newStateCaptor1 = ArgumentCaptor.forClass(
880                 CallAudioState.class);
881         ArgumentCaptor<CallAudioState> newStateCaptor2 = ArgumentCaptor.forClass(
882                 CallAudioState.class);
883         verify(mockCallsManager, timeout(TEST_TIMEOUT).atLeastOnce()).onCallAudioStateChanged(
884                 oldStateCaptor.capture(), newStateCaptor1.capture());
885         verify(mockConnectionServiceWrapper, timeout(TEST_TIMEOUT).atLeastOnce())
886                 .onCallAudioStateChanged(same(fakeCall), newStateCaptor2.capture());
887 
888         assertEquals(expectedOldState, oldStateCaptor.getAllValues().get(0));
889         assertEquals(expectedNewState, newStateCaptor1.getValue());
890         assertEquals(expectedNewState, newStateCaptor2.getValue());
891     }
892 
verifyNoSystemAudioChanges()893     private void verifyNoSystemAudioChanges() {
894         verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
895         verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class));
896         verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
897         verify(mockCallsManager, never()).onCallAudioStateChanged(any(CallAudioState.class),
898                 any(CallAudioState.class));
899         verify(mockConnectionServiceWrapper, never()).onCallAudioStateChanged(
900                 any(Call.class), any(CallAudioState.class));
901     }
902 
clearInvocations()903     private void clearInvocations() {
904         Mockito.clearInvocations(mockAudioManager, mockBluetoothRouteManager, mockCallsManager,
905                 mockConnectionServiceWrapper);
906     }
907 }