Fixed file descriptor leak in telephony unit tests
[android/platform/frameworks/opt/telephony.git] / tests / telephonytests / src / com / android / internal / telephony / imsphone / ImsPhoneCallTrackerTest.java
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 package com.android.internal.telephony.imsphone;
17
18 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.ArgumentMatchers.nullable;
24 import static org.mockito.Mockito.any;
25 import static org.mockito.Mockito.anyBoolean;
26 import static org.mockito.Mockito.anyInt;
27 import static org.mockito.Mockito.doAnswer;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.doThrow;
30 import static org.mockito.Mockito.eq;
31 import static org.mockito.Mockito.isNull;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36
37 import android.app.PendingIntent;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.os.Bundle;
41 import android.os.Handler;
42 import android.os.HandlerThread;
43 import android.os.Message;
44 import android.support.test.filters.FlakyTest;
45 import android.telephony.PhoneNumberUtils;
46 import android.telephony.ims.feature.ImsFeature;
47 import android.test.suitebuilder.annotation.SmallTest;
48
49 import com.android.ims.ImsCall;
50 import com.android.ims.ImsCallProfile;
51 import com.android.ims.ImsConfig;
52 import com.android.ims.ImsConnectionStateListener;
53 import com.android.ims.ImsException;
54 import com.android.ims.ImsManager;
55 import com.android.ims.ImsReasonInfo;
56 import com.android.ims.ImsServiceClass;
57 import com.android.ims.internal.ImsCallSession;
58 import com.android.internal.telephony.Call;
59 import com.android.internal.telephony.Connection;
60 import com.android.internal.telephony.PhoneConstants;
61 import com.android.internal.telephony.TelephonyTest;
62
63 import org.junit.After;
64 import org.junit.Assert;
65 import org.junit.Before;
66 import org.junit.Ignore;
67 import org.junit.Test;
68 import org.mockito.Mock;
69 import org.mockito.invocation.InvocationOnMock;
70 import org.mockito.stubbing.Answer;
71
72 public class ImsPhoneCallTrackerTest extends TelephonyTest {
73     private ImsPhoneCallTracker mCTUT;
74     private ImsCTHandlerThread mImsCTHandlerThread;
75     private ImsConnectionStateListener mImsConnectionStateListener;
76     private ImsCall.Listener mImsCallListener;
77     private ImsCall mImsCall;
78     private ImsCall mSecondImsCall;
79     private Bundle mBundle = new Bundle();
80     private int mServiceId;
81     @Mock
82     private ImsCallSession mImsCallSession;
83     private Handler mCTHander;
84
85     private class ImsCTHandlerThread extends HandlerThread {
86
87         private ImsCTHandlerThread(String name) {
88             super(name);
89         }
90         @Override
91         public void onLooperPrepared() {
92             mCTUT = new ImsPhoneCallTracker(mImsPhone);
93             mCTUT.addReasonCodeRemapping(null, "Wifi signal lost.", ImsReasonInfo.CODE_WIFI_LOST);
94             mCTUT.addReasonCodeRemapping(501, "Call answered elsewhere.",
95                     ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
96             mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.",
97                     ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
98             mCTHander = new Handler(mCTUT.getLooper());
99             setReady(true);
100         }
101     }
102
103     private void imsCallMocking(final ImsCall mImsCall) throws Exception {
104
105         doAnswer(new Answer<Void>() {
106             @Override
107             public Void answer(InvocationOnMock invocation) throws Throwable {
108                 // trigger the listener on accept call
109                 if (mImsCallListener != null) {
110                     mImsCallListener.onCallStarted(mImsCall);
111                 }
112                 return null;
113             }
114         }).when(mImsCall).accept(anyInt());
115
116         doAnswer(new Answer<Void>() {
117             @Override
118             public Void answer(InvocationOnMock invocation) throws Throwable {
119                 // trigger the listener on reject call
120                 int reasonCode = (int) invocation.getArguments()[0];
121                 if (mImsCallListener != null) {
122                     mImsCallListener.onCallStartFailed(mImsCall, new ImsReasonInfo(reasonCode, -1));
123                     mImsCallListener.onCallTerminated(mImsCall, new ImsReasonInfo(reasonCode, -1));
124                 }
125                 return null;
126             }
127         }).when(mImsCall).reject(anyInt());
128
129         doAnswer(new Answer<Void>() {
130             @Override
131             public Void answer(InvocationOnMock invocation) throws Throwable {
132                 // trigger the listener on reject call
133                 int reasonCode = (int) invocation.getArguments()[0];
134                 if (mImsCallListener != null) {
135                     mImsCallListener.onCallTerminated(mImsCall, new ImsReasonInfo(reasonCode, -1));
136                 }
137                 return null;
138             }
139         }).when(mImsCall).terminate(anyInt());
140
141         doAnswer(new Answer<Void>() {
142             @Override
143             public Void answer(InvocationOnMock invocation) throws Throwable {
144                 if (mImsCallListener != null) {
145                     mImsCallListener.onCallHeld(mImsCall);
146                 }
147                 return null;
148             }
149         }).when(mImsCall).hold();
150
151         doReturn(mImsCallSession).when(mImsCall).getCallSession();
152     }
153
154     @Before
155     public void setUp() throws Exception {
156         super.setUp(this.getClass().getSimpleName());
157         mServiceId = 0;
158         mImsCallProfile.mCallExtras = mBundle;
159         mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager);
160         mImsCall = spy(new ImsCall(mContext, mImsCallProfile));
161         mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
162         imsCallMocking(mImsCall);
163         imsCallMocking(mSecondImsCall);
164         doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus();
165         doReturn(mImsCallProfile).when(mImsManager).createCallProfile(eq(mServiceId),
166                 anyInt(), anyInt());
167
168         //cache the listener
169         doAnswer(new Answer<Integer>() {
170             @Override
171             public Integer answer(InvocationOnMock invocation) throws Throwable {
172                 mImsConnectionStateListener =
173                         (ImsConnectionStateListener) invocation.getArguments()[2];
174                 return mServiceId;
175             }
176         }).when(mImsManager).open(anyInt(), (PendingIntent) any(),
177                 (ImsConnectionStateListener) any());
178
179         doAnswer(new Answer<ImsCall>() {
180             @Override
181             public ImsCall answer(InvocationOnMock invocation) throws Throwable {
182                 mImsCallListener =
183                         (ImsCall.Listener) invocation.getArguments()[2];
184                 return mImsCall;
185             }
186         }).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(), (ImsCall.Listener) any());
187
188         doAnswer(new Answer<ImsCall>() {
189             @Override
190             public ImsCall answer(InvocationOnMock invocation) throws Throwable {
191                 mImsCallListener =
192                         (ImsCall.Listener) invocation.getArguments()[3];
193                 return mSecondImsCall;
194             }
195         }).when(mImsManager).makeCall(eq(mServiceId), eq(mImsCallProfile), (String []) any(),
196                 (ImsCall.Listener) any());
197
198         mImsCTHandlerThread = new ImsCTHandlerThread(this.getClass().getSimpleName());
199         mImsCTHandlerThread.start();
200
201         waitUntilReady();
202         logd("ImsPhoneCallTracker initiated");
203         /* Make sure getImsService is triggered on handler */
204         waitForHandlerAction(mCTHander, 100);
205     }
206
207     @After
208     public void tearDown() throws Exception {
209         mCTUT = null;
210         mImsCTHandlerThread.quit();
211         super.tearDown();
212     }
213
214     @Test
215     @SmallTest
216     public void testImsFeatureCapabilityChange() {
217         int[] featureEnableArray = {-1, -1, -1, -1, -1, -1},
218                 featureDisableArray = {-1, -1, -1, -1, -1, -1};
219         assertFalse(mCTUT.isVolteEnabled());
220         assertFalse(mCTUT.isVideoCallEnabled());
221         //enable VoLTE feature
222         featureEnableArray[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] =
223                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
224         mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
225                 featureEnableArray,
226                 featureDisableArray);
227         assertTrue(mCTUT.isVolteEnabled());
228         assertFalse(mCTUT.isVideoCallEnabled());
229         // video call not enabled
230         verify(mImsPhone, times(0)).notifyForVideoCapabilityChanged(anyBoolean());
231         verify(mImsPhone, times(1)).onFeatureCapabilityChanged();
232         // enable video call
233         featureEnableArray[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] =
234                 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE;
235         mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
236                 featureEnableArray,
237                 featureDisableArray);
238         assertTrue(mCTUT.isVideoCallEnabled());
239         verify(mImsPhone, times(1)).notifyForVideoCapabilityChanged(eq(true));
240     }
241
242     @Test
243     @SmallTest
244     public void testImsMTCall() {
245         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
246         assertFalse(mCTUT.mRingingCall.isRinging());
247         // mock a MT call
248         Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
249         mContext.sendBroadcast(mIntent);
250         verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
251         verify(mImsPhone, times(1)).notifyIncomingRing();
252         assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
253         assertTrue(mCTUT.mRingingCall.isRinging());
254         assertEquals(1, mCTUT.mRingingCall.getConnections().size());
255     }
256
257     @Test
258     @SmallTest
259     public void testImsMTCallAccept() {
260         testImsMTCall();
261         assertTrue(mCTUT.mRingingCall.isRinging());
262         try {
263             mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
264             verify(mImsCall, times(1)).accept(eq(ImsCallProfile
265                     .getCallTypeFromVideoState(ImsCallProfile.CALL_TYPE_VOICE)));
266         } catch (Exception ex) {
267             ex.printStackTrace();
268             Assert.fail("unexpected exception thrown" + ex.getMessage());
269         }
270         assertFalse(mCTUT.mRingingCall.isRinging());
271         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
272         assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
273         assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
274     }
275
276     @Test
277     @SmallTest
278     public void testImsMTCallReject() {
279         testImsMTCall();
280         assertTrue(mCTUT.mRingingCall.isRinging());
281         try {
282             mCTUT.rejectCall();
283             verify(mImsCall, times(1)).reject(eq(ImsReasonInfo.CODE_USER_DECLINE));
284         } catch (Exception ex) {
285             ex.printStackTrace();
286             Assert.fail("unexpected exception thrown" + ex.getMessage());
287         }
288         assertFalse(mCTUT.mRingingCall.isRinging());
289         assertEquals(0, mCTUT.mRingingCall.getConnections().size());
290         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
291     }
292
293     @Test
294     @SmallTest
295     public void testImsMTCallAcceptHangUp() {
296         testImsMTCallAccept();
297         assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
298         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
299         try {
300             mCTUT.hangup(mCTUT.mForegroundCall);
301         } catch (Exception ex) {
302             ex.printStackTrace();
303             Assert.fail("unexpected exception thrown" + ex.getMessage());
304         }
305         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
306         assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
307     }
308
309     @Test
310     @SmallTest
311     public void testImsMTCallAcceptHold() {
312         testImsMTCallAccept();
313
314         assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
315         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
316         // mock a new MT
317         try {
318             doReturn(mSecondImsCall).when(mImsManager).takeCall(eq(0), (Intent) any(),
319                     (ImsCall.Listener) any());
320         } catch (Exception ex) {
321             ex.printStackTrace();
322             Assert.fail("unexpected exception thrown" + ex.getMessage());
323         }
324         Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
325         mContext.sendBroadcast(mIntent);
326
327         verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
328         verify(mImsPhone, times(2)).notifyIncomingRing();
329         assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
330         assertEquals(ImsPhoneCall.State.WAITING, mCTUT.mRingingCall.getState());
331         assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
332
333         //hold the foreground active call, accept the new ringing call
334         try {
335             mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
336             verify(mImsCall, times(1)).hold();
337         } catch (Exception ex) {
338             ex.printStackTrace();
339             Assert.fail("unexpected exception thrown" + ex.getMessage());
340         }
341
342         waitForMs(100);
343         assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
344         assertFalse(mCTUT.mRingingCall.isRinging());
345         assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
346     }
347
348     @FlakyTest
349     @Test
350     @SmallTest
351     public void testImsMOCallDial() {
352         assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
353         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
354         try {
355             mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
356             verify(mImsManager, times(1)).makeCall(eq(0), eq(mImsCallProfile),
357                     eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
358         } catch (Exception ex) {
359             ex.printStackTrace();
360             Assert.fail("unexpected exception thrown" + ex.getMessage());
361         }
362         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
363         assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
364         //call established
365         mImsCallListener.onCallProgressing(mSecondImsCall);
366         assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState());
367     }
368
369     @FlakyTest
370     @Ignore
371     @Test
372     @SmallTest
373     public void testImsMTActiveMODial() {
374         assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
375         assertEquals(Call.State.IDLE, mCTUT.mBackgroundCall.getState());
376
377         testImsMTCallAccept();
378
379         assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
380         assertEquals(Call.State.IDLE, mCTUT.mBackgroundCall.getState());
381         try {
382             mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
383             verify(mImsManager, times(1)).makeCall(eq(mServiceId), eq(mImsCallProfile),
384                     eq(new String[]{"+17005554141"}), (ImsCall.Listener) any());
385         } catch (Exception ex) {
386             ex.printStackTrace();
387             Assert.fail("unexpected exception thrown" + ex.getMessage());
388         }
389         waitForMs(100);
390         assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
391         assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
392     }
393
394     @Test
395     @SmallTest
396     public void testImsMOCallHangup() {
397         testImsMOCallDial();
398         //hangup before call go to active
399         try {
400             mCTUT.hangup(mCTUT.mForegroundCall);
401         } catch (Exception ex) {
402             ex.printStackTrace();
403             Assert.fail("unexpected exception thrown" + ex.getMessage());
404         }
405         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
406         assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
407     }
408
409     @Test
410     @SmallTest
411     public void testImsSendDtmf() {
412         //establish a MT call
413         testImsMTCallAccept();
414         mCTUT.sendDtmf(PhoneNumberUtils.PAUSE, null);
415         //verify trigger sendDtmf to mImsCall
416         verify(mImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.PAUSE), (Message) isNull());
417         // mock a new MT
418         try {
419             doReturn(mSecondImsCall).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(),
420                     (ImsCall.Listener) any());
421             Intent mIntent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
422             mContext.sendBroadcast(mIntent);
423             mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
424         } catch (Exception ex) {
425             ex.printStackTrace();
426             Assert.fail("unexpected exception thrown" + ex.getMessage());
427         }
428
429         waitForMs(100);
430
431         mCTUT.sendDtmf(PhoneNumberUtils.WAIT, null);
432         //verify trigger sendDtmf to mImsSecondCall
433         verify(mSecondImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.WAIT), (Message) isNull());
434     }
435
436     @Test
437     @SmallTest
438     public void testReasonCodeRemap() {
439         assertEquals(ImsReasonInfo.CODE_WIFI_LOST, mCTUT.maybeRemapReasonCode(
440                 new ImsReasonInfo(1, 1, "Wifi signal lost.")));
441         assertEquals(ImsReasonInfo.CODE_WIFI_LOST, mCTUT.maybeRemapReasonCode(
442                 new ImsReasonInfo(200, 1, "Wifi signal lost.")));
443         assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
444                 mCTUT.maybeRemapReasonCode(new ImsReasonInfo(501, 1, "Call answered elsewhere.")));
445         assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
446                 mCTUT.maybeRemapReasonCode(new ImsReasonInfo(510, 1, "Call answered elsewhere.")));
447         assertEquals(90210, mCTUT.maybeRemapReasonCode(new ImsReasonInfo(90210, 1,
448                 "Call answered elsewhere.")));
449     }
450
451
452     @Test
453     @SmallTest
454     public void testDialImsServiceUnavailable() throws ImsException {
455         doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
456                 mImsManager).createCallProfile(anyInt(), anyInt(), anyInt());
457         mCTUT.mRetryTimeout = () -> 0; //ms
458         assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
459         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
460
461         try {
462             mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
463         } catch (Exception e) {
464             Assert.fail();
465         }
466
467         // wait for handler to process ImsService connection retry
468         waitForHandlerAction(mCTHander, 1000); // 1 second timeout
469         verify(mImsManager, never()).makeCall(anyInt(), nullable(ImsCallProfile.class),
470                 eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class));
471         // Make sure that open is called in ImsPhoneCallTracker when it was first connected and
472         // again after retry.
473         verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
474                 nullable(ImsConnectionStateListener.class));
475     }
476
477     @FlakyTest
478     @Ignore
479     @Test
480     @SmallTest
481     public void testTTYImsServiceUnavailable() throws ImsException {
482         doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
483                 mImsManager).setUiTTYMode(nullable(Context.class), anyInt(),
484                 nullable(Message.class));
485         // Remove retry timeout delay
486         mCTUT.mRetryTimeout = () -> 0; //ms
487
488         mCTUT.setUiTTYMode(0, new Message());
489
490         // wait for handler to process ImsService connection retry
491         waitForHandlerAction(mCTHander, 100);
492         // Make sure that open is called in ImsPhoneCallTracker to re-establish connection to
493         // ImsService
494         verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
495                 nullable(ImsConnectionStateListener.class));
496     }
497 }