• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.util.List;
40 import java.util.stream.Collectors;
41 
42 import static org.mockito.Matchers.any;
43 import static org.mockito.Matchers.anyInt;
44 import static org.mockito.Matchers.eq;
45 import static org.mockito.Mockito.atLeastOnce;
46 import static org.mockito.Mockito.doAnswer;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.times;
49 import static org.mockito.Mockito.verify;
50 import static org.mockito.Mockito.when;
51 
52 public class CallAudioManagerTest extends TelecomTestCase {
53     @Mock private CallAudioRouteStateMachine mCallAudioRouteStateMachine;
54     @Mock private CallsManager mCallsManager;
55     @Mock private CallAudioModeStateMachine mCallAudioModeStateMachine;
56     @Mock private InCallTonePlayer.Factory mPlayerFactory;
57     @Mock private Ringer mRinger;
58     @Mock private RingbackPlayer mRingbackPlayer;
59     @Mock private DtmfLocalTonePlayer mDtmfLocalTonePlayer;
60 
61     private CallAudioManager mCallAudioManager;
62 
63     @Override
setUp()64     public void setUp() throws Exception {
65         super.setUp();
66         doAnswer((invocation) -> {
67             InCallTonePlayer mockInCallTonePlayer = mock(InCallTonePlayer.class);
68             doAnswer((invocation2) -> {
69                 mCallAudioManager.setIsTonePlaying(true);
70                 return null;
71             }).when(mockInCallTonePlayer).startTone();
72             return mockInCallTonePlayer;
73         }).when(mPlayerFactory).createPlayer(anyInt());
74         mCallAudioManager = new CallAudioManager(
75                 mCallAudioRouteStateMachine,
76                 mCallsManager,
77                 mCallAudioModeStateMachine,
78                 mPlayerFactory,
79                 mRinger,
80                 mRingbackPlayer,
81                 mDtmfLocalTonePlayer);
82     }
83 
84     @MediumTest
testUnmuteOfSecondIncomingCall()85     public void testUnmuteOfSecondIncomingCall() {
86         // Start with a single incoming call.
87         Call call = createIncomingCall();
88         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
89                 .thenReturn(false);
90         when(call.getId()).thenReturn("1");
91 
92         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
93                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
94         // Answer the incoming call
95         mCallAudioManager.onIncomingCallAnswered(call);
96         when(call.getState()).thenReturn(CallState.ACTIVE);
97         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE);
98         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
99                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
100         CallAudioModeStateMachine.MessageArgs correctArgs =
101                 new CallAudioModeStateMachine.MessageArgs(
102                         true, // hasActiveOrDialingCalls
103                         false, // hasRingingCalls
104                         false, // hasHoldingCalls
105                         false, // isTonePlaying
106                         false, // foregroundCallIsVoip
107                         null // session
108                 );
109         assertMessageArgEquality(correctArgs, captor.getValue());
110         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
111                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
112         assertMessageArgEquality(correctArgs, captor.getValue());
113 
114         // Mute the current ongoing call.
115         mCallAudioManager.mute(true);
116 
117         // Create a second incoming call.
118         Call call2 = mock(Call.class);
119         when(call2.getState()).thenReturn(CallState.RINGING);
120         when(call2.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
121                 .thenReturn(false);
122         when(call2.getId()).thenReturn("2");
123         mCallAudioManager.onCallAdded(call2);
124 
125         // Answer the incoming call
126         mCallAudioManager.onIncomingCallAnswered(call);
127 
128         // Capture the calls to sendMessageWithSessionInfo; we want to look for mute on and off
129         // messages and make sure that there was a mute on before the mute off.
130         ArgumentCaptor<Integer> muteCaptor = ArgumentCaptor.forClass(Integer.class);
131         verify(mCallAudioRouteStateMachine, atLeastOnce())
132                 .sendMessageWithSessionInfo(muteCaptor.capture());
133         List<Integer> values = muteCaptor.getAllValues();
134         values = values.stream()
135                 .filter(value -> value == CallAudioRouteStateMachine.MUTE_ON ||
136                         value == CallAudioRouteStateMachine.MUTE_OFF)
137                 .collect(Collectors.toList());
138 
139         // Make sure we got a mute on and a mute off.
140         assertTrue(values.contains(CallAudioRouteStateMachine.MUTE_ON));
141         assertTrue(values.contains(CallAudioRouteStateMachine.MUTE_OFF));
142         // And that the mute on happened before the off.
143         assertTrue(values.indexOf(CallAudioRouteStateMachine.MUTE_ON) < values
144                 .lastIndexOf(CallAudioRouteStateMachine.MUTE_OFF));
145     }
146 
147     @MediumTest
148     public void testSingleIncomingCallFlowWithoutMTSpeedUp() {
149         Call call = createIncomingCall();
150         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
151                 .thenReturn(false);
152 
153         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
154                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
155         // Answer the incoming call
156         mCallAudioManager.onIncomingCallAnswered(call);
157         when(call.getState()).thenReturn(CallState.ACTIVE);
158         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE);
159         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
160                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
161         CallAudioModeStateMachine.MessageArgs correctArgs =
162                 new CallAudioModeStateMachine.MessageArgs(
163                         true, // hasActiveOrDialingCalls
164                         false, // hasRingingCalls
165                         false, // hasHoldingCalls
166                         false, // isTonePlaying
167                         false, // foregroundCallIsVoip
168                         null // session
169                 );
170         assertMessageArgEquality(correctArgs, captor.getValue());
171         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
172                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
173         assertMessageArgEquality(correctArgs, captor.getValue());
174 
175         disconnectCall(call);
176         stopTone();
177 
178         mCallAudioManager.onCallRemoved(call);
179         verifyProperCleanup();
180     }
181 
182     @MediumTest
183     public void testSingleIncomingCallFlowWithMTSpeedUp() {
184         Call call = createIncomingCall();
185         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
186                 .thenReturn(true);
187 
188         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
189                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
190         // Answer the incoming call
191         mCallAudioManager.onIncomingCallAnswered(call);
192         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
193                 eq(CallAudioModeStateMachine.MT_AUDIO_SPEEDUP_FOR_RINGING_CALL), captor.capture());
194         CallAudioModeStateMachine.MessageArgs correctArgs =
195                 new CallAudioModeStateMachine.MessageArgs(
196                         true, // hasActiveOrDialingCalls
197                         false, // hasRingingCalls
198                         false, // hasHoldingCalls
199                         false, // isTonePlaying
200                         false, // foregroundCallIsVoip
201                         null // session
202                 );
203         assertMessageArgEquality(correctArgs, captor.getValue());
204         assertMessageArgEquality(correctArgs, captor.getValue());
205         when(call.getState()).thenReturn(CallState.ACTIVE);
206         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ACTIVE);
207 
208         disconnectCall(call);
209         stopTone();
210 
211         mCallAudioManager.onCallRemoved(call);
212         verifyProperCleanup();
213     }
214 
215     @MediumTest
216     public void testSingleOutgoingCall() {
217         Call call = mock(Call.class);
218         when(call.getState()).thenReturn(CallState.CONNECTING);
219 
220         mCallAudioManager.onCallAdded(call);
221         assertEquals(call, mCallAudioManager.getForegroundCall());
222         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
223                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
224         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
225                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
226         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
227                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
228         CallAudioModeStateMachine.MessageArgs expectedArgs =
229                 new CallAudioModeStateMachine.MessageArgs(
230                         true, // hasActiveOrDialingCalls
231                         false, // hasRingingCalls
232                         false, // hasHoldingCalls
233                         false, // isTonePlaying
234                         false, // foregroundCallIsVoip
235                         null // session
236                 );
237         assertMessageArgEquality(expectedArgs, captor.getValue());
238 
239         when(call.getState()).thenReturn(CallState.DIALING);
240         mCallAudioManager.onCallStateChanged(call, CallState.CONNECTING, CallState.DIALING);
241         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
242                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
243         assertMessageArgEquality(expectedArgs, captor.getValue());
244         verify(mCallAudioModeStateMachine, times(2)).sendMessageWithArgs(
245                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
246 
247 
248         when(call.getState()).thenReturn(CallState.ACTIVE);
249         mCallAudioManager.onCallStateChanged(call, CallState.DIALING, CallState.ACTIVE);
250         verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
251                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
252         assertMessageArgEquality(expectedArgs, captor.getValue());
253         verify(mCallAudioModeStateMachine, times(3)).sendMessageWithArgs(
254                 anyInt(), any(CallAudioModeStateMachine.MessageArgs.class));
255 
256         disconnectCall(call);
257         stopTone();
258 
259         mCallAudioManager.onCallRemoved(call);
260         verifyProperCleanup();
261     }
262 
263     private Call createIncomingCall() {
264         Call call = mock(Call.class);
265         when(call.getState()).thenReturn(CallState.RINGING);
266 
267         mCallAudioManager.onCallAdded(call);
268         assertEquals(call, mCallAudioManager.getForegroundCall());
269         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
270                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
271         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
272                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
273         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
274                 eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture());
275         assertMessageArgEquality(new CallAudioModeStateMachine.MessageArgs(
276                 false, // hasActiveOrDialingCalls
277                 true, // hasRingingCalls
278                 false, // hasHoldingCalls
279                 false, // isTonePlaying
280                 false, // foregroundCallIsVoip
281                 null // session
282         ), captor.getValue());
283 
284         return call;
285     }
286 
287     private void disconnectCall(Call call) {
288         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
289                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
290         CallAudioModeStateMachine.MessageArgs correctArgs;
291 
292         when(call.getState()).thenReturn(CallState.DISCONNECTED);
293         when(call.getDisconnectCause()).thenReturn(new DisconnectCause(DisconnectCause.LOCAL,
294                 "", "", "", ToneGenerator.TONE_PROP_PROMPT));
295 
296         mCallAudioManager.onCallStateChanged(call, CallState.ACTIVE, CallState.DISCONNECTED);
297         verify(mPlayerFactory).createPlayer(InCallTonePlayer.TONE_CALL_ENDED);
298         correctArgs = new CallAudioModeStateMachine.MessageArgs(
299                 false, // hasActiveOrDialingCalls
300                 false, // hasRingingCalls
301                 false, // hasHoldingCalls
302                 true, // isTonePlaying
303                 false, // foregroundCallIsVoip
304                 null // session
305         );
306         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
307                 eq(CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS), captor.capture());
308         assertMessageArgEquality(correctArgs, captor.getValue());
309         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
310                 eq(CallAudioModeStateMachine.TONE_STARTED_PLAYING), captor.capture());
311         assertMessageArgEquality(correctArgs, captor.getValue());
312     }
313 
314     private void stopTone() {
315         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
316                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
317         mCallAudioManager.setIsTonePlaying(false);
318         CallAudioModeStateMachine.MessageArgs correctArgs =
319                 new CallAudioModeStateMachine.MessageArgs(
320                         false, // hasActiveOrDialingCalls
321                         false, // hasRingingCalls
322                         false, // hasHoldingCalls
323                         false, // isTonePlaying
324                         false, // foregroundCallIsVoip
325                         null // session
326                 );
327         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
328                 eq(CallAudioModeStateMachine.TONE_STOPPED_PLAYING), captor.capture());
329         assertMessageArgEquality(correctArgs, captor.getValue());
330     }
331 
332     private void verifyProperCleanup() {
333         assertEquals(0, mCallAudioManager.getTrackedCalls().size());
334         SparseArray<LinkedHashSet<Call>> callStateToCalls = mCallAudioManager.getCallStateToCalls();
335         for (int i = 0; i < callStateToCalls.size(); i++) {
336             assertEquals(0, callStateToCalls.valueAt(i).size());
337         }
338     }
339 
340     private void assertMessageArgEquality(CallAudioModeStateMachine.MessageArgs expected,
341             CallAudioModeStateMachine.MessageArgs actual) {
342         assertEquals(expected.hasActiveOrDialingCalls, actual.hasActiveOrDialingCalls);
343         assertEquals(expected.hasHoldingCalls, actual.hasHoldingCalls);
344         assertEquals(expected.hasRingingCalls, actual.hasRingingCalls);
345         assertEquals(expected.isTonePlaying, actual.isTonePlaying);
346         assertEquals(expected.foregroundCallIsVoip, actual.foregroundCallIsVoip);
347     }
348 }
349