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 static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
19
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertNotNull;
22import static org.mockito.Mockito.any;
23import static org.mockito.Mockito.anyInt;
24import static org.mockito.Mockito.doReturn;
25import static org.mockito.Mockito.eq;
26import static org.mockito.Mockito.isA;
27import static org.mockito.Mockito.times;
28import static org.mockito.Mockito.verify;
29
30import android.os.Handler;
31import android.os.HandlerThread;
32import android.os.Message;
33import android.support.test.filters.FlakyTest;
34import android.telephony.DisconnectCause;
35import android.telephony.PhoneNumberUtils;
36import android.telephony.ServiceState;
37import android.test.suitebuilder.annotation.MediumTest;
38import android.test.suitebuilder.annotation.SmallTest;
39
40import org.junit.After;
41import org.junit.Assert;
42import org.junit.Before;
43import org.junit.Ignore;
44import org.junit.Test;
45import org.mockito.ArgumentCaptor;
46import org.mockito.Mock;
47
48public class GsmCdmaCallTrackerTest extends TelephonyTest {
49    private static final int VOICE_CALL_STARTED_EVENT = 0;
50    private static final int VOICE_CALL_ENDED_EVENT = 1;
51    private String mDialString = PhoneNumberUtils.stripSeparators("+17005554141");
52    /* Handler class initiated at the HandlerThread */
53    private GsmCdmaCallTracker mCTUT;
54    private GsmCdmaCTHandlerThread mGsmCdmaCTHandlerThread;
55    @Mock
56    GsmCdmaCall mCall;
57    @Mock
58    private Handler mHandler;
59
60    private class GsmCdmaCTHandlerThread extends HandlerThread {
61
62        private GsmCdmaCTHandlerThread(String name) {
63            super(name);
64        }
65        @Override
66        public void onLooperPrepared() {
67            mCTUT = new GsmCdmaCallTracker(mPhone);
68            setReady(true);
69        }
70    }
71
72    @Before
73    public void setUp() throws Exception {
74        super.setUp(this.getClass().getSimpleName());
75        mSimulatedCommands.setRadioPower(true, null);
76        mPhone.mCi = this.mSimulatedCommands;
77
78        mGsmCdmaCTHandlerThread = new GsmCdmaCTHandlerThread(TAG);
79        mGsmCdmaCTHandlerThread.start();
80
81        waitUntilReady();
82        logd("GsmCdmaCallTracker initiated, waiting for Power on");
83        /* Make sure radio state is power on before dial.
84         * When radio state changed from off to on, CallTracker
85         * will poll result from RIL. Avoid dialing triggered at the same*/
86        waitForMs(100);
87    }
88
89    @After
90    public void tearDown() throws Exception {
91        mCTUT = null;
92        mGsmCdmaCTHandlerThread.quit();
93        super.tearDown();
94    }
95
96    @Test
97    @SmallTest
98    public void testMOCallDial() {
99        doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
100        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
101        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
102        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
103        assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
104        try {
105            mCTUT.dial(mDialString);
106            waitForMs(100);
107        } catch(Exception ex) {
108            ex.printStackTrace();
109            Assert.fail("unexpected exception thrown"+ex.getMessage()+ex.getStackTrace());
110        }
111
112        assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
113        assertEquals(GsmCdmaCall.State.DIALING, mCTUT.mForegroundCall.getState());
114        assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
115        /* verify the command is sent out to RIL */
116        verify(mSimulatedCommandsVerifier).dial(eq(PhoneNumberUtils.
117                        extractNetworkPortionAlt(mDialString)), anyInt(),
118                eq((UUSInfo) null),
119                isA(Message.class));
120    }
121
122    @Test
123    @SmallTest
124    public void testMOCallPickUp() {
125        testMOCallDial();
126        logd("Waiting for POLL CALL response from RIL");
127        TelephonyTestUtils.waitForMs(50);
128        logd("Pick Up MO call, expecting call state change event ");
129        mSimulatedCommands.progressConnectingToActive();
130        waitForMs(100);
131        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
132        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
133    }
134
135    @FlakyTest
136    @Ignore
137    @Test
138    @MediumTest
139    public void testMOCallHangup() {
140        testMOCallDial();
141        logd("Waiting for POLL CALL response from RIL ");
142        TelephonyTestUtils.waitForMs(50);
143        assertEquals(GsmCdmaCall.State.DIALING, mCTUT.mForegroundCall.getState());
144        assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
145        assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
146        logd("Hang up MO call after MO call established ");
147        try {
148            mCTUT.hangup(mCTUT.mForegroundCall);
149        } catch(Exception ex) {
150            ex.printStackTrace();
151            Assert.fail("unexpected exception thrown" + ex.getMessage());
152        }
153        waitForMs(300);
154        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
155        assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
156        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
157    }
158
159    @FlakyTest
160    @Ignore
161    @Test
162    @MediumTest
163    public void testMOCallDialPickUpHangup() {
164        testMOCallPickUp();
165        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
166        assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
167        assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
168         /* get the reference of the connection before reject */
169        Connection mConnection = mCTUT.mForegroundCall.getConnections().get(0);
170        assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
171        logd("hang up MO call after pickup");
172        try {
173            mCTUT.hangup(mCTUT.mForegroundCall);
174        } catch(Exception ex) {
175            ex.printStackTrace();
176            Assert.fail("unexpected exception thrown" + ex.getMessage());
177        }
178        /* request send to RIL still in disconnecting state */
179        waitForMs(300);
180        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
181        assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
182        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
183        assertEquals(DisconnectCause.LOCAL, mConnection.getDisconnectCause());
184
185    }
186
187    @FlakyTest
188    @Test
189    @MediumTest
190    public void testMOCallPendingHangUp() {
191        testMOCallDial();
192        logd("MO call hangup before established[ getting result from RIL ]");
193        /* poll call result from RIL, find that there is a pendingMO call,
194         * Didn't do anything for hangup, clear during handle poll result */
195        try {
196            mCTUT.hangup(mCTUT.mForegroundCall);
197        } catch(Exception ex) {
198            ex.printStackTrace();
199            Assert.fail("unexpected exception thrown" + ex.getMessage());
200        }
201        waitForMs(300);
202        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
203        assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
204        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
205    }
206
207    @Test
208    @MediumTest
209    public void testMOCallSwitch() {
210        testMOCallPickUp();
211        logd("MO call picked up, initiating a new MO call");
212        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
213        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
214        assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
215        assertEquals(0, mCTUT.mBackgroundCall.getConnections().size());
216
217        String mDialString = PhoneNumberUtils.stripSeparators("+17005554142");
218        try {
219            mCTUT.dial(mDialString);
220        } catch(Exception ex) {
221            ex.printStackTrace();
222            Assert.fail("unexpected exception thrown" + ex.getMessage());
223        }
224        waitForMs(200);
225        assertEquals(GsmCdmaCall.State.DIALING, mCTUT.mForegroundCall.getState());
226        assertEquals(GsmCdmaCall.State.HOLDING, mCTUT.mBackgroundCall.getState());
227        assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
228        assertEquals(1, mCTUT.mBackgroundCall.getConnections().size());
229
230    }
231
232    @Test
233    @SmallTest
234    @FlakyTest
235    @Ignore
236    public void testMTCallRinging() {
237        /* Mock there is a MT call mRinging call and try to accept this MT call */
238        /* if we got a active state followed by another MT call-> move to background call */
239        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
240        assertEquals(0, mCTUT.mRingingCall.getConnections().size());
241        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
242        String mDialString = PhoneNumberUtils.stripSeparators("+17005554141");
243        logd("MT call Ringing");
244        mSimulatedCommands.triggerRing(mDialString);
245        waitForMs(50);
246        assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
247        assertEquals(1, mCTUT.mRingingCall.getConnections().size());
248    }
249
250    @Test
251    @SmallTest
252    @FlakyTest
253    @Ignore
254    public void testMTCallAccept() {
255        testMTCallRinging();
256        assertEquals(mCTUT.mForegroundCall.getConnections().size(),0);
257        logd("accept the MT call");
258        try{
259            mCTUT.acceptCall();
260        } catch(Exception ex) {
261            ex.printStackTrace();
262            Assert.fail("unexpected exception thrown" + ex.getMessage());
263        }
264        verify(mSimulatedCommandsVerifier).acceptCall(isA(Message.class));
265        /* send to the RIL */
266        TelephonyTestUtils.waitForMs(50);
267        assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
268        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
269        assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
270        assertEquals(0, mCTUT.mRingingCall.getConnections().size());
271    }
272
273    @Test
274    @SmallTest
275    public void testMTCallReject() {
276        testMTCallRinging();
277        logd("MT call ringing and rejected ");
278        /* get the reference of the connection before reject */
279        Connection mConnection = mCTUT.mRingingCall.getConnections().get(0);
280        assertNotNull(mConnection);
281        assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
282        try {
283            mCTUT.rejectCall();
284        } catch(Exception ex) {
285            ex.printStackTrace();
286            Assert.fail("unexpected exception thrown" + ex.getMessage());
287        }
288        waitForMs(50);
289        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
290        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
291        assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
292        /* ? why rejectCall didnt -> hang up locally to set the cause to LOCAL? */
293        assertEquals(DisconnectCause.INCOMING_MISSED, mConnection.getDisconnectCause());
294
295    }
296
297    @Test
298    @MediumTest
299    public void testMOCallSwitchHangupForeGround() {
300        testMOCallSwitch();
301        logd("Hang up the foreground MO call while dialing ");
302        try {
303            mCTUT.hangup(mCTUT.mForegroundCall);
304        } catch(Exception ex) {
305            ex.printStackTrace();
306            Assert.fail("unexpected exception thrown" + ex.getMessage());
307        }
308        waitForMs(300);
309        logd(" Foreground Call is IDLE and BackGround Call is still HOLDING ");
310        /* if we want to hang up foreground call which is alerting state, hangup all */
311        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
312        assertEquals(GsmCdmaCall.State.HOLDING, mCTUT.mBackgroundCall.getState());
313    }
314
315    @FlakyTest
316    @Ignore
317    @Test
318    @MediumTest
319    public void testMOCallPickUpHangUpResumeBackGround() {
320        testMOCallSwitch();
321        logd("Pick up the new MO Call");
322        try{
323            mSimulatedCommands.progressConnectingToActive();
324        } catch(Exception ex) {
325            ex.printStackTrace();
326            Assert.fail("unexpected exception thrown" + ex.getMessage());
327        }
328
329        waitForMs(200);
330        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
331        assertEquals(GsmCdmaCall.State.HOLDING, mCTUT.mBackgroundCall.getState());
332
333        logd("Hang up the new MO Call");
334        try {
335            mCTUT.hangup(mCTUT.mForegroundCall);
336        } catch(Exception ex) {
337            ex.printStackTrace();
338            Assert.fail("unexpected exception thrown" + ex.getMessage());
339        }
340
341        waitForMs(300);
342        logd(" BackGround Call switch to ForeGround Call ");
343        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
344        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
345    }
346
347    @Test @SmallTest
348    public void testVoiceCallStartListener(){
349        logd("register for voice call started event");
350        mCTUT.registerForVoiceCallStarted(mHandler, VOICE_CALL_STARTED_EVENT, null);
351        logd("voice call started");
352        testMOCallPickUp();
353        ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
354        ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
355        verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
356        assertEquals(VOICE_CALL_STARTED_EVENT, mCaptorMessage.getValue().what);
357
358    }
359
360    @FlakyTest
361    @Ignore
362    @Test @SmallTest
363    public void testVoiceCallEndedListener(){
364        logd("register for voice call ended event");
365        mCTUT.registerForVoiceCallEnded(mHandler, VOICE_CALL_ENDED_EVENT, null);
366        ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
367        ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
368        testMOCallHangup();
369        verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
370        assertEquals(VOICE_CALL_ENDED_EVENT, mCaptorMessage.getValue().what);
371    }
372
373    @Test @SmallTest
374    public void testUpdatePhoneType() {
375        // verify getCurrentCalls is called on init
376        verify(mSimulatedCommandsVerifier).getCurrentCalls(any(Message.class));
377
378        // update phone type (call the function on same thread as the call tracker)
379        Handler updatePhoneTypeHandler = new Handler(mCTUT.getLooper()) {
380            @Override
381            public void handleMessage(Message msg) {
382                mCTUT.updatePhoneType();
383            }
384        };
385        updatePhoneTypeHandler.sendEmptyMessage(0);
386        waitForMs(100);
387
388        // verify getCurrentCalls is called on updating phone type
389        verify(mSimulatedCommandsVerifier, times(2)).getCurrentCalls(any(Message.class));
390
391        // we'd like to verify that if phone type is updated, calls and callTracker go to idle.
392        // However, as soon as phone type is updated, call tracker queries for calls from RIL and
393        // will go back to OFFHOOK
394
395        // call tracker goes to OFFHOOK
396        testMOCallPickUp();
397
398        // update phone type - call tracker goes to IDLE and then due to getCurrentCalls(),
399        // goes back to OFFHOOK
400        updatePhoneTypeHandler.sendEmptyMessage(0);
401        waitForMs(100);
402
403        // verify CT and calls go to idle
404        assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
405        assertEquals(GsmCdmaCall.State.ACTIVE, mCTUT.mForegroundCall.getState());
406        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
407        assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mRingingCall.getState());
408    }
409}
410
411