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.imsphone; 17 18import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; 19 20import static org.junit.Assert.assertEquals; 21import static org.junit.Assert.assertFalse; 22import static org.junit.Assert.assertTrue; 23import static org.mockito.ArgumentMatchers.nullable; 24import static org.mockito.Mockito.any; 25import static org.mockito.Mockito.anyBoolean; 26import static org.mockito.Mockito.anyInt; 27import static org.mockito.Mockito.doAnswer; 28import static org.mockito.Mockito.doNothing; 29import static org.mockito.Mockito.doReturn; 30import static org.mockito.Mockito.doThrow; 31import static org.mockito.Mockito.eq; 32import static org.mockito.Mockito.isNull; 33import static org.mockito.Mockito.mock; 34import static org.mockito.Mockito.never; 35import static org.mockito.Mockito.spy; 36import static org.mockito.Mockito.times; 37import static org.mockito.Mockito.verify; 38 39import android.content.Context; 40import android.content.SharedPreferences; 41import android.os.Bundle; 42import android.os.Handler; 43import android.os.HandlerThread; 44import android.os.Message; 45import android.os.PersistableBundle; 46import android.support.test.filters.FlakyTest; 47import android.telecom.VideoProfile; 48import android.telephony.CarrierConfigManager; 49import android.telephony.DisconnectCause; 50import android.telephony.PhoneNumberUtils; 51import android.telephony.ServiceState; 52import android.telephony.TelephonyManager; 53import android.telephony.ims.ImsCallProfile; 54import android.telephony.ims.ImsCallSession; 55import android.telephony.ims.ImsReasonInfo; 56import android.telephony.ims.ImsStreamMediaProfile; 57import android.telephony.ims.feature.ImsFeature; 58import android.telephony.ims.feature.MmTelFeature; 59import android.telephony.ims.stub.ImsRegistrationImplBase; 60import android.test.suitebuilder.annotation.SmallTest; 61 62import com.android.ims.ImsCall; 63import com.android.ims.ImsConfig; 64import com.android.ims.ImsException; 65import com.android.ims.internal.IImsCallSession; 66import com.android.internal.telephony.Call; 67import com.android.internal.telephony.CallStateException; 68import com.android.internal.telephony.CommandsInterface; 69import com.android.internal.telephony.Connection; 70import com.android.internal.telephony.PhoneConstants; 71import com.android.internal.telephony.TelephonyTest; 72 73import org.junit.After; 74import org.junit.Assert; 75import org.junit.Before; 76import org.junit.Ignore; 77import org.junit.Test; 78import org.mockito.ArgumentCaptor; 79import org.mockito.Mock; 80import org.mockito.invocation.InvocationOnMock; 81import org.mockito.stubbing.Answer; 82 83public class ImsPhoneCallTrackerTest extends TelephonyTest { 84 private ImsPhoneCallTracker mCTUT; 85 private ImsCTHandlerThread mImsCTHandlerThread; 86 private MmTelFeature.Listener mMmTelListener; 87 private ImsRegistrationImplBase.Callback mRegistrationCallback; 88 private ImsFeature.CapabilityCallback mCapabilityCallback; 89 private ImsCall.Listener mImsCallListener; 90 private ImsCall mImsCall; 91 private ImsCall mSecondImsCall; 92 private Bundle mBundle = new Bundle(); 93 private int mServiceId; 94 @Mock 95 private ImsCallSession mImsCallSession; 96 @Mock 97 private SharedPreferences mSharedPreferences; 98 @Mock 99 private ImsPhoneConnection.Listener mImsPhoneConnectionListener; 100 @Mock 101 private ImsConfig mImsConfig; 102 private Handler mCTHander; 103 104 private class ImsCTHandlerThread extends HandlerThread { 105 106 private ImsCTHandlerThread(String name) { 107 super(name); 108 } 109 @Override 110 public void onLooperPrepared() { 111 mCTUT = new ImsPhoneCallTracker(mImsPhone); 112 mCTUT.addReasonCodeRemapping(null, "Wifi signal lost.", ImsReasonInfo.CODE_WIFI_LOST); 113 mCTUT.addReasonCodeRemapping(501, "Call answered elsewhere.", 114 ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); 115 mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.", 116 ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); 117 mCTUT.setDataEnabled(true); 118 mCTHander = new Handler(mCTUT.getLooper()); 119 setReady(true); 120 } 121 } 122 123 private void imsCallMocking(final ImsCall mImsCall) throws Exception { 124 125 doAnswer(new Answer<Void>() { 126 @Override 127 public Void answer(InvocationOnMock invocation) throws Throwable { 128 // trigger the listener on accept call 129 if (mImsCallListener != null) { 130 mImsCallListener.onCallStarted(mImsCall); 131 } 132 return null; 133 } 134 }).when(mImsCall).accept(anyInt()); 135 136 doAnswer(new Answer<Void>() { 137 @Override 138 public Void answer(InvocationOnMock invocation) throws Throwable { 139 // trigger the listener on reject call 140 int reasonCode = (int) invocation.getArguments()[0]; 141 if (mImsCallListener != null) { 142 mImsCallListener.onCallStartFailed(mImsCall, new ImsReasonInfo(reasonCode, -1)); 143 mImsCallListener.onCallTerminated(mImsCall, new ImsReasonInfo(reasonCode, -1)); 144 } 145 return null; 146 } 147 }).when(mImsCall).reject(anyInt()); 148 149 doAnswer(new Answer<Void>() { 150 @Override 151 public Void answer(InvocationOnMock invocation) throws Throwable { 152 // trigger the listener on reject call 153 int reasonCode = (int) invocation.getArguments()[0]; 154 if (mImsCallListener != null) { 155 mImsCallListener.onCallTerminated(mImsCall, new ImsReasonInfo(reasonCode, -1)); 156 } 157 return null; 158 } 159 }).when(mImsCall).terminate(anyInt()); 160 161 doAnswer(new Answer<Void>() { 162 @Override 163 public Void answer(InvocationOnMock invocation) throws Throwable { 164 if (mImsCallListener != null) { 165 mImsCallListener.onCallHeld(mImsCall); 166 } 167 return null; 168 } 169 }).when(mImsCall).hold(); 170 171 mImsCall.attachSession(mImsCallSession); 172 } 173 174 @Before 175 public void setUp() throws Exception { 176 super.setUp(this.getClass().getSimpleName()); 177 mServiceId = 0; 178 mImsCallProfile.mCallExtras = mBundle; 179 mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager); 180 mImsCall = spy(new ImsCall(mContext, mImsCallProfile)); 181 mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile)); 182 mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class); 183 imsCallMocking(mImsCall); 184 imsCallMocking(mSecondImsCall); 185 doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceState(); 186 doReturn(mImsCallProfile).when(mImsManager).createCallProfile(anyInt(), anyInt()); 187 188 doAnswer(invocation -> { 189 mMmTelListener = (MmTelFeature.Listener) invocation.getArguments()[0]; 190 return null; 191 }).when(mImsManager).open(any(MmTelFeature.Listener.class)); 192 193 194 doAnswer(new Answer<ImsCall>() { 195 @Override 196 public ImsCall answer(InvocationOnMock invocation) throws Throwable { 197 mImsCallListener = 198 (ImsCall.Listener) invocation.getArguments()[2]; 199 mImsCall.setListener(mImsCallListener); 200 return mImsCall; 201 } 202 }).when(mImsManager).takeCall(any(), any(), any()); 203 204 doAnswer(new Answer<ImsCall>() { 205 @Override 206 public ImsCall answer(InvocationOnMock invocation) throws Throwable { 207 mImsCallListener = 208 (ImsCall.Listener) invocation.getArguments()[2]; 209 mSecondImsCall.setListener(mImsCallListener); 210 return mSecondImsCall; 211 } 212 }).when(mImsManager).makeCall(eq(mImsCallProfile), (String []) any(), 213 (ImsCall.Listener) any()); 214 215 doAnswer(invocation -> { 216 mRegistrationCallback = invocation.getArgument(0); 217 return mRegistrationCallback; 218 }).when(mImsManager).addRegistrationCallback(any(ImsRegistrationImplBase.Callback.class)); 219 220 doAnswer(invocation -> { 221 mCapabilityCallback = (ImsFeature.CapabilityCallback) invocation.getArguments()[0]; 222 return mCapabilityCallback; 223 224 }).when(mImsManager).addCapabilitiesCallback(any(ImsFeature.CapabilityCallback.class)); 225 226 doReturn(mImsConfig).when(mImsManager).getConfigInterface(); 227 228 doNothing().when(mImsManager).addNotifyStatusChangedCallbackIfAvailable(any()); 229 230 mImsCTHandlerThread = new ImsCTHandlerThread(this.getClass().getSimpleName()); 231 mImsCTHandlerThread.start(); 232 233 waitUntilReady(); 234 logd("ImsPhoneCallTracker initiated"); 235 /* Make sure getImsService is triggered on handler */ 236 waitForHandlerAction(mCTHander, 100); 237 } 238 239 @After 240 public void tearDown() throws Exception { 241 mCTUT = null; 242 mImsCTHandlerThread.quit(); 243 super.tearDown(); 244 } 245 246 @Test 247 @SmallTest 248 public void testImsRegistered() { 249 // when IMS is registered 250 mRegistrationCallback.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 251 // then service state should be IN_SERVICE and ImsPhone state set to registered 252 verify(mImsPhone).setServiceState(eq(ServiceState.STATE_IN_SERVICE)); 253 verify(mImsPhone).setImsRegistered(eq(true)); 254 } 255 256 @Test 257 @SmallTest 258 public void testImsRegistering() { 259 // when IMS is registering 260 mRegistrationCallback.onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 261 // then service state should be OUT_OF_SERVICE and ImsPhone state set to not registered 262 verify(mImsPhone).setServiceState(eq(ServiceState.STATE_OUT_OF_SERVICE)); 263 verify(mImsPhone).setImsRegistered(eq(false)); 264 } 265 266 @Test 267 @SmallTest 268 public void testImsDeregistered() { 269 // when IMS is deregistered 270 mRegistrationCallback.onDeregistered(new ImsReasonInfo()); 271 // then service state should be OUT_OF_SERVICE and ImsPhone state set to not registered 272 verify(mImsPhone).setServiceState(eq(ServiceState.STATE_OUT_OF_SERVICE)); 273 verify(mImsPhone).setImsRegistered(eq(false)); 274 } 275 276 @Test 277 @SmallTest 278 public void testVowifiDisabledOnLte() { 279 // LTE is registered. 280 doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when( 281 mImsManager).getRegistrationTech(); 282 assertFalse(mCTUT.isVowifiEnabled()); 283 284 // enable Voice over LTE 285 ImsFeature.Capabilities caps = new ImsFeature.Capabilities(); 286 caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 287 mCapabilityCallback.onCapabilitiesStatusChanged(caps); 288 waitForHandlerAction(mCTHander, 1000); 289 290 // Voice over IWLAN is still disabled 291 assertFalse(mCTUT.isVowifiEnabled()); 292 } 293 294 @Test 295 @SmallTest 296 public void testVowifiDisabledOnIwlan() { 297 // LTE is registered. 298 doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).when( 299 mImsManager).getRegistrationTech(); 300 assertFalse(mCTUT.isVowifiEnabled()); 301 302 // enable Voice over IWLAN 303 ImsFeature.Capabilities caps = new ImsFeature.Capabilities(); 304 caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 305 mCapabilityCallback.onCapabilitiesStatusChanged(caps); 306 waitForHandlerAction(mCTHander, 1000); 307 308 // Voice over IWLAN is enabled 309 assertTrue(mCTUT.isVowifiEnabled()); 310 } 311 312 @Test 313 @SmallTest 314 public void testImsFeatureCapabilityChange() { 315 doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when( 316 mImsManager).getRegistrationTech(); 317 assertFalse(mCTUT.isVolteEnabled()); 318 assertFalse(mCTUT.isVideoCallEnabled()); 319 320 // enable only Voice 321 ImsFeature.Capabilities caps = new ImsFeature.Capabilities(); 322 caps.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 323 mCapabilityCallback.onCapabilitiesStatusChanged(caps); 324 waitForHandlerAction(mCTHander, 1000); 325 326 assertTrue(mCTUT.isVolteEnabled()); 327 assertFalse(mCTUT.isVideoCallEnabled()); 328 // video call not enabled 329 verify(mImsPhone, times(0)).notifyForVideoCapabilityChanged(anyBoolean()); 330 verify(mImsPhone, times(1)).onFeatureCapabilityChanged(); 331 332 // enable video call 333 ImsFeature.Capabilities capsVideo = new ImsFeature.Capabilities(); 334 capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 335 capsVideo.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 336 mCapabilityCallback.onCapabilitiesStatusChanged(capsVideo); 337 waitForHandlerAction(mCTHander, 1000); 338 assertTrue(mCTUT.isVideoCallEnabled()); 339 verify(mImsPhone, times(1)).notifyForVideoCapabilityChanged(eq(true)); 340 } 341 342 @Test 343 @SmallTest 344 public void testImsMTCall() { 345 assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); 346 assertFalse(mCTUT.mRingingCall.isRinging()); 347 // mock a MT call 348 mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY); 349 verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any()); 350 verify(mImsPhone, times(1)).notifyIncomingRing(); 351 assertEquals(PhoneConstants.State.RINGING, mCTUT.getState()); 352 assertTrue(mCTUT.mRingingCall.isRinging()); 353 assertEquals(1, mCTUT.mRingingCall.getConnections().size()); 354 ImsPhoneConnection connection = 355 (ImsPhoneConnection) mCTUT.mRingingCall.getConnections().get(0); 356 connection.addListener(mImsPhoneConnectionListener); 357 } 358 359 @Test 360 @SmallTest 361 public void testImsMTCallAccept() { 362 testImsMTCall(); 363 assertTrue(mCTUT.mRingingCall.isRinging()); 364 try { 365 mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE); 366 verify(mImsCall, times(1)).accept(eq(ImsCallProfile 367 .getCallTypeFromVideoState(ImsCallProfile.CALL_TYPE_VOICE))); 368 } catch (Exception ex) { 369 ex.printStackTrace(); 370 Assert.fail("unexpected exception thrown" + ex.getMessage()); 371 } 372 assertFalse(mCTUT.mRingingCall.isRinging()); 373 assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState()); 374 assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState()); 375 assertEquals(1, mCTUT.mForegroundCall.getConnections().size()); 376 } 377 378 @Test 379 @SmallTest 380 public void testImsMTCallReject() { 381 testImsMTCall(); 382 assertTrue(mCTUT.mRingingCall.isRinging()); 383 try { 384 mCTUT.rejectCall(); 385 verify(mImsCall, times(1)).reject(eq(ImsReasonInfo.CODE_USER_DECLINE)); 386 } catch (Exception ex) { 387 ex.printStackTrace(); 388 Assert.fail("unexpected exception thrown" + ex.getMessage()); 389 } 390 assertFalse(mCTUT.mRingingCall.isRinging()); 391 assertEquals(0, mCTUT.mRingingCall.getConnections().size()); 392 assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); 393 } 394 395 @Test 396 @SmallTest 397 public void testImsMTCallAcceptHangUp() { 398 testImsMTCallAccept(); 399 assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState()); 400 assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState()); 401 try { 402 mCTUT.hangup(mCTUT.mForegroundCall); 403 } catch (Exception ex) { 404 ex.printStackTrace(); 405 Assert.fail("unexpected exception thrown" + ex.getMessage()); 406 } 407 assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); 408 assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); 409 } 410 411 @Test 412 @SmallTest 413 public void testImsMTCallAcceptHold() { 414 testImsMTCallAccept(); 415 416 assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState()); 417 assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState()); 418 // mock a new MT 419 try { 420 doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class), 421 any(Bundle.class), any(ImsCall.Listener.class)); 422 } catch (Exception ex) { 423 ex.printStackTrace(); 424 Assert.fail("unexpected exception thrown" + ex.getMessage()); 425 } 426 mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY); 427 428 verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any()); 429 verify(mImsPhone, times(2)).notifyIncomingRing(); 430 assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState()); 431 assertEquals(ImsPhoneCall.State.WAITING, mCTUT.mRingingCall.getState()); 432 assertEquals(PhoneConstants.State.RINGING, mCTUT.getState()); 433 434 //hold the foreground active call, accept the new ringing call 435 try { 436 mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE); 437 verify(mImsCall, times(1)).hold(); 438 } catch (Exception ex) { 439 ex.printStackTrace(); 440 Assert.fail("unexpected exception thrown" + ex.getMessage()); 441 } 442 443 waitForMs(100); 444 assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState()); 445 assertFalse(mCTUT.mRingingCall.isRinging()); 446 assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState()); 447 } 448 449 /** 450 * Ensures that the dial method will perform a shared preferences lookup using the correct 451 * shared preference key to determine the CLIR mode. 452 */ 453 @Test 454 @SmallTest 455 public void testDialClirMode() { 456 mCTUT.setSharedPreferenceProxy((Context context) -> { 457 return mSharedPreferences; 458 }); 459 ArgumentCaptor<String> mStringCaptor = ArgumentCaptor.forClass(String.class); 460 doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt( 461 mStringCaptor.capture(), anyInt()); 462 463 try { 464 mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null); 465 } catch (CallStateException cse) { 466 cse.printStackTrace(); 467 Assert.fail("unexpected exception thrown" + cse.getMessage()); 468 } 469 470 // Ensure that the correct key was queried from the shared prefs. 471 assertEquals("clir_key0", mStringCaptor.getValue()); 472 } 473 474 /** 475 * Ensures for an emergency call that the dial method will default the CLIR to 476 * {@link CommandsInterface#CLIR_SUPPRESSION}, ensuring the caller's ID is shown. 477 */ 478 @Test 479 @SmallTest 480 public void testEmergencyDialSuppressClir() { 481 mCTUT.setSharedPreferenceProxy((Context context) -> { 482 return mSharedPreferences; 483 }); 484 // Mock implementation of phone number utils treats everything as an emergency. 485 mCTUT.setPhoneNumberUtilsProxy((String string) -> { 486 return true; 487 }); 488 // Set preference to hide caller ID. 489 ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class); 490 doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt( 491 stringCaptor.capture(), anyInt()); 492 493 try { 494 mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null); 495 496 ArgumentCaptor<ImsCallProfile> profileCaptor = ArgumentCaptor.forClass( 497 ImsCallProfile.class); 498 verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile), 499 eq(new String[]{"+17005554141"}), any()); 500 501 // Because this is an emergency call, we expect caller id to be visible now. 502 assertEquals(mImsCallProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR), 503 CommandsInterface.CLIR_SUPPRESSION); 504 } catch (CallStateException cse) { 505 cse.printStackTrace(); 506 Assert.fail("unexpected exception thrown" + cse.getMessage()); 507 } catch (ImsException ie) { 508 ie.printStackTrace(); 509 Assert.fail("unexpected exception thrown" + ie.getMessage()); 510 } 511 } 512 513 @FlakyTest 514 @Ignore 515 @Test 516 @SmallTest 517 public void testImsMOCallDial() { 518 assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); 519 assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); 520 try { 521 mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null); 522 verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile), 523 eq(new String[]{"+17005554141"}), (ImsCall.Listener) any()); 524 } catch (Exception ex) { 525 ex.printStackTrace(); 526 Assert.fail("unexpected exception thrown" + ex.getMessage()); 527 } 528 assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState()); 529 assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState()); 530 //call established 531 mImsCallListener.onCallProgressing(mSecondImsCall); 532 assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState()); 533 } 534 535 @FlakyTest 536 @Ignore 537 @Test 538 @SmallTest 539 public void testImsMTActiveMODial() { 540 assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); 541 assertEquals(Call.State.IDLE, mCTUT.mBackgroundCall.getState()); 542 543 testImsMTCallAccept(); 544 545 assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState()); 546 assertEquals(Call.State.IDLE, mCTUT.mBackgroundCall.getState()); 547 try { 548 mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null); 549 verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile), 550 eq(new String[]{"+17005554141"}), (ImsCall.Listener) any()); 551 } catch (Exception ex) { 552 ex.printStackTrace(); 553 Assert.fail("unexpected exception thrown" + ex.getMessage()); 554 } 555 waitForMs(100); 556 assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState()); 557 assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState()); 558 } 559 560 @Test 561 @SmallTest 562 public void testImsMOCallHangup() { 563 testImsMOCallDial(); 564 //hangup before call go to active 565 try { 566 mCTUT.hangup(mCTUT.mForegroundCall); 567 } catch (Exception ex) { 568 ex.printStackTrace(); 569 Assert.fail("unexpected exception thrown" + ex.getMessage()); 570 } 571 assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); 572 assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); 573 } 574 575 @Test 576 @SmallTest 577 public void testImsSendDtmf() { 578 //establish a MT call 579 testImsMTCallAccept(); 580 mCTUT.sendDtmf(PhoneNumberUtils.PAUSE, null); 581 //verify trigger sendDtmf to mImsCall 582 verify(mImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.PAUSE), (Message) isNull()); 583 // mock a new MT 584 try { 585 doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class), 586 any(Bundle.class), any(ImsCall.Listener.class)); 587 mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY); 588 mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE); 589 } catch (Exception ex) { 590 ex.printStackTrace(); 591 Assert.fail("unexpected exception thrown" + ex.getMessage()); 592 } 593 594 waitForMs(100); 595 596 mCTUT.sendDtmf(PhoneNumberUtils.WAIT, null); 597 //verify trigger sendDtmf to mImsSecondCall 598 verify(mSecondImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.WAIT), (Message) isNull()); 599 } 600 601 @Test 602 @SmallTest 603 public void testReasonCodeRemap() { 604 assertEquals(ImsReasonInfo.CODE_WIFI_LOST, mCTUT.maybeRemapReasonCode( 605 new ImsReasonInfo(1, 1, "Wifi signal lost."))); 606 assertEquals(ImsReasonInfo.CODE_WIFI_LOST, mCTUT.maybeRemapReasonCode( 607 new ImsReasonInfo(200, 1, "Wifi signal lost."))); 608 assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, 609 mCTUT.maybeRemapReasonCode(new ImsReasonInfo(501, 1, "Call answered elsewhere."))); 610 assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, 611 mCTUT.maybeRemapReasonCode(new ImsReasonInfo(510, 1, "Call answered elsewhere."))); 612 assertEquals(90210, mCTUT.maybeRemapReasonCode(new ImsReasonInfo(90210, 1, 613 "Call answered elsewhere."))); 614 } 615 616 617 @Test 618 @SmallTest 619 public void testDialImsServiceUnavailable() throws ImsException { 620 doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when( 621 mImsManager).createCallProfile(anyInt(), anyInt()); 622 mCTUT.setRetryTimeout(() -> 0); 623 assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); 624 assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); 625 626 try { 627 mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null); 628 } catch (Exception e) { 629 Assert.fail(); 630 } 631 632 // wait for handler to process ImsService connection retry 633 waitForHandlerAction(mCTHander, 1000); // 1 second timeout 634 verify(mImsManager, never()).makeCall(nullable(ImsCallProfile.class), 635 eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class)); 636 // Make sure that open is called in ImsPhoneCallTracker when it was first connected and 637 // again after retry. 638 verify(mImsManager, times(2)).open( 639 nullable(MmTelFeature.Listener.class)); 640 } 641 642 @FlakyTest 643 @Ignore 644 @Test 645 @SmallTest 646 public void testTTYImsServiceUnavailable() throws ImsException { 647 doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when( 648 mImsManager).setUiTTYMode(nullable(Context.class), anyInt(), 649 nullable(Message.class)); 650 // Remove retry timeout delay 651 mCTUT.setRetryTimeout(() -> 0); //ms 652 653 mCTUT.setUiTTYMode(0, new Message()); 654 655 // wait for handler to process ImsService connection retry 656 waitForHandlerAction(mCTHander, 100); 657 // Make sure that open is called in ImsPhoneCallTracker to re-establish connection to 658 // ImsService 659 verify(mImsManager, times(2)).open( 660 nullable(MmTelFeature.Listener.class)); 661 } 662 663 /** 664 * Test notification of handover from LTE to WIFI and WIFI to LTE and ensure that the expected 665 * connection events are sent. 666 */ 667 @Test 668 @SmallTest 669 public void testNotifyHandovers() { 670 setupCarrierConfig(); 671 672 //establish a MT call 673 testImsMTCallAccept(); 674 ImsPhoneConnection connection = 675 (ImsPhoneConnection) mCTUT.mForegroundCall.getConnections().get(0); 676 ImsCall call = connection.getImsCall(); 677 // Needs to be a video call to see this signalling. 678 mImsCallProfile.mCallType = ImsCallProfile.CALL_TYPE_VT; 679 680 // First handover from LTE to WIFI; this takes us into a mid-call state. 681 call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), 682 ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, 683 new ImsReasonInfo()); 684 // Handover back to LTE. 685 call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), 686 ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.RIL_RADIO_TECHNOLOGY_LTE, 687 new ImsReasonInfo()); 688 verify(mImsPhoneConnectionListener).onConnectionEvent(eq( 689 TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE), isNull()); 690 691 // Finally hand back to WIFI 692 call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), 693 ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, 694 new ImsReasonInfo()); 695 verify(mImsPhoneConnectionListener).onConnectionEvent(eq( 696 TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI), isNull()); 697 } 698 699 /** 700 * Configure carrier config options relevant to the unit test. 701 */ 702 public void setupCarrierConfig() { 703 PersistableBundle bundle = new PersistableBundle(); 704 bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, 705 true); 706 bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, 707 true); 708 bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, true); 709 mCTUT.updateCarrierConfigCache(bundle); 710 } 711 712 @Test 713 @SmallTest 714 public void testLowBatteryDisconnectMidCall() { 715 assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo( 716 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.ACTIVE)); 717 assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo( 718 new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.ACTIVE)); 719 } 720 721 @Test 722 @SmallTest 723 public void testImsAlternateEmergencyDisconnect() { 724 assertEquals(DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL, 725 mCTUT.getDisconnectCauseFromReasonInfo( 726 new ImsReasonInfo(ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0), 727 Call.State.ACTIVE)); 728 } 729 730 @Test 731 @SmallTest 732 public void testLowBatteryDisconnectDialing() { 733 assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo( 734 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.DIALING)); 735 assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo( 736 new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.DIALING)); 737 } 738 739 /** 740 * Tests that no hold tone is played if the call is remotely held and the media direction is 741 * send/receive (i.e. there is an audio stream present). 742 */ 743 @Test 744 @SmallTest 745 public void testNoRemoteHoldtone() { 746 //establish a MT call 747 testImsMTCallAccept(); 748 ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection(); 749 ImsCall call = connection.getImsCall(); 750 751 // Set the media direction to send/receive. 752 ImsCallProfile callProfile = new ImsCallProfile(); 753 callProfile.mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE; 754 call.setCallProfile(callProfile); 755 756 try { 757 mCTUT.onCallHoldReceived(call); 758 } catch (Exception ex) { 759 ex.printStackTrace(); 760 Assert.fail("unexpected exception thrown" + ex.getMessage()); 761 } 762 verify(mImsPhone, never()).startOnHoldTone(nullable(Connection.class)); 763 } 764 765 /** 766 * Verifies that a remote hold tone is played when the call is remotely held and the media 767 * direction is inactive (i.e. the audio stream is not playing, so we should play the tone). 768 */ 769 @Test 770 @SmallTest 771 public void testRemoteToneInactive() { 772 //establish a MT call 773 testImsMTCallAccept(); 774 ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection(); 775 ImsCall call = connection.getImsCall(); 776 777 // Set the media direction to inactive to trigger a hold tone. 778 ImsCallProfile callProfile = new ImsCallProfile(); 779 callProfile.mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_INACTIVE; 780 call.setCallProfile(callProfile); 781 782 try { 783 mCTUT.onCallHoldReceived(call); 784 } catch (Exception ex) { 785 ex.printStackTrace(); 786 Assert.fail("unexpected exception thrown" + ex.getMessage()); 787 } 788 verify(mImsPhone, times(1)).startOnHoldTone(nullable(Connection.class)); 789 } 790 791 @Test 792 @SmallTest 793 public void testRemoteHoldtone() { 794 // Set carrier config to always play remote hold tone. 795 mCTUT.setAlwaysPlayRemoteHoldTone(true); 796 //establish a MT call 797 testImsMTCallAccept(); 798 ImsPhoneConnection connection = mCTUT.mForegroundCall.getFirstConnection(); 799 ImsCall call = connection.getImsCall(); 800 801 // Set the media direction to send/receive; normally we don't play a hold tone but the 802 // carrier config option is set to ensure we will do it in this case. 803 ImsCallProfile callProfile = new ImsCallProfile(); 804 callProfile.mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE; 805 call.setCallProfile(callProfile); 806 807 try { 808 mCTUT.onCallHoldReceived(call); 809 } catch (Exception ex) { 810 ex.printStackTrace(); 811 Assert.fail("unexpected exception thrown" + ex.getMessage()); 812 } 813 verify(mImsPhone, times(1)).startOnHoldTone(nullable(Connection.class)); 814 } 815} 816