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 */
16package com.android.internal.telephony;
17
18import android.os.Handler;
19import android.os.HandlerThread;
20import android.os.Message;
21import android.telephony.PhoneNumberUtils;
22import android.telephony.ServiceState;
23import android.test.suitebuilder.annotation.SmallTest;
24
25import org.junit.After;
26import org.junit.Before;
27import org.junit.Test;
28import org.mockito.ArgumentCaptor;
29import org.mockito.Mock;
30import org.mockito.Mockito;
31
32import java.lang.reflect.Field;
33
34import static org.junit.Assert.assertEquals;
35import static org.junit.Assert.assertFalse;
36import static org.junit.Assert.assertTrue;
37import static org.mockito.Matchers.anyBoolean;
38import static org.mockito.Matchers.anyInt;
39import static org.mockito.Matchers.eq;
40import static org.mockito.Matchers.isA;
41import static org.mockito.Matchers.isNull;
42import static org.mockito.Mockito.doReturn;
43import static org.mockito.Mockito.times;
44import static org.mockito.Mockito.verify;
45import static org.mockito.Mockito.mock;
46import static org.mockito.Mockito.any;
47import static org.mockito.Mockito.anyChar;
48import static org.mockito.Mockito.anyString;
49
50public class CallManagerTest extends TelephonyTest {
51
52    @Mock
53    GsmCdmaCall mFgCall;
54    @Mock
55    GsmCdmaCall mBgCall;
56    @Mock
57    GsmCdmaCall mRingingCall;
58    @Mock
59    Phone mSecondPhone;
60
61    private CallManagerHandlerThread mCallManagerHandlerThread;
62    private Handler mHandler;
63    private static final int PHONE_REGISTER_EVENT = 0;
64
65    private class CallManagerHandlerThread extends HandlerThread {
66        private CallManagerHandlerThread(String name) {
67            super(name);
68        }
69        @Override
70        public void onLooperPrepared() {
71            /* CallManager is a static object with private constructor,no need call constructor */
72            registerForPhone(mPhone);
73
74            // create a custom handler for the Handler Thread
75            mHandler = new Handler(mCallManagerHandlerThread.getLooper()) {
76                @Override
77                public void handleMessage(Message msg) {
78                    switch (msg.what) {
79                        case PHONE_REGISTER_EVENT:
80                            logd("Phone registered with CallManager");
81                            registerForPhone((Phone) msg.obj);
82                            setReady(true);
83                            break;
84                        default:
85                            logd("Unknown Event " + msg.what);
86                    }
87                }
88            };
89
90            setReady(true);
91        }
92
93        private void registerForPhone(Phone mPhone) {
94            CallManager.getInstance().registerPhone(mPhone);
95        }
96    }
97
98    @Before
99    public void setUp() throws Exception {
100        super.setUp(this.getClass().getSimpleName());
101        restoreInstance(CallManager.class, "INSTANCE", null);
102        /* Mock Phone and Call, initially all calls are idle */
103        doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
104        doReturn(mBgCall).when(mPhone).getBackgroundCall();
105        doReturn(mFgCall).when(mPhone).getForegroundCall();
106        doReturn(mRingingCall).when(mPhone).getRingingCall();
107        doReturn(mPhone).when(mBgCall).getPhone();
108        doReturn(mPhone).when(mFgCall).getPhone();
109        doReturn(mPhone).when(mRingingCall).getPhone();
110        doReturn(Call.State.IDLE).when(mBgCall).getState();
111        doReturn(Call.State.IDLE).when(mFgCall).getState();
112        doReturn(Call.State.IDLE).when(mRingingCall).getState();
113        doReturn(true).when(mBgCall).isIdle();
114        doReturn(true).when(mFgCall).isIdle();
115        doReturn(true).when(mRingingCall).isIdle();
116
117        mCallManagerHandlerThread = new CallManagerHandlerThread(TAG);
118        mCallManagerHandlerThread.start();
119        waitUntilReady();
120    }
121
122    @After
123    public void tearDown() throws Exception {
124        CallManager.getInstance().unregisterPhone(mPhone);
125        mCallManagerHandlerThread.quit();
126        super.tearDown();
127    }
128
129    @SmallTest @Test
130    public void testSanity() {
131        assertEquals(mPhone, CallManager.getInstance().getDefaultPhone());
132        assertFalse(CallManager.getInstance().hasActiveBgCall());
133        assertFalse(CallManager.getInstance().hasActiveRingingCall());
134        assertFalse(CallManager.getInstance().hasActiveFgCall());
135        /* return the default phone if there is no any active call */
136        assertEquals(mPhone, CallManager.getInstance().getRingingPhone());
137        assertEquals(mPhone, CallManager.getInstance().getBgPhone());
138        assertEquals(mPhone, CallManager.getInstance().getFgPhone());
139    }
140
141    @SmallTest @Test
142    public void testBasicDial() throws Exception {
143        //verify can dial and dial function of the phone is being triggered
144        CallManager.getInstance().dial(mPhone,
145                PhoneNumberUtils.stripSeparators("+17005554141"), 0);
146        ArgumentCaptor<String> mCaptorString = ArgumentCaptor.forClass(String.class);
147        ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsCaptor =
148                ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
149        verify(mPhone, times(1)).dial(mCaptorString.capture(), dialArgsCaptor.capture());
150        assertEquals(PhoneNumberUtils.stripSeparators("+17005554141"),
151                mCaptorString.getValue());
152        assertEquals(0, dialArgsCaptor.getValue().videoState);
153    }
154
155    @SmallTest @Test
156    public void testBasicAcceptCall() throws Exception {
157        CallManager.getInstance().acceptCall(mRingingCall);
158        verify(mPhone, times(1)).acceptCall(anyInt());
159    }
160
161    @SmallTest @Test
162    public void testBasicRejectCall() throws Exception {
163        //verify can dial and dial function of the phone is being triggered
164        CallManager.getInstance().rejectCall(mRingingCall);
165        verify(mPhone, times(1)).rejectCall();
166    }
167
168    @SmallTest @Test
169    public void testSendDtmf() throws Exception {
170        CallManager.getInstance().sendDtmf('a');
171        verify(mPhone, times(0)).sendDtmf(eq('a'));
172
173        //has active fg Call
174        doReturn(false).when(mFgCall).isIdle();
175        assertEquals(mFgCall, CallManager.getInstance().getActiveFgCall());
176        CallManager.getInstance().sendDtmf('a');
177        verify(mPhone, times(1)).sendDtmf(eq('a'));
178    }
179
180    @SmallTest @Test
181    public void testStartDtmf() throws Exception {
182        doReturn(true).when(mFgCall).isIdle();
183        assertFalse(CallManager.getInstance().startDtmf('a'));
184        verify(mPhone, times(0)).startDtmf(anyChar());
185
186        //has active fg Call
187        doReturn(false).when(mFgCall).isIdle();
188        assertEquals(mFgCall, CallManager.getInstance().getActiveFgCall());
189        assertTrue(CallManager.getInstance().startDtmf('a'));
190        verify(mPhone, times(1)).startDtmf('a');
191    }
192
193    @SmallTest @Test
194    public void testStopDtmf() throws Exception {
195        doReturn(true).when(mFgCall).isIdle();
196        CallManager.getInstance().stopDtmf();
197        verify(mPhone, times(0)).stopDtmf();
198
199        //has active fg Call
200        doReturn(false).when(mFgCall).isIdle();
201        assertEquals(mPhone, CallManager.getInstance().getFgPhone());
202        CallManager.getInstance().stopDtmf();
203        verify(mPhone, times(1)).stopDtmf();
204    }
205
206    @SmallTest @Test
207    public void testSendBurstDtmf() throws Exception {
208        doReturn(true).when(mFgCall).isIdle();
209        assertFalse(CallManager.getInstance().sendBurstDtmf("12*#", 0, 0, null));
210        verify(mPhone, times(0)).sendBurstDtmf(anyString(), anyInt(), anyInt(), (Message) any());
211
212        //has active fg Call
213        doReturn(false).when(mFgCall).isIdle();
214        assertTrue(CallManager.getInstance().sendBurstDtmf("12*#", 0, 0, null));
215        verify(mPhone, times(1)).sendBurstDtmf("12*#", 0, 0, null);
216    }
217
218    @SmallTest @Test
219    public void testSetGetMute() throws Exception {
220        CallManager.getInstance().setMute(false);
221        verify(mPhone, times(0)).setMute(anyBoolean());
222
223        //has active fg Call
224        doReturn(false).when(mFgCall).isIdle();
225        CallManager.getInstance().setMute(false);
226        verify(mPhone, times(1)).setMute(false);
227
228        CallManager.getInstance().setMute(true);
229        verify(mPhone, times(1)).setMute(true);
230    }
231
232    @SmallTest @Test
233    public void testSwitchHoldingAndActive() throws Exception {
234        /* case 1: only active call */
235        doReturn(false).when(mFgCall).isIdle();
236        CallManager.getInstance().switchHoldingAndActive(null);
237        verify(mPhone, times(1)).switchHoldingAndActive();
238        /* case 2: no active call but only held call, aka, unhold */
239        doReturn(true).when(mFgCall).isIdle();
240        CallManager.getInstance().switchHoldingAndActive(mBgCall);
241        verify(mPhone, times(2)).switchHoldingAndActive();
242        /* case 3: both active and held calls from same phone, aka, swap */
243        doReturn(false).when(mFgCall).isIdle();
244        CallManager.getInstance().switchHoldingAndActive(mBgCall);
245        verify(mPhone, times(3)).switchHoldingAndActive();
246        GsmCdmaPhone mPhoneHold = Mockito.mock(GsmCdmaPhone.class);
247        /* case 4: active and held calls from different phones, aka, phone swap */
248        doReturn(mPhoneHold).when(mBgCall).getPhone();
249        CallManager.getInstance().switchHoldingAndActive(mBgCall);
250        verify(mPhone, times(4)).switchHoldingAndActive();
251        verify(mPhoneHold, times(1)).switchHoldingAndActive();
252    }
253
254    @SmallTest @Test
255    public void testHangupForegroundResumeBackground() throws Exception {
256        CallManager.getInstance().hangupForegroundResumeBackground(mBgCall);
257        /* no active fgCall */
258        verify(mPhone, times(0)).switchHoldingAndActive();
259        verify(mFgCall, times(0)).hangup();
260
261        /* have active foreground call, get hanged up */
262        doReturn(false).when(mFgCall).isIdle();
263        CallManager.getInstance().hangupForegroundResumeBackground(mBgCall);
264        verify(mFgCall, times(1)).hangup();
265        verify(mPhone, times(0)).switchHoldingAndActive();
266
267        /* mock bgcall and fgcall from different phone */
268        GsmCdmaPhone mPhoneHold = Mockito.mock(GsmCdmaPhone.class);
269        doReturn(mPhoneHold).when(mBgCall).getPhone();
270        CallManager.getInstance().hangupForegroundResumeBackground(mBgCall);
271        verify(mFgCall, times(2)).hangup();
272        /* always hangup fgcall and both phone trigger swap */
273        verify(mPhoneHold, times(1)).switchHoldingAndActive();
274        verify(mPhone, times(1)).switchHoldingAndActive();
275    }
276
277    @SmallTest @Test
278    public void testFgCallActiveDial() throws Exception {
279        /* set Fg/Bg Call state to active, verify CallManager Logical */
280        doReturn(false).when(mFgCall).isIdle();
281        doReturn(false).when(mBgCall).isIdle();
282        assertTrue(CallManager.getInstance().hasActiveFgCall());
283        assertTrue(CallManager.getInstance().hasActiveBgCall());
284        assertTrue(CallManager.getInstance().hasActiveFgCall(mPhone.getSubId()));
285        assertFalse(CallManager.getInstance().hasDisconnectedFgCall());
286        /* try dial with non-idle foreground call and background call */
287        CallManager.getInstance().dial(mPhone,
288                PhoneNumberUtils.stripSeparators("+17005554141"), 0);
289        ArgumentCaptor<String> mCaptorString = ArgumentCaptor.forClass(String.class);
290        ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsCaptor =
291                ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
292
293        verify(mPhone, times(1)).dial(mCaptorString.capture(), dialArgsCaptor.capture());
294        assertEquals(PhoneNumberUtils.stripSeparators("+17005554141"),
295                mCaptorString.getValue());
296        assertEquals(0, dialArgsCaptor.getValue().videoState);
297    }
298
299    @Test @SmallTest
300    public void testRegisterEvent() throws Exception {
301        Field field = CallManager.class.getDeclaredField("EVENT_CALL_WAITING");
302        field.setAccessible(true);
303        int mEvent = (Integer) field.get(CallManager.getInstance());
304        verify(mPhone, times(1)).registerForCallWaiting(isA(Handler.class),
305                eq(mEvent), isNull());
306
307        field = CallManager.class.getDeclaredField("EVENT_PRECISE_CALL_STATE_CHANGED");
308        field.setAccessible(true);
309        mEvent = (Integer) field.get(CallManager.getInstance());
310        verify(mPhone, times(1)).registerForPreciseCallStateChanged(isA(Handler.class),
311                eq(mEvent), isA(Object.class));
312
313        field = CallManager.class.getDeclaredField("EVENT_RINGBACK_TONE");
314        field.setAccessible(true);
315        mEvent = (Integer) field.get(CallManager.getInstance());
316        verify(mPhone, times(1)).registerForRingbackTone(isA(Handler.class),
317                eq(mEvent), isA(Object.class));
318    }
319
320    @Test @SmallTest
321    public void testGetServiceState() throws Exception {
322        // register for another phone
323        ServiceState mSecondServiceState = mock(ServiceState.class);
324        doReturn(mSecondServiceState).when(mSecondPhone).getServiceState();
325
326        Message mRegisterPhone = mHandler.obtainMessage(PHONE_REGISTER_EVENT,
327                mSecondPhone);
328        setReady(false);
329        mRegisterPhone.sendToTarget();
330
331        waitUntilReady();
332
333        // mPhone: STATE_IN_SERVICE > mPhoneSecond: state STATE_OUT_OF_SERVICE
334        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSecondServiceState).getState();
335        assertEquals(ServiceState.STATE_IN_SERVICE, CallManager.getInstance().getServiceState());
336
337        // mPhone: STATE_IN_SERVICE > mPhoneSecond: state STATE_EMERGENCY_ONLY
338        doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mSecondServiceState).getState();
339        assertEquals(ServiceState.STATE_IN_SERVICE, CallManager.getInstance().getServiceState());
340
341        // mPhone: STATE_IN_SERVICE > mPhoneSecond: state STATE_POWER_OFF
342        doReturn(ServiceState.STATE_POWER_OFF).when(mSecondServiceState).getState();
343        assertEquals(ServiceState.STATE_IN_SERVICE, CallManager.getInstance().getServiceState());
344
345        // mPhone: STATE_EMERGENCY_ONLY < mPhoneSecond: state STATE_OUT_OF_SERVICE
346        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSecondServiceState).getState();
347        doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState();
348        assertEquals(ServiceState.STATE_OUT_OF_SERVICE,
349                CallManager.getInstance().getServiceState());
350
351        // mPhone: STATE_POWER_OFF < mPhoneSecond: state STATE_OUT_OF_SERVICE
352        doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getState();
353        assertEquals(ServiceState.STATE_OUT_OF_SERVICE,
354                CallManager.getInstance().getServiceState());
355
356        /* mPhone: STATE_POWER_OFF < mPhoneSecond: state STATE_EMERGENCY_ONLY
357           but OUT_OF_SERVICE will replaces EMERGENCY_ONLY and POWER_OFF */
358        doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mSecondServiceState).getState();
359        assertEquals(ServiceState.STATE_OUT_OF_SERVICE,
360                CallManager.getInstance().getServiceState());
361        CallManager.getInstance().unregisterPhone(mSecondPhone);
362    }
363}
364