diff options
Diffstat (limited to 'tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java')
-rw-r--r-- | tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java | 665 |
1 files changed, 600 insertions, 65 deletions
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java index 4a9b7421f..1fa14a559 100644 --- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java +++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java @@ -16,6 +16,27 @@ package com.android.server.telecom.tests; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.atLeastOnce; +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.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; @@ -29,15 +50,16 @@ import android.telecom.CallAudioState; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; -import com.android.server.telecom.bluetooth.BluetoothRouteManager; import com.android.server.telecom.Call; +import com.android.server.telecom.CallAudioCommunicationDeviceTracker; +import com.android.server.telecom.CallAudioManager; import com.android.server.telecom.CallAudioRouteStateMachine; import com.android.server.telecom.CallsManager; import com.android.server.telecom.ConnectionServiceWrapper; -import com.android.server.telecom.CallAudioManager; 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.After; import org.junit.Before; @@ -50,6 +72,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -57,25 +80,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.same; -import static org.mockito.Mockito.atLeastOnce; -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.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @RunWith(JUnit4.class) public class CallAudioRouteStateMachineTest extends TelecomTestCase { @@ -95,12 +99,14 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { @Mock Call fakeSelfManagedCall; @Mock Call fakeCall; @Mock CallAudioManager mockCallAudioManager; + @Mock BluetoothDevice mockWatchDevice; private CallAudioManager.AudioServiceFactory mAudioServiceFactory; private static final int TEST_TIMEOUT = 500; private AudioManager mockAudioManager; private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { }; private HandlerThread mThreadHandler; + CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker; @Override @Before @@ -111,6 +117,8 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mThreadHandler.start(); mContext = mComponentContextFixture.getTestDouble().getApplicationContext(); mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mCommunicationDeviceTracker = new CallAudioCommunicationDeviceTracker(mContext); + mCommunicationDeviceTracker.setBluetoothRouteManager(mockBluetoothRouteManager); mAudioServiceFactory = new CallAudioManager.AudioServiceFactory() { @Override @@ -130,9 +138,12 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { when(fakeSelfManagedCall.isAlive()).thenReturn(true); when(fakeSelfManagedCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL); when(fakeSelfManagedCall.isSelfManaged()).thenReturn(true); + when(mFeatureFlags.transitRouteBeforeAudioDisconnectBt()).thenReturn(false); doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class), any(CallAudioState.class)); + when(mFeatureFlags.ignoreAutoRouteToWatchDevice()).thenReturn(false); + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(false); } @Override @@ -155,7 +166,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT, mThreadHandler.getLooper(), - Runnable::run /** do async stuff sync for test purposes */); + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); // Since we don't know if we're on a platform with an earpiece or not, all we can do // is ensure the stateMachine construction didn't fail. But at least we exercised the @@ -174,11 +187,63 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + Set<Call> trackedCalls = new HashSet<>(Arrays.asList(fakeCall, fakeSelfManagedCall)); + when(mockCallsManager.getTrackedCalls()).thenReturn(trackedCalls); + + // start state --> ROUTE_EARPIECE + CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); + stateMachine.initialize(initState); + + stateMachine.setCallAudioManager(mockCallAudioManager); + + assertEquals(stateMachine.getCurrentCallAudioState().getRoute(), + CallAudioRouteStateMachine.ROUTE_EARPIECE); + + // ROUTE_EARPIECE --> ROUTE_SPEAKER + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_SPEAKER, + CallAudioRouteStateMachine.SPEAKER_ON); + + stateMachine.sendMessageWithSessionInfo( + CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); + + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // assert expected end state + assertEquals(stateMachine.getCurrentCallAudioState().getRoute(), + CallAudioRouteStateMachine.ROUTE_SPEAKER); + // should update the audio route on all tracked calls ... + verify(mockConnectionServiceWrapper, times(trackedCalls.size())) + .onCallAudioStateChanged(any(), any()); + } + + @SmallTest + @Test + public void testSystemAudioStateIsNotUpdatedFlagOff() { + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); Set<Call> trackedCalls = new HashSet<>(Arrays.asList(fakeCall, fakeSelfManagedCall)); when(mockCallsManager.getTrackedCalls()).thenReturn(trackedCalls); + when(mFeatureFlags.availableRoutesNeverUpdatedAfterSetSystemAudioState()).thenReturn(false); // start state --> ROUTE_EARPIECE CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, @@ -200,12 +265,69 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + CallAudioState expectedCallAudioState = stateMachine.getLastKnownCallAudioState(); + // assert expected end state assertEquals(stateMachine.getCurrentCallAudioState().getRoute(), CallAudioRouteStateMachine.ROUTE_SPEAKER); // should update the audio route on all tracked calls ... verify(mockConnectionServiceWrapper, times(trackedCalls.size())) .onCallAudioStateChanged(any(), any()); + + assertNotEquals(expectedCallAudioState, stateMachine.getCurrentCallAudioState()); + } + + @SmallTest + @Test + public void testSystemAudioStateIsUpdatedFlagOn() { + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + Set<Call> trackedCalls = new HashSet<>(Arrays.asList(fakeCall, fakeSelfManagedCall)); + when(mockCallsManager.getTrackedCalls()).thenReturn(trackedCalls); + when(mFeatureFlags.availableRoutesNeverUpdatedAfterSetSystemAudioState()).thenReturn(true); + + // start state --> ROUTE_EARPIECE + CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); + stateMachine.initialize(initState); + + stateMachine.setCallAudioManager(mockCallAudioManager); + + assertEquals(stateMachine.getCurrentCallAudioState().getRoute(), + CallAudioRouteStateMachine.ROUTE_EARPIECE); + + // ROUTE_EARPIECE --> ROUTE_SPEAKER + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_SPEAKER, + CallAudioRouteStateMachine.SPEAKER_ON); + + stateMachine.sendMessageWithSessionInfo( + CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); + + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + CallAudioState expectedCallAudioState = stateMachine.getLastKnownCallAudioState(); + + // assert expected end state + assertEquals(stateMachine.getCurrentCallAudioState().getRoute(), + CallAudioRouteStateMachine.ROUTE_SPEAKER); + // should update the audio route on all tracked calls ... + verify(mockConnectionServiceWrapper, times(trackedCalls.size())) + .onCallAudioStateChanged(any(), any()); + + assertEquals(expectedCallAudioState, stateMachine.getCurrentCallAudioState()); } @MediumTest @@ -219,7 +341,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER, CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); @@ -263,7 +388,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true); @@ -286,14 +414,14 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioState expectedMiddleState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET, CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verifyNewSystemCallAudioState(initState, expectedMiddleState); resetMocks(); stateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verifyNewSystemCallAudioState(expectedMiddleState, initState); } @@ -308,7 +436,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); @@ -329,7 +460,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioState.ROUTE_EARPIECE, CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verifyNewSystemCallAudioState(initState, expectedEndState); resetMocks(); stateMachine.sendMessageWithSessionInfo( @@ -337,7 +468,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { stateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState()); } @@ -352,7 +483,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); Collection<BluetoothDevice> availableDevices = Collections.singleton(bluetoothDevice1); @@ -377,12 +511,12 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, null, availableDevices); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verifyNewSystemCallAudioState(initState, expectedMidState); // clear out the handler state before resetting mocks in order to avoid introducing a // CallAudioState that has a null list of supported BT devices - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); resetMocks(); // Now, switch back to BT explicitly @@ -400,9 +534,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioState.ROUTE_BLUETOOTH, CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, bluetoothDevice1, availableDevices); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); // second wait needed for the BT_AUDIO_CONNECTED message - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verifyNewSystemCallAudioState(expectedMidState, expectedEndState); stateMachine.sendMessageWithSessionInfo( @@ -412,9 +546,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { stateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); // second wait needed for the BT_AUDIO_CONNECTED message - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); // Verify that we're still on bluetooth. assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState()); } @@ -430,7 +564,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); @@ -445,13 +582,13 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.RINGING_FOCUS); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class)); stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.ACTIVE_FOCUS); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(nullable(String.class)); } @@ -466,7 +603,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); setInBandRing(false); when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); @@ -480,7 +620,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioRouteStateMachine.RINGING_FOCUS); // Wait for the state machine to finish transiting to ActiveEarpiece before hooking up // bluetooth mocks - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true); when(mockBluetoothRouteManager.getConnectedDevices()) @@ -489,7 +629,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED); stateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null); CallAudioState expectedEndState = new CallAudioState(false, @@ -500,14 +640,17 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.ACTIVE_FOCUS); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(null); when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice()) .thenReturn(bluetoothDevice1); stateMachine.sendMessage(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); - verify(mockCallAudioManager, times(1)).onRingerModeChange(); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + // It is possible that this will be called twice from ActiveBluetoothRoute#enter. The extra + // call to setBluetoothOn will trigger BT_AUDIO_CONNECTED, which also ends up invoking + // CallAudioManager#onRingerModeChange. + verify(mockCallAudioManager, atLeastOnce()).onRingerModeChange(); } @SmallTest @@ -521,7 +664,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); List<BluetoothDevice> availableDevices = Arrays.asList(bluetoothDevice1, bluetoothDevice2, bluetoothDevice3); @@ -547,17 +693,80 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH, 0, bluetoothDevice2.getAddress()); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verify(mockBluetoothRouteManager).connectBluetoothAudio(bluetoothDevice2.getAddress()); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); CallAudioState expectedEndState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH, CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, bluetoothDevice2, availableDevices); - verifyNewSystemCallAudioState(initState, expectedEndState); + assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState()); + } + + @SmallTest + @Test + public void testCallDisconnectedWhenAudioRoutedToBluetooth() { + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true); + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + List<BluetoothDevice> availableDevices = Arrays.asList(bluetoothDevice1); + + when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false); + when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); + when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true); + when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices); + when(mockBluetoothRouteManager.isInbandRingingEnabled()).thenReturn(true); + when(mFeatureFlags.transitRouteBeforeAudioDisconnectBt()).thenReturn(true); + doAnswer(invocation -> { + when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice()) + .thenReturn(bluetoothDevice1); + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); + return null; + }).when(mockBluetoothRouteManager).connectBluetoothAudio(bluetoothDevice1.getAddress()); + doAnswer(invocation -> { + when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice()) + .thenReturn(bluetoothDevice1); + stateMachine.sendMessageWithSessionInfo( + CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED); + return null; + }).when(mockBluetoothRouteManager).disconnectAudio(); + + CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, null, + availableDevices); + stateMachine.initialize(initState); + + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.NO_FOCUS, bluetoothDevice1.getAddress()); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + verify(mockBluetoothRouteManager).disconnectAudio(); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + CallAudioState expectedEndState = new CallAudioState(false, + CallAudioState.ROUTE_BLUETOOTH, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, + bluetoothDevice1, + availableDevices); + + assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState()); } @SmallTest @@ -571,7 +780,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false); CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER, @@ -580,13 +792,13 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { // Raise a dock connect event. stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.CONNECT_DOCK); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); assertTrue(!stateMachine.isInActiveState()); verify(mockAudioManager, never()).setSpeakerphoneOn(eq(true)); // Raise a dock disconnect event. stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.DISCONNECT_DOCK); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); assertTrue(!stateMachine.isInActiveState()); verify(mockAudioManager, never()).setSpeakerphoneOn(eq(false)); } @@ -602,7 +814,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false); @@ -614,7 +829,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { // Switch to active, pretending that a call came in. stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.ACTIVE_FOCUS); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); // Make sure that we've successfully switched to the active speaker route and that we've // called setSpeakerOn @@ -636,7 +851,10 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mockStatusBarNotifier, mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, - Runnable::run /** do async stuff sync for test purposes */); + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); List<BluetoothDevice> availableDevices = Arrays.asList(bluetoothDevice1, bluetoothDevice2); @@ -658,7 +876,7 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { // Switch to active, pretending that a call came in. stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.ACTIVE_FOCUS); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); // Make sure that we've successfully switched to the active BT route and that we've // called connectAudio on the right device. @@ -669,6 +887,112 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { @SmallTest @Test + public void testSetAndClearEarpieceCommunicationDevice() { + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true); + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + AudioDeviceInfo earpiece = mock(AudioDeviceInfo.class); + when(earpiece.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); + when(earpiece.getAddress()).thenReturn(""); + List<AudioDeviceInfo> devices = new ArrayList<>(); + devices.add(earpiece); + + when(mockAudioManager.getAvailableCommunicationDevices()) + .thenReturn(devices); + when(mockAudioManager.setCommunicationDevice(eq(earpiece))) + .thenReturn(true); + when(mockAudioManager.getCommunicationDevice()).thenReturn(earpiece); + + CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER | + CallAudioState.ROUTE_WIRED_HEADSET); + stateMachine.initialize(initState); + + // Switch to active + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // Make sure that we've successfully switched to the active earpiece and that we set the + // communication device. + assertTrue(stateMachine.isInActiveState()); + ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass( + AudioDeviceInfo.class); + verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture()); + assertEquals(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, + infoArgumentCaptor.getValue().getType()); + + // Route earpiece to speaker + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_SPEAKER, + CallAudioRouteStateMachine.SPEAKER_ON); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // Assert that communication device was cleared + verify(mockAudioManager).clearCommunicationDevice(); + } + + @SmallTest + @Test + public void testSetAndClearWiredHeadsetCommunicationDevice() { + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true); + verifySetAndClearHeadsetCommunicationDevice(AudioDeviceInfo.TYPE_WIRED_HEADSET); + } + + @SmallTest + @Test + public void testSetAndClearUsbHeadsetCommunicationDevice() { + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true); + verifySetAndClearHeadsetCommunicationDevice(AudioDeviceInfo.TYPE_USB_HEADSET); + } + + @SmallTest + @Test + public void testActiveFocusRouteSwitchFromQuiescentBluetooth() { + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true); + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + // Start the route in quiescent and ensure that a switch to ACTIVE_FOCUS transitions to + // the corresponding active route even when there aren't any active BT devices available. + CallAudioState initState = new CallAudioState(false, + CallAudioState.ROUTE_BLUETOOTH, + CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE); + stateMachine.initialize(initState); + + // Switch to active + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // Make sure that we've successfully switched to the active route on BT + assertTrue(stateMachine.isInActiveState()); + } + + @SmallTest + @Test public void testInitializationWithEarpieceNoHeadsetNoBluetooth() { CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); @@ -752,7 +1076,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, mThreadHandler.getLooper(), - Runnable::run /** do async stuff sync for test purposes */); + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.initialize(); assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); } @@ -769,7 +1095,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, mThreadHandler.getLooper(), - Runnable::run /** do async stuff sync for test purposes */); + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, @@ -780,12 +1108,12 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { CallAudioState expectedEndState = new CallAudioState(false, CallAudioState.ROUTE_STREAMING, CallAudioState.ROUTE_STREAMING); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); verifyNewSystemCallAudioState(initState, expectedEndState); resetMocks(); stateMachine.sendMessageWithSessionInfo( CallAudioRouteStateMachine.STREAMING_FORCE_DISABLED); - waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); assertEquals(initState, stateMachine.getCurrentCallAudioState()); } @@ -805,7 +1133,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mAudioServiceFactory, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, mThreadHandler.getLooper(), - Runnable::run /** do async stuff sync for test purposes */); + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.setCallAudioManager(mockCallAudioManager); CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER, @@ -830,6 +1160,154 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); } + @MediumTest + @Test + public void testIgnoreImplicitBTSwitchWhenDeviceIsWatch() { + when(mFeatureFlags.ignoreAutoRouteToWatchDevice()).thenReturn(true); + when(mFeatureFlags.callAudioCommunicationDeviceRefactor()).thenReturn(true); + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + AudioDeviceInfo headset = mock(AudioDeviceInfo.class); + when(headset.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET); + when(headset.getAddress()).thenReturn(""); + List<AudioDeviceInfo> devices = new ArrayList<>(); + devices.add(headset); + + when(mockAudioManager.getAvailableCommunicationDevices()) + .thenReturn(devices); + when(mockAudioManager.setCommunicationDevice(eq(headset))) + .thenReturn(true); + when(mockAudioManager.getCommunicationDevice()).thenReturn(headset); + + CallAudioState initState = new CallAudioState(false, + CallAudioState.ROUTE_WIRED_HEADSET, CallAudioState.ROUTE_WIRED_HEADSET + | CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH); + stateMachine.initialize(initState); + + // Switch to active + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // Make sure that we've successfully switched to the active headset. + assertTrue(stateMachine.isInActiveState()); + ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass( + AudioDeviceInfo.class); + verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture()); + assertEquals(AudioDeviceInfo.TYPE_WIRED_HEADSET, infoArgumentCaptor.getValue().getType()); + + // Set up watch device as only available BT device. + Collection<BluetoothDevice> availableDevices = Collections.singleton(mockWatchDevice); + + when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false); + when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true); + when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices); + when(mockBluetoothRouteManager.isWatch(any(BluetoothDevice.class))).thenReturn(true); + + // Disconnect wired headset to force switch to BT (verify that we ignore the implicit switch + // to BT when the watch is the only connected device and that we move into the next + // available route. + stateMachine.sendMessageWithSessionInfo( + CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, + null, availableDevices); + assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); + } + + @SmallTest + @Test + public void testQuiescentBluetoothRouteResetMute() { + when(mFeatureFlags.resetMuteWhenEnteringQuiescentBtRoute()).thenReturn(true); + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + CallAudioState initState = new CallAudioState(false, + CallAudioState.ROUTE_BLUETOOTH, CallAudioState.ROUTE_SPEAKER + | CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH); + stateMachine.initialize(initState); + + // Switch to active and mute + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + assertTrue(stateMachine.isInActiveState()); + + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.MUTE_ON); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + CallAudioState expectedState = new CallAudioState(true, + CallAudioState.ROUTE_BLUETOOTH, CallAudioState.ROUTE_SPEAKER + | CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH); + assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); + + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.NO_FOCUS); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + expectedState = new CallAudioState(false, + CallAudioState.ROUTE_BLUETOOTH, CallAudioState.ROUTE_SPEAKER + | CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH); + // TODO: Re-enable this part of the test; this is now failing because we have to + // revert ag/23783145. + // assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); + } + + @SmallTest + @Test + public void testSupportRouteMaskUpdateWhenBtAudioConnected() { + when(mFeatureFlags.updateRouteMaskWhenBtConnected()).thenReturn(true); + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + CallAudioState initState = new CallAudioState(false, + CallAudioState.ROUTE_EARPIECE, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); + stateMachine.initialize(initState); + + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER + | CallAudioState.ROUTE_BLUETOOTH); + assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); + } + private void initializationTestHelper(CallAudioState expectedState, int earpieceControl) { when(mockWiredHeadsetManager.isPluggedIn()).thenReturn( @@ -848,7 +1326,9 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { mAudioServiceFactory, earpieceControl, mThreadHandler.getLooper(), - Runnable::run /** do async stuff sync for test purposes */); + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); stateMachine.initialize(); assertEquals(expectedState, stateMachine.getCurrentCallAudioState()); } @@ -888,4 +1368,59 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class), any(CallAudioState.class)); } + + private void verifySetAndClearHeadsetCommunicationDevice(int audioType) { + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper(), + Runnable::run /** do async stuff sync for test purposes */, + mCommunicationDeviceTracker, + mFeatureFlags); + stateMachine.setCallAudioManager(mockCallAudioManager); + + AudioDeviceInfo headset = mock(AudioDeviceInfo.class); + when(headset.getType()).thenReturn(audioType); + when(headset.getAddress()).thenReturn(""); + List<AudioDeviceInfo> devices = new ArrayList<>(); + devices.add(headset); + + when(mockAudioManager.getAvailableCommunicationDevices()) + .thenReturn(devices); + when(mockAudioManager.setCommunicationDevice(eq(headset))) + .thenReturn(true); + when(mockAudioManager.getCommunicationDevice()).thenReturn(headset); + + CallAudioState initState = new CallAudioState(false, + CallAudioState.ROUTE_WIRED_HEADSET, + CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_EARPIECE); + stateMachine.initialize(initState); + + // Switch to active + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // Make sure that we've successfully switched to the active headset and that we set the + // communication device. + assertTrue(stateMachine.isInActiveState()); + ArgumentCaptor<AudioDeviceInfo> infoArgumentCaptor = ArgumentCaptor.forClass( + AudioDeviceInfo.class); + verify(mockAudioManager).setCommunicationDevice(infoArgumentCaptor.capture()); + assertEquals(audioType, infoArgumentCaptor.getValue().getType()); + + // Route out of headset route + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS, + CallAudioRouteStateMachine.ACTIVE_FOCUS); + stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.USER_SWITCH_EARPIECE); + waitForHandlerAction(stateMachine.getAdapterHandler(), TEST_TIMEOUT); + + // Assert that communication device was cleared + verify(mockAudioManager).clearCommunicationDevice(); + } } |