1 /* 2 * Copyright (C) 2016 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 android.media.ToneGenerator; 20 import android.telecom.DisconnectCause; 21 import android.test.suitebuilder.annotation.MediumTest; 22 import android.util.SparseArray; 23 24 import com.android.server.telecom.Call; 25 import com.android.server.telecom.CallAudioModeStateMachine; 26 import com.android.server.telecom.CallAudioRouteStateMachine; 27 import com.android.server.telecom.CallState; 28 import com.android.server.telecom.CallsManager; 29 import com.android.server.telecom.CallAudioManager; 30 import com.android.server.telecom.DtmfLocalTonePlayer; 31 import com.android.server.telecom.InCallTonePlayer; 32 import com.android.server.telecom.RingbackPlayer; 33 import com.android.server.telecom.Ringer; 34 35 import org.mockito.ArgumentCaptor; 36 import org.mockito.Mock; 37 38 import java.util.LinkedHashSet; 39 40 import static org.mockito.Matchers.any; 41 import static org.mockito.Matchers.anyInt; 42 import static org.mockito.Matchers.eq; 43 import static org.mockito.Mockito.doAnswer; 44 import static org.mockito.Mockito.mock; 45 import static org.mockito.Mockito.times; 46 import static org.mockito.Mockito.verify; 47 import static org.mockito.Mockito.when; 48 49 public class CallAudioManagerTest extends TelecomTestCase { 50 @Mock private CallAudioRouteStateMachine mCallAudioRouteStateMachine; 51 @Mock private CallsManager mCallsManager; 52 @Mock private CallAudioModeStateMachine mCallAudioModeStateMachine; 53 @Mock private InCallTonePlayer.Factory mPlayerFactory; 54 @Mock private Ringer mRinger; 55 @Mock private RingbackPlayer mRingbackPlayer; 56 @Mock private DtmfLocalTonePlayer mDtmfLocalTonePlayer; 57 58 private CallAudioManager mCallAudioManager; 59 60 @Override setUp()61 public void setUp() throws Exception { 62 super.setUp(); 63 doAnswer((invocation) -> { 64 InCallTonePlayer mockInCallTonePlayer = mock(InCallTonePlayer.class); 65 doAnswer((invocation2) -> { 66 mCallAudioManager.setIsTonePlaying(true); 67 return null; 68 }).when(mockInCallTonePlayer).startTone(); 69 return mockInCallTonePlayer; 70 }).when(mPlayerFactory).createPlayer(anyInt()); 71 mCallAudioManager = new CallAudioManager( 72 mCallAudioRouteStateMachine, 73 mCallsManager, 74 mCallAudioModeStateMachine, 75 mPlayerFactory, 76 mRinger, 77 mRingbackPlayer, 78 mDtmfLocalTonePlayer); 79 } 80 81 @MediumTest testSingleIncomingCallFlowWithoutMTSpeedUp()82 public void testSingleIncomingCallFlowWithoutMTSpeedUp() { 83 Call call = createIncomingCall(); 84 when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) 85 .thenReturn(false); 86 87 ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = 88 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class); 89 // Answer the incoming call 90 mCallAudioManager.onIncomingCallAnswered(call); 91 when(call.getState()).thenReturn(CallState.ACTIVE); 92 mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE); 93 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 94 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture()); 95 CallAudioModeStateMachine.MessageArgs correctArgs = 96 new CallAudioModeStateMachine.MessageArgs( 97 true, // hasActiveOrDialingCalls 98 false, // hasRingingCalls 99 false, // hasHoldingCalls 100 false, // isTonePlaying 101 false, // foregroundCallIsVoip 102 null // session 103 ); 104 assertMessageArgEquality(correctArgs, captor.getValue()); 105 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 106 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); 107 assertMessageArgEquality(correctArgs, captor.getValue()); 108 109 disconnectCall(call); 110 stopTone(); 111 112 mCallAudioManager.onCallRemoved(call); 113 verifyProperCleanup(); 114 } 115 116 @MediumTest testSingleIncomingCallFlowWithMTSpeedUp()117 public void testSingleIncomingCallFlowWithMTSpeedUp() { 118 Call call = createIncomingCall(); 119 when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) 120 .thenReturn(true); 121 122 ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = 123 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class); 124 // Answer the incoming call 125 mCallAudioManager.onIncomingCallAnswered(call); 126 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 127 eq(CallAudioModeStateMachine.MT_AUDIO_SPEEDUP_FOR_RINGING_CALL), captor.capture()); 128 CallAudioModeStateMachine.MessageArgs correctArgs = 129 new CallAudioModeStateMachine.MessageArgs( 130 true, // hasActiveOrDialingCalls 131 false, // hasRingingCalls 132 false, // hasHoldingCalls 133 false, // isTonePlaying 134 false, // foregroundCallIsVoip 135 null // session 136 ); 137 assertMessageArgEquality(correctArgs, captor.getValue()); 138 assertMessageArgEquality(correctArgs, captor.getValue()); 139 when(call.getState()).thenReturn(CallState.ACTIVE); 140 mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE); 141 142 disconnectCall(call); 143 stopTone(); 144 145 mCallAudioManager.onCallRemoved(call); 146 verifyProperCleanup(); 147 } 148 149 @MediumTest testSingleOutgoingCall()150 public void testSingleOutgoingCall() { 151 Call call = mock(Call.class); 152 when(call.getState()).thenReturn(CallState.CONNECTING); 153 154 mCallAudioManager.onCallAdded(call); 155 assertEquals(call, mCallAudioManager.getForegroundCall()); 156 ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = 157 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class); 158 verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo( 159 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); 160 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 161 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); 162 CallAudioModeStateMachine.MessageArgs expectedArgs = 163 new CallAudioModeStateMachine.MessageArgs( 164 true, // hasActiveOrDialingCalls 165 false, // hasRingingCalls 166 false, // hasHoldingCalls 167 false, // isTonePlaying 168 false, // foregroundCallIsVoip 169 null // session 170 ); 171 assertMessageArgEquality(expectedArgs, captor.getValue()); 172 173 when(call.getState()).thenReturn(CallState.DIALING); 174 mCallAudioManager.onCallStateChanged(call, CallState.CONNECTING, CallState.DIALING); 175 verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( 176 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); 177 assertMessageArgEquality(expectedArgs, captor.getValue()); 178 verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs( 179 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); 180 181 182 when(call.getState()).thenReturn(CallState.ACTIVE); 183 mCallAudioManager.onCallStateChanged(call, CallState.DIALING, CallState.ACTIVE); 184 verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( 185 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture()); 186 assertMessageArgEquality(expectedArgs, captor.getValue()); 187 verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs( 188 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class)); 189 190 disconnectCall(call); 191 stopTone(); 192 193 mCallAudioManager.onCallRemoved(call); 194 verifyProperCleanup(); 195 } 196 createIncomingCall()197 private Call createIncomingCall() { 198 Call call = mock(Call.class); 199 when(call.getState()).thenReturn(CallState.RINGING); 200 201 mCallAudioManager.onCallAdded(call); 202 assertEquals(call, mCallAudioManager.getForegroundCall()); 203 ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = 204 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class); 205 verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo( 206 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); 207 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 208 eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture()); 209 assertMessageArgEquality(new CallAudioModeStateMachine.MessageArgs( 210 false, // hasActiveOrDialingCalls 211 true, // hasRingingCalls 212 false, // hasHoldingCalls 213 false, // isTonePlaying 214 false, // foregroundCallIsVoip 215 null // session 216 ), captor.getValue()); 217 218 return call; 219 } 220 disconnectCall(Call call)221 private void disconnectCall(Call call) { 222 ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = 223 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class); 224 CallAudioModeStateMachine.MessageArgs correctArgs; 225 226 when(call.getState()).thenReturn(CallState.DISCONNECTED); 227 when(call.getDisconnectCause()).thenReturn(new DisconnectCause(DisconnectCause.LOCAL, 228 "", "", "", ToneGenerator.TONE_PROP_PROMPT)); 229 230 mCallAudioManager.onCallStateChanged(call, CallState.ACTIVE, CallState.DISCONNECTED); 231 verify(mPlayerFactory).createPlayer(InCallTonePlayer.TONE_CALL_ENDED); 232 correctArgs = new CallAudioModeStateMachine.MessageArgs( 233 false, // hasActiveOrDialingCalls 234 false, // hasRingingCalls 235 false, // hasHoldingCalls 236 true, // isTonePlaying 237 false, // foregroundCallIsVoip 238 null // session 239 ); 240 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 241 eq(CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS), captor.capture()); 242 assertMessageArgEquality(correctArgs, captor.getValue()); 243 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 244 eq(CallAudioModeStateMachine.TONE_STARTED_PLAYING), captor.capture()); 245 assertMessageArgEquality(correctArgs, captor.getValue()); 246 } 247 stopTone()248 private void stopTone() { 249 ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = 250 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class); 251 mCallAudioManager.setIsTonePlaying(false); 252 CallAudioModeStateMachine.MessageArgs correctArgs = 253 new CallAudioModeStateMachine.MessageArgs( 254 false, // hasActiveOrDialingCalls 255 false, // hasRingingCalls 256 false, // hasHoldingCalls 257 false, // isTonePlaying 258 false, // foregroundCallIsVoip 259 null // session 260 ); 261 verify(mCallAudioModeStateMachine).sendMessageWithArgs( 262 eq(CallAudioModeStateMachine.TONE_STOPPED_PLAYING), captor.capture()); 263 assertMessageArgEquality(correctArgs, captor.getValue()); 264 } 265 verifyProperCleanup()266 private void verifyProperCleanup() { 267 assertEquals(0, mCallAudioManager.getTrackedCalls().size()); 268 SparseArray<LinkedHashSet<Call>> callStateToCalls = mCallAudioManager.getCallStateToCalls(); 269 for (int i = 0; i < callStateToCalls.size(); i++) { 270 assertEquals(0, callStateToCalls.valueAt(i).size()); 271 } 272 } 273 assertMessageArgEquality(CallAudioModeStateMachine.MessageArgs expected, CallAudioModeStateMachine.MessageArgs actual)274 private void assertMessageArgEquality(CallAudioModeStateMachine.MessageArgs expected, 275 CallAudioModeStateMachine.MessageArgs actual) { 276 assertEquals(expected.hasActiveOrDialingCalls, actual.hasActiveOrDialingCalls); 277 assertEquals(expected.hasHoldingCalls, actual.hasHoldingCalls); 278 assertEquals(expected.hasRingingCalls, actual.hasRingingCalls); 279 assertEquals(expected.isTonePlaying, actual.isTonePlaying); 280 assertEquals(expected.foregroundCallIsVoip, actual.foregroundCallIsVoip); 281 } 282 } 283