| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.server.telecom.tests; |
| |
| import android.bluetooth.BluetoothDevice; |
| import android.content.Context; |
| import android.media.AudioManager; |
| import android.media.IAudioService; |
| import android.os.Handler; |
| import android.telecom.CallAudioState; |
| import android.test.suitebuilder.annotation.SmallTest; |
| |
| import com.android.server.telecom.Call; |
| import com.android.server.telecom.CallAudioManager; |
| import com.android.server.telecom.CallAudioModeStateMachine; |
| import com.android.server.telecom.CallAudioRouteStateMachine; |
| import com.android.server.telecom.CallsManager; |
| import com.android.server.telecom.ConnectionServiceWrapper; |
| import com.android.server.telecom.StatusBarNotifier; |
| import com.android.server.telecom.TelecomSystem; |
| import com.android.server.telecom.WiredHeadsetManager; |
| import com.android.server.telecom.bluetooth.BluetoothRouteManager; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.concurrent.CountDownLatch; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.nullable; |
| import static org.mockito.ArgumentMatchers.same; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.reset; |
| import static org.mockito.Mockito.timeout; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| @RunWith(Parameterized.class) |
| public class CallAudioRouteTransitionTests extends TelecomTestCase { |
| private static final int NONE = 0; |
| private static final int ON = 1; |
| private static final int OFF = 2; |
| private static final int OPTIONAL = 3; |
| |
| static class RoutingTestParameters { |
| public String name; |
| public int initialRoute; |
| public BluetoothDevice initialBluetoothDevice = null; |
| public int availableRoutes; // may excl. speakerphone, because that's always available |
| public List<BluetoothDevice> availableBluetoothDevices = Collections.emptyList(); |
| public int speakerInteraction; // one of NONE, ON, or OFF |
| public int bluetoothInteraction; // one of NONE, ON, or OFF |
| public int action; |
| public int expectedRoute; |
| public BluetoothDevice expectedBluetoothDevice = null; |
| public int expectedAvailableRoutes; // also may exclude the speakerphone. |
| public int earpieceControl; // Allows disabling the earpiece to simulate Wear or Car |
| |
| public int callSupportedRoutes = CallAudioState.ROUTE_ALL; |
| |
| public RoutingTestParameters(String name, int initialRoute, |
| int availableRoutes, int speakerInteraction, |
| int bluetoothInteraction, int action, int expectedRoute, |
| int expectedAvailableRoutes, int earpieceControl) { |
| this.name = name; |
| this.initialRoute = initialRoute; |
| this.availableRoutes = availableRoutes; |
| this.speakerInteraction = speakerInteraction; |
| this.bluetoothInteraction = bluetoothInteraction; |
| this.action = action; |
| this.expectedRoute = expectedRoute; |
| this.expectedAvailableRoutes = expectedAvailableRoutes; |
| this.earpieceControl = earpieceControl; |
| } |
| |
| public RoutingTestParameters setCallSupportedRoutes(int routes) { |
| callSupportedRoutes = routes; |
| return this; |
| } |
| |
| public RoutingTestParameters setInitialBluetoothDevice(BluetoothDevice device) { |
| initialBluetoothDevice = device; |
| return this; |
| } |
| |
| public RoutingTestParameters setAvailableBluetoothDevices(BluetoothDevice... devices) { |
| availableBluetoothDevices = Arrays.asList(devices); |
| return this; |
| } |
| |
| public RoutingTestParameters setExpectedBluetoothDevice(BluetoothDevice device) { |
| expectedBluetoothDevice = device; |
| return this; |
| } |
| |
| @Override |
| public String toString() { |
| return "RoutingTestParameters{" + |
| "name='" + name + '\'' + |
| ", initialRoute=" + initialRoute + |
| ", availableRoutes=" + availableRoutes + |
| ", speakerInteraction=" + speakerInteraction + |
| ", bluetoothInteraction=" + bluetoothInteraction + |
| ", action=" + action + |
| ", expectedRoute=" + expectedRoute + |
| ", expectedAvailableRoutes=" + expectedAvailableRoutes + |
| ", earpieceControl=" + earpieceControl + |
| '}'; |
| } |
| } |
| |
| private final RoutingTestParameters mParams; |
| @Mock CallsManager mockCallsManager; |
| @Mock BluetoothRouteManager mockBluetoothRouteManager; |
| @Mock IAudioService mockAudioService; |
| @Mock ConnectionServiceWrapper mockConnectionServiceWrapper; |
| @Mock WiredHeadsetManager mockWiredHeadsetManager; |
| @Mock StatusBarNotifier mockStatusBarNotifier; |
| @Mock Call fakeCall; |
| private CallAudioManager.AudioServiceFactory mAudioServiceFactory; |
| private static final int TEST_TIMEOUT = 500; |
| private AudioManager mockAudioManager; |
| private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { }; |
| |
| public CallAudioRouteTransitionTests(RoutingTestParameters params) { |
| mParams = params; |
| } |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| MockitoAnnotations.initMocks(this); |
| mContext = mComponentContextFixture.getTestDouble().getApplicationContext(); |
| mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); |
| |
| mAudioServiceFactory = new CallAudioManager.AudioServiceFactory() { |
| @Override |
| public IAudioService getAudioService() { |
| return mockAudioService; |
| } |
| }; |
| |
| when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall); |
| when(mockCallsManager.getLock()).thenReturn(mLock); |
| when(mockCallsManager.hasVideoCall()).thenReturn(false); |
| when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper); |
| when(fakeCall.isAlive()).thenReturn(true); |
| when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL); |
| |
| doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class), |
| any(CallAudioState.class)); |
| } |
| |
| private void setupMocksForParams(final CallAudioRouteStateMachine sm, |
| RoutingTestParameters params) { |
| // Set up bluetooth and speakerphone state |
| when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn( |
| params.initialRoute == CallAudioState.ROUTE_BLUETOOTH); |
| when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn( |
| (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0 |
| || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0); |
| when(mockBluetoothRouteManager.getConnectedDevices()) |
| .thenReturn(params.availableBluetoothDevices); |
| if (params.initialBluetoothDevice != null) { |
| when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice()) |
| .thenReturn(params.initialBluetoothDevice); |
| } |
| |
| |
| doAnswer(invocation -> { |
| sm.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); |
| return null; |
| }).when(mockBluetoothRouteManager).connectBluetoothAudio(nullable(String.class)); |
| |
| when(mockAudioManager.isSpeakerphoneOn()).thenReturn( |
| params.initialRoute == CallAudioState.ROUTE_SPEAKER); |
| when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes); |
| } |
| |
| @Test |
| @SmallTest |
| public void testActiveTransition() { |
| final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( |
| mContext, |
| mockCallsManager, |
| mockBluetoothRouteManager, |
| mockWiredHeadsetManager, |
| mockStatusBarNotifier, |
| mAudioServiceFactory, |
| mParams.earpieceControl); |
| |
| setupMocksForParams(stateMachine, mParams); |
| |
| // Set the initial CallAudioState object |
| final CallAudioState initState = new CallAudioState(false, |
| mParams.initialRoute, (mParams.availableRoutes | CallAudioState.ROUTE_SPEAKER)); |
| stateMachine.initialize(initState); |
| |
| // Make the state machine have focus so that we actually do something |
| stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, |
| CallAudioRouteStateMachine.ACTIVE_FOCUS); |
| // Tell the state machine that BT is on, if that's what the mParams say. |
| if (mParams.initialRoute == CallAudioState.ROUTE_BLUETOOTH) { |
| stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); |
| } |
| waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); |
| |
| // Reset mocks to discard stuff from initialization |
| resetMocks(); |
| setupMocksForParams(stateMachine, mParams); |
| stateMachine.sendMessageWithSessionInfo(mParams.action); |
| |
| waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); |
| waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); |
| |
| Handler h = stateMachine.getHandler(); |
| waitForHandlerAction(h, TEST_TIMEOUT); |
| stateMachine.quitStateMachine(); |
| |
| // Verify interactions with the speakerphone and bluetooth systems |
| switch (mParams.bluetoothInteraction) { |
| case NONE: |
| verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio(); |
| verify(mockBluetoothRouteManager, never()) |
| .connectBluetoothAudio(nullable(String.class)); |
| break; |
| case ON: |
| if (mParams.expectedBluetoothDevice == null) { |
| verify(mockBluetoothRouteManager).connectBluetoothAudio(null); |
| } else { |
| verify(mockBluetoothRouteManager).connectBluetoothAudio( |
| mParams.expectedBluetoothDevice.getAddress()); |
| } |
| verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio(); |
| break; |
| case OFF: |
| verify(mockBluetoothRouteManager, never()) |
| .connectBluetoothAudio(nullable(String.class)); |
| verify(mockBluetoothRouteManager).disconnectBluetoothAudio(); |
| break; |
| case OPTIONAL: |
| // optional, don't test |
| break; |
| } |
| |
| switch (mParams.speakerInteraction) { |
| case NONE: |
| verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class)); |
| break; |
| case ON: // fall through |
| case OFF: |
| verify(mockAudioManager).setSpeakerphoneOn(mParams.speakerInteraction == ON); |
| break; |
| case OPTIONAL: |
| // optional, don't test |
| break; |
| } |
| |
| // Verify the end state |
| CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute, |
| mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER); |
| verifyNewSystemCallAudioState(initState, expectedState); |
| } |
| |
| @Test |
| @SmallTest |
| public void testQuiescentTransition() { |
| final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( |
| mContext, |
| mockCallsManager, |
| mockBluetoothRouteManager, |
| mockWiredHeadsetManager, |
| mockStatusBarNotifier, |
| mAudioServiceFactory, |
| mParams.earpieceControl); |
| |
| // Set up bluetooth and speakerphone state |
| when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn( |
| (mParams.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0 |
| || (mParams.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0); |
| when(mockAudioManager.isSpeakerphoneOn()).thenReturn( |
| mParams.initialRoute == CallAudioState.ROUTE_SPEAKER); |
| when(fakeCall.getSupportedAudioRoutes()).thenReturn(mParams.callSupportedRoutes); |
| |
| // Set the initial CallAudioState object |
| CallAudioState initState = new CallAudioState(false, |
| mParams.initialRoute, (mParams.availableRoutes | CallAudioState.ROUTE_SPEAKER)); |
| stateMachine.initialize(initState); |
| // Omit the focus-getting statement |
| stateMachine.sendMessageWithSessionInfo(mParams.action); |
| |
| waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); |
| |
| Handler h = stateMachine.getHandler(); |
| waitForHandlerAction(h, TEST_TIMEOUT); |
| stateMachine.quitStateMachine(); |
| |
| // Verify that no substantive interactions have taken place with the |
| // rest of the system |
| verifyNoSystemAudioChanges(); |
| |
| // Verify the end state |
| CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute, |
| mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER); |
| assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); |
| } |
| |
| @Parameterized.Parameters(name = "{0}") |
| public static Collection<RoutingTestParameters> testParametersCollection() { |
| List<RoutingTestParameters> params = new ArrayList<>(); |
| params.add(new RoutingTestParameters( |
| "Connect headset during earpiece", // name |
| CallAudioState.ROUTE_EARPIECE, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Connect headset during bluetooth", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Connect headset during speakerphone", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OFF, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect headset during headset", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect headset during headset with bluetooth available", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| OPTIONAL, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect headset during bluetooth", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect headset during speakerphone", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect headset during speakerphone with bluetooth available", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Connect bluetooth during earpiece", // name |
| CallAudioState.ROUTE_EARPIECE, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Connect bluetooth during wired headset", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvai |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Connect bluetooth during speakerphone", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OFF, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect bluetooth during bluetooth without headset in", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect bluetooth during bluetooth without headset in, priority mode ", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect bluetooth during bluetooth with headset in", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| OPTIONAL, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect bluetooth during speakerphone", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect bluetooth during earpiece", // name |
| CallAudioState.ROUTE_EARPIECE, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to speakerphone from earpiece", // name |
| CallAudioState.ROUTE_EARPIECE, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| ON, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_SPEAKER, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to speakerphone from headset", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes |
| ON, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_SPEAKER, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to speakerphone from bluetooth", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| ON, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_SPEAKER, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to earpiece from bluetooth", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_EARPIECE, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to earpiece from speakerphone", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OFF, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_EARPIECE, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to earpiece from speakerphone, priority notifications", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OFF, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_EARPIECE, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to earpiece from speakerphone, silent mode", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_EARPIECE, // availableRoutes |
| OFF, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_EARPIECE, // action |
| CallAudioState.ROUTE_EARPIECE, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to bluetooth from speakerphone", // name |
| CallAudioState.ROUTE_SPEAKER, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OFF, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to bluetooth from earpiece", // name |
| CallAudioState.ROUTE_EARPIECE, // initialRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| OPTIONAL, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch to bluetooth from wired headset", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou |
| OPTIONAL, // speakerInteraction |
| ON, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action |
| CallAudioState.ROUTE_BLUETOOTH, // expectedRoute |
| CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Switch from bluetooth to wired/earpiece when neither are available", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| ON, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.SWITCH_BASELINE_ROUTE, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_BLUETOOTH, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect wired headset when device does not support earpiece", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes |
| ON, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED // earpieceControl |
| )); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect wired headset when call doesn't support earpiece", // name |
| CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute |
| CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes |
| ON, // speakerInteraction |
| NONE, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE)); |
| |
| params.add(new RoutingTestParameters( |
| "Disconnect bluetooth when call does not support earpiece", // name |
| CallAudioState.ROUTE_BLUETOOTH, // initialRoute |
| CallAudioState.ROUTE_BLUETOOTH, // availableRoutes |
| ON, // speakerInteraction |
| OFF, // bluetoothInteraction |
| CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action |
| CallAudioState.ROUTE_SPEAKER, // expectedRoute |
| CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes |
| CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl |
| ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE)); |
| |
| return params; |
| } |
| |
| private void verifyNewSystemCallAudioState(CallAudioState expectedOldState, |
| CallAudioState expectedNewState) { |
| ArgumentCaptor<CallAudioState> oldStateCaptor = ArgumentCaptor.forClass( |
| CallAudioState.class); |
| ArgumentCaptor<CallAudioState> newStateCaptor1 = ArgumentCaptor.forClass( |
| CallAudioState.class); |
| ArgumentCaptor<CallAudioState> newStateCaptor2 = ArgumentCaptor.forClass( |
| CallAudioState.class); |
| verify(mockCallsManager, timeout(TEST_TIMEOUT).atLeastOnce()).onCallAudioStateChanged( |
| oldStateCaptor.capture(), newStateCaptor1.capture()); |
| verify(mockConnectionServiceWrapper, timeout(TEST_TIMEOUT).atLeastOnce()) |
| .onCallAudioStateChanged(same(fakeCall), newStateCaptor2.capture()); |
| |
| assertTrue(oldStateCaptor.getAllValues().get(0).equals(expectedOldState)); |
| assertTrue(newStateCaptor1.getValue().equals(expectedNewState)); |
| assertTrue(newStateCaptor2.getValue().equals(expectedNewState)); |
| } |
| |
| private void verifyNoSystemAudioChanges() { |
| verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio(); |
| verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class)); |
| verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class)); |
| verify(mockCallsManager, never()).onCallAudioStateChanged(any(CallAudioState.class), |
| any(CallAudioState.class)); |
| verify(mockConnectionServiceWrapper, never()).onCallAudioStateChanged( |
| any(Call.class), any(CallAudioState.class)); |
| } |
| |
| private void resetMocks() { |
| reset(mockAudioManager, mockBluetoothRouteManager, mockCallsManager, |
| mockConnectionServiceWrapper); |
| fakeCall = mock(Call.class); |
| when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall); |
| when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper); |
| when(fakeCall.isAlive()).thenReturn(true); |
| when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL); |
| when(mockCallsManager.getLock()).thenReturn(mLock); |
| doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class), |
| any(CallAudioState.class)); |
| } |
| } |