1 /* 2 * Copyright (C) 2025 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 static com.android.server.telecom.CallsManager.CALL_FILTER_ALL; 20 import static com.android.server.telecom.CallsManager.ONGOING_CALL_STATES; 21 22 import static junit.framework.Assert.assertNotNull; 23 import static junit.framework.TestCase.fail; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.mockito.ArgumentMatchers.any; 27 import static org.mockito.ArgumentMatchers.anyBoolean; 28 import static org.mockito.ArgumentMatchers.anyInt; 29 import static org.mockito.ArgumentMatchers.anyString; 30 import static org.mockito.ArgumentMatchers.eq; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.timeout; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 import static org.junit.Assert.assertTrue; 38 import static org.junit.Assert.assertFalse; 39 40 import android.content.ComponentName; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.net.Uri; 44 import android.os.Bundle; 45 import android.os.OutcomeReceiver; 46 import android.os.PersistableBundle; 47 import android.os.UserHandle; 48 import android.telecom.CallAttributes; 49 import android.telecom.CallException; 50 import android.telecom.Connection; 51 import android.telecom.DisconnectCause; 52 import android.telecom.PhoneAccount; 53 import android.telecom.PhoneAccountHandle; 54 import android.telephony.CarrierConfigManager; 55 56 import androidx.test.filters.SmallTest; 57 58 import com.android.server.telecom.Analytics; 59 import com.android.server.telecom.AnomalyReporterAdapter; 60 import com.android.server.telecom.Call; 61 import com.android.server.telecom.CallState; 62 import com.android.server.telecom.CallsManager; 63 import com.android.server.telecom.ClockProxy; 64 import com.android.server.telecom.ConnectionServiceFocusManager; 65 import com.android.server.telecom.MmiUtils; 66 import com.android.server.telecom.PhoneAccountRegistrar; 67 import com.android.server.telecom.Timeouts; 68 import com.android.server.telecom.callsequencing.CallSequencingController; 69 import com.android.server.telecom.callsequencing.CallTransaction; 70 import com.android.server.telecom.callsequencing.voip.OutgoingCallTransactionSequencing; 71 import com.android.server.telecom.metrics.TelecomMetricsController; 72 import com.android.server.telecom.stats.CallFailureCause; 73 74 import org.junit.After; 75 import org.junit.Before; 76 import org.junit.Test; 77 import org.junit.runner.RunWith; 78 import org.junit.runners.JUnit4; 79 import org.mockito.Mock; 80 81 import java.util.Arrays; 82 import java.util.Collections; 83 import java.util.List; 84 import java.util.concurrent.CompletableFuture; 85 import java.util.concurrent.CountDownLatch; 86 import java.util.concurrent.TimeUnit; 87 88 @RunWith(JUnit4.class) 89 public class CallSequencingTests extends TelecomTestCase { 90 private static final long SEQUENCING_TIMEOUT_MS = 2000L; 91 private static final PhoneAccountHandle mHandle1 = new PhoneAccountHandle( 92 new ComponentName("foo", "bar"), "1"); 93 private static final PhoneAccountHandle mHandle2 = new PhoneAccountHandle( 94 new ComponentName("bar", "foo"), "2"); 95 private static final String TEST_NAME = "Alan Turing"; 96 private static final Uri TEST_URI = Uri.fromParts("tel", "abc", "123"); 97 private static final String ACTIVE_CALL_ID = "TC@1"; 98 private static final String NEW_CALL_ID = "TC@2"; 99 100 private CallSequencingController mController; 101 @Mock 102 private CallsManager mCallsManager; 103 @Mock Context mContext; 104 @Mock ClockProxy mClockProxy; 105 @Mock AnomalyReporterAdapter mAnomalyReporter; 106 @Mock Timeouts.Adapter mTimeoutsAdapter; 107 @Mock TelecomMetricsController mMetricsController; 108 @Mock MmiUtils mMmiUtils; 109 @Mock 110 ConnectionServiceFocusManager mConnectionServiceFocusManager; 111 @Mock Call mActiveCall; 112 @Mock Call mHeldCall; 113 @Mock Call mNewCall; 114 @Mock Call mRingingCall; 115 116 @Override 117 @Before setUp()118 public void setUp() throws Exception { 119 super.setUp(); 120 when(mFeatureFlags.enableCallSequencing()).thenReturn(true); 121 mController = new CallSequencingController(mCallsManager, mContext, mClockProxy, 122 mAnomalyReporter, mTimeoutsAdapter, mMetricsController, mMmiUtils, mFeatureFlags); 123 124 when(mActiveCall.getState()).thenReturn(CallState.ACTIVE); 125 when(mRingingCall.getState()).thenReturn(CallState.RINGING); 126 when(mHeldCall.getState()).thenReturn(CallState.ON_HOLD); 127 128 when(mActiveCall.getId()).thenReturn(ACTIVE_CALL_ID); 129 when(mNewCall.getId()).thenReturn(NEW_CALL_ID); 130 } 131 132 @Override 133 @After tearDown()134 public void tearDown() throws Exception { 135 super.tearDown(); 136 } 137 138 139 @Test 140 @SmallTest testTransactionOutgoingCall_CallNotPermitted()141 public void testTransactionOutgoingCall_CallNotPermitted() { 142 String callingPkg = "testPkg"; 143 CallAttributes outgoingCallAttributes = getOutgoingCallAttributes(); 144 145 // Outgoing call is not permitted 146 when(mCallsManager.isOutgoingCallPermitted(mHandle1)).thenReturn(false); 147 CompletableFuture<CallTransaction> transactionFuture = mController 148 .createTransactionalOutgoingCall("callId", outgoingCallAttributes, 149 new Bundle(), callingPkg); 150 OutgoingCallTransactionSequencing transaction = (OutgoingCallTransactionSequencing) 151 transactionFuture.getNow(null); 152 assertNotNull(transaction); 153 assertTrue(transaction.getCallNotPermitted()); 154 155 // Call future is null 156 when(mCallsManager.isOutgoingCallPermitted(mHandle1)).thenReturn(true); 157 when(mCallsManager.startOutgoingCall(any(Uri.class), any(PhoneAccountHandle.class), 158 any(Bundle.class), any(UserHandle.class), any(Intent.class), anyString())) 159 .thenReturn(null); 160 transactionFuture = mController 161 .createTransactionalOutgoingCall("callId", outgoingCallAttributes, 162 new Bundle(), callingPkg); 163 transaction = (OutgoingCallTransactionSequencing) transactionFuture 164 .getNow(null); 165 assertNotNull(transaction); 166 assertTrue(transaction.getCallNotPermitted()); 167 } 168 169 @Test 170 @SmallTest testTransactionOutgoingCall()171 public void testTransactionOutgoingCall() { 172 String callingPkg = "testPkg"; 173 CallAttributes outgoingCallAttributes = getOutgoingCallAttributes(); 174 175 when(mCallsManager.isOutgoingCallPermitted(mHandle1)).thenReturn(true); 176 when(mCallsManager.startOutgoingCall(any(Uri.class), any(PhoneAccountHandle.class), 177 any(Bundle.class), any(UserHandle.class), any(Intent.class), anyString())) 178 .thenReturn(CompletableFuture.completedFuture(mNewCall)); 179 CompletableFuture<CallTransaction> transactionFuture = mController 180 .createTransactionalOutgoingCall("callId", outgoingCallAttributes, 181 new Bundle(), callingPkg); 182 try { 183 OutgoingCallTransactionSequencing transaction = (OutgoingCallTransactionSequencing) 184 transactionFuture.get(SEQUENCING_TIMEOUT_MS, TimeUnit.MILLISECONDS); 185 assertNotNull(transaction); 186 assertFalse(transaction.getCallNotPermitted()); 187 } catch (Exception e) { 188 fail("Failed to retrieve future in allocated time (" + SEQUENCING_TIMEOUT_MS + ")."); 189 } 190 } 191 192 @SmallTest 193 @Test testAnswerCall()194 public void testAnswerCall() { 195 // This will allow holdActiveCallForNewCallWithSequencing to immediately return true 196 setActiveCallFocus(null); 197 mController.answerCall(mNewCall, 0, CallsManager.REQUEST_ORIGIN_UNKNOWN); 198 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS)) 199 .requestFocusActionAnswerCall(eq(mNewCall), eq(0)); 200 } 201 202 @SmallTest 203 @Test testAnswerCallFail()204 public void testAnswerCallFail() { 205 setupHoldActiveCallForNewCallFailMocks(); 206 mController.answerCall(mNewCall, 0, CallsManager.REQUEST_ORIGIN_UNKNOWN); 207 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(0)) 208 .requestFocusActionAnswerCall(eq(mNewCall), eq(0)); 209 } 210 211 @SmallTest 212 @Test testAnswerCallAcceptedFromTelecom()213 public void testAnswerCallAcceptedFromTelecom() { 214 setPhoneAccounts(mNewCall, mActiveCall, false); 215 setActiveCallFocus(mActiveCall); 216 when(mCallsManager.canHold(mActiveCall)).thenReturn(true); 217 when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(true)); 218 219 when(mHeldCall.isSelfManaged()).thenReturn(false); 220 when(mNewCall.isSelfManaged()).thenReturn(true); 221 mController.answerCall(mNewCall, 0, CallsManager.REQUEST_ORIGIN_TELECOM_DISAMBIGUATION); 222 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(1)) 223 .requestFocusActionAnswerCall(eq(mNewCall), eq(0)); 224 } 225 226 @SmallTest 227 @Test testSetSelfManagedCallActive()228 public void testSetSelfManagedCallActive() { 229 // This will allow holdActiveCallForNewCallWithSequencing to immediately return true 230 setActiveCallFocus(null); 231 mController.handleSetSelfManagedCallActive(mNewCall); 232 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS)) 233 .requestActionSetActiveCall(eq(mNewCall), anyString()); 234 } 235 236 @SmallTest 237 @Test testSetSelfManagedCallActiveFail()238 public void testSetSelfManagedCallActiveFail() { 239 setupHoldActiveCallForNewCallFailMocks(); 240 mController.handleSetSelfManagedCallActive(mNewCall); 241 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(0)) 242 .requestActionSetActiveCall(eq(mNewCall), anyString()); 243 } 244 245 @SmallTest 246 @Test testTransactionHoldActiveCallForNewCall()247 public void testTransactionHoldActiveCallForNewCall() throws InterruptedException { 248 // This will allow holdActiveCallForNewCallWithSequencing to immediately return true 249 setActiveCallFocus(null); 250 CountDownLatch latch = new CountDownLatch(1); 251 OutcomeReceiver<Boolean, CallException> callback = new OutcomeReceiver<>() { 252 @Override 253 public void onResult(Boolean result) { 254 // Expected result 255 latch.countDown(); 256 } 257 @Override 258 public void onError(CallException exception) { 259 } 260 }; 261 verifyTransactionHoldActiveCallForNewCall(callback, latch); 262 } 263 264 @SmallTest 265 @Test testTransactionHoldActiveCallForNewCallFail()266 public void testTransactionHoldActiveCallForNewCallFail() { 267 setupHoldActiveCallForNewCallFailMocks(); 268 CountDownLatch latch = new CountDownLatch(1); 269 OutcomeReceiver<Boolean, CallException> callback = new OutcomeReceiver<>() { 270 @Override 271 public void onResult(Boolean result) { 272 } 273 274 @Override 275 public void onError(CallException exception) { 276 // Expected result 277 latch.countDown(); 278 } 279 }; 280 verifyTransactionHoldActiveCallForNewCall(callback, latch); 281 } 282 283 @Test 284 @SmallTest testHoldCallForNewCall_NoActiveCall()285 public void testHoldCallForNewCall_NoActiveCall() { 286 setActiveCallFocus(null); 287 CompletableFuture<Boolean> resultFuture = mController 288 .holdActiveCallForNewCallWithSequencing(mNewCall, 289 CallsManager.REQUEST_ORIGIN_UNKNOWN); 290 assertTrue(waitForFutureResult(resultFuture, false)); 291 } 292 293 @Test 294 @SmallTest testHoldCallForNewCall_CanHold()295 public void testHoldCallForNewCall_CanHold() { 296 setPhoneAccounts(mNewCall, mActiveCall, false); 297 setActiveCallFocus(mActiveCall); 298 when(mCallsManager.canHold(mActiveCall)).thenReturn(true); 299 when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(true)); 300 301 // Cross phone account case (sequencing enabled) 302 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 303 CompletableFuture<Boolean> resultFuture = mController 304 .holdActiveCallForNewCallWithSequencing(mNewCall, 305 CallsManager.REQUEST_ORIGIN_UNKNOWN); 306 assertTrue(waitForFutureResult(resultFuture, false)); 307 308 // Same phone account case 309 setPhoneAccounts(mNewCall, mActiveCall, true); 310 assertTrue(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 311 resultFuture = mController.holdActiveCallForNewCallWithSequencing(mNewCall, 312 CallsManager.REQUEST_ORIGIN_UNKNOWN); 313 assertTrue(waitForFutureResult(resultFuture, false)); 314 } 315 316 @Test 317 @SmallTest testHoldCallForNewCall_SupportsHold()318 public void testHoldCallForNewCall_SupportsHold() { 319 setPhoneAccounts(mNewCall, mActiveCall, false); 320 setActiveCallFocus(mActiveCall); 321 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 322 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(true); 323 when(mCallsManager.getFirstCallWithState(anyInt())).thenReturn(mHeldCall); 324 when(mHeldCall.isSelfManaged()).thenReturn(true); 325 when(mNewCall.isSelfManaged()).thenReturn(false); 326 when(mHeldCall.disconnect()).thenReturn(CompletableFuture.completedFuture(true)); 327 when(mActiveCall.hold()).thenReturn(CompletableFuture.completedFuture(true)); 328 329 // Verify that we abort transaction when there's a new (VOIP) call and we're trying to 330 // disconnect the active (carrier) call. 331 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 332 CompletableFuture<Boolean> resultFuture = mController 333 .holdActiveCallForNewCallWithSequencing(mNewCall, 334 CallsManager.REQUEST_ORIGIN_UNKNOWN); 335 verify(mHeldCall, timeout(SEQUENCING_TIMEOUT_MS)).disconnect(); 336 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold(); 337 verify(mNewCall).increaseHeldByThisCallCount(); 338 assertTrue(waitForFutureResult(resultFuture, false)); 339 } 340 341 @Test 342 @SmallTest testHoldCallForNewCall_SupportsHold_NoHeldCall()343 public void testHoldCallForNewCall_SupportsHold_NoHeldCall() { 344 setPhoneAccounts(mNewCall, mActiveCall, false); 345 setActiveCallFocus(mActiveCall); 346 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 347 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(true); 348 when(mCallsManager.getFirstCallWithState(anyInt())).thenReturn(null); 349 when(mActiveCall.hold()).thenReturn(CompletableFuture.completedFuture(true)); 350 351 // Cross phone account case (sequencing enabled) 352 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 353 CompletableFuture<Boolean> resultFuture = mController 354 .holdActiveCallForNewCallWithSequencing(mNewCall, 355 CallsManager.REQUEST_ORIGIN_UNKNOWN); 356 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold(); 357 verify(mNewCall).increaseHeldByThisCallCount(); 358 assertTrue(waitForFutureResult(resultFuture, false)); 359 } 360 361 @Test 362 @SmallTest testHoldCallForNewCall_DoesNotSupportHold_Disconnect()363 public void testHoldCallForNewCall_DoesNotSupportHold_Disconnect() { 364 setPhoneAccounts(mNewCall, mActiveCall, false); 365 setActiveCallFocus(mActiveCall); 366 when(mCallsManager.getCalls()).thenReturn(Collections.singletonList(mActiveCall)); 367 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 368 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(false); 369 when(mActiveCall.disconnect(anyString())).thenReturn( 370 CompletableFuture.completedFuture(true)); 371 when(mActiveCall.isEmergencyCall()).thenReturn(false); 372 373 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 374 CompletableFuture<Boolean> resultFuture = mController 375 .holdActiveCallForNewCallWithSequencing(mNewCall, 376 CallsManager.REQUEST_ORIGIN_UNKNOWN); 377 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).disconnect(anyString()); 378 assertTrue(waitForFutureResult(resultFuture, false)); 379 } 380 381 @Test 382 @SmallTest testHoldCallForNewCallFail_SupportsHold_VoipPstn()383 public void testHoldCallForNewCallFail_SupportsHold_VoipPstn() { 384 setPhoneAccounts(mNewCall, mActiveCall, false); 385 setActiveCallFocus(mActiveCall); 386 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 387 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(true); 388 when(mCallsManager.getFirstCallWithState(anyInt())).thenReturn(mHeldCall); 389 when(mHeldCall.isSelfManaged()).thenReturn(false); 390 when(mNewCall.isSelfManaged()).thenReturn(true); 391 392 // Verify that we abort transaction when there's a new (VOIP) call and we're trying to 393 // disconnect the active (carrier) call. 394 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 395 CompletableFuture<Boolean> resultFuture = mController 396 .holdActiveCallForNewCallWithSequencing(mNewCall, 397 CallsManager.REQUEST_ORIGIN_UNKNOWN); 398 assertFalse(waitForFutureResult(resultFuture, true)); 399 } 400 401 @Test 402 @SmallTest testHoldCallForNewCall_DoesNotSupportHold_SameManagedPA()403 public void testHoldCallForNewCall_DoesNotSupportHold_SameManagedPA() { 404 setPhoneAccounts(mNewCall, mActiveCall, true); 405 setActiveCallFocus(mActiveCall); 406 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 407 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(false); 408 when(mActiveCall.isEmergencyCall()).thenReturn(false); 409 410 assertTrue(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 411 CompletableFuture<Boolean> resultFuture = mController 412 .holdActiveCallForNewCallWithSequencing(mNewCall, 413 CallsManager.REQUEST_ORIGIN_UNKNOWN); 414 assertTrue(waitForFutureResult(resultFuture, true)); 415 } 416 417 @Test 418 @SmallTest testHoldCallForNewCallFail_DoesNotSupportHold_Reject()419 public void testHoldCallForNewCallFail_DoesNotSupportHold_Reject() { 420 setPhoneAccounts(mNewCall, mActiveCall, false); 421 setActiveCallFocus(mActiveCall); 422 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 423 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(false); 424 when(mNewCall.reject(anyBoolean(), anyString(), anyString())) 425 .thenReturn(CompletableFuture.completedFuture(true)); 426 when(mActiveCall.isEmergencyCall()).thenReturn(true); 427 428 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 429 CompletableFuture<Boolean> resultFuture = mController 430 .holdActiveCallForNewCallWithSequencing(mNewCall, 431 CallsManager.REQUEST_ORIGIN_UNKNOWN); 432 verify(mNewCall, timeout(SEQUENCING_TIMEOUT_MS)).reject( 433 anyBoolean(), anyString(), anyString()); 434 assertFalse(waitForFutureResult(resultFuture, true)); 435 } 436 437 @Test 438 @SmallTest testHoldCallForNewCallFail_DoesNotSupportHold_Abort()439 public void testHoldCallForNewCallFail_DoesNotSupportHold_Abort() { 440 setPhoneAccounts(mNewCall, mActiveCall, false); 441 setActiveCallFocus(mActiveCall); 442 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 443 when(mCallsManager.supportsHold(mActiveCall)).thenReturn(false); 444 when(mActiveCall.isEmergencyCall()).thenReturn(false); 445 when(mActiveCall.isSelfManaged()).thenReturn(false); 446 when(mNewCall.isSelfManaged()).thenReturn(true); 447 448 assertFalse(mController.arePhoneAccountsSame(mNewCall, mActiveCall)); 449 CompletableFuture<Boolean> resultFuture = mController 450 .holdActiveCallForNewCallWithSequencing(mNewCall, 451 CallsManager.REQUEST_ORIGIN_UNKNOWN); 452 assertFalse(waitForFutureResult(resultFuture, true)); 453 } 454 455 @Test 456 @SmallTest testUnholdCallNoActiveCall()457 public void testUnholdCallNoActiveCall() { 458 setActiveCallFocus(null); 459 mController.unholdCall(mHeldCall); 460 verify(mCallsManager).requestActionUnholdCall(eq(mHeldCall), eq(null)); 461 } 462 463 @Test 464 @SmallTest testUnholdCallSwapCase()465 public void testUnholdCallSwapCase() { 466 when(mActiveCall.can(eq(Connection.CAPABILITY_SUPPORT_HOLD))).thenReturn(true); 467 when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(true)); 468 when(mActiveCall.isLocallyDisconnecting()).thenReturn(false); 469 setPhoneAccounts(mHeldCall, mActiveCall, false); 470 setActiveCallFocus(mActiveCall); 471 472 mController.unholdCall(mHeldCall); 473 assertFalse(mController.arePhoneAccountsSame(mActiveCall, mHeldCall)); 474 verify(mActiveCall).hold(anyString()); 475 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS)) 476 .requestActionUnholdCall(eq(mHeldCall), eq(ACTIVE_CALL_ID)); 477 } 478 479 @Test 480 @SmallTest testUnholdCallFail_DoesNotSupportHold()481 public void testUnholdCallFail_DoesNotSupportHold() { 482 when(mActiveCall.can(eq(Connection.CAPABILITY_SUPPORT_HOLD))).thenReturn(false); 483 when(mActiveCall.isEmergencyCall()).thenReturn(true); 484 when(mActiveCall.isLocallyDisconnecting()).thenReturn(false); 485 setPhoneAccounts(mHeldCall, mActiveCall, false); 486 setActiveCallFocus(mActiveCall); 487 488 // Emergency call case 489 mController.unholdCall(mHeldCall); 490 assertFalse(mController.arePhoneAccountsSame(mActiveCall, mHeldCall)); 491 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(0)) 492 .requestActionUnholdCall(eq(mHeldCall), anyString()); 493 } 494 495 @Test 496 @SmallTest testUnholdFail()497 public void testUnholdFail() { 498 // Fail the hold. 499 when(mActiveCall.can(eq(Connection.CAPABILITY_SUPPORT_HOLD))).thenReturn(true); 500 when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(false)); 501 when(mActiveCall.isLocallyDisconnecting()).thenReturn(false); 502 // Use different phone accounts so that the sequencing code path is hit. 503 setPhoneAccounts(mHeldCall, mActiveCall, false); 504 setActiveCallFocus(mActiveCall); 505 506 mController.unholdCall(mHeldCall); 507 assertFalse(mController.arePhoneAccountsSame(mActiveCall, mHeldCall)); 508 verify(mActiveCall).hold(anyString()); 509 // Verify unhold is never reached. 510 verify(mCallsManager, never()) 511 .requestActionUnholdCall(eq(mHeldCall), anyString()); 512 } 513 514 @SmallTest 515 @Test testMakeRoomForOutgoingEmergencyCall_SamePkg()516 public void testMakeRoomForOutgoingEmergencyCall_SamePkg() { 517 // Ensure that the live call and emergency call are from the same pkg. 518 when(mActiveCall.getTargetPhoneAccount()).thenReturn(mHandle1); 519 when(mNewCall.getTargetPhoneAccount()).thenReturn(mHandle1); 520 when(mRingingCall.getTargetPhoneAccount()).thenReturn(mHandle2); 521 setupMakeRoomForOutgoingEmergencyCallMocks(); 522 523 CompletableFuture<Boolean> future = mController.makeRoomForOutgoingCall(true, mNewCall); 524 verify(mRingingCall, timeout(SEQUENCING_TIMEOUT_MS)) 525 .reject(anyBoolean(), eq(null), anyString()); 526 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold(anyString()); 527 assertTrue(waitForFutureResult(future, false)); 528 } 529 530 @SmallTest 531 @Test testMakeRoomForOutgoingEmergencyCall_CanHold()532 public void testMakeRoomForOutgoingEmergencyCall_CanHold() { 533 // Ensure that the live call and emergency call are from different pkgs. 534 when(mActiveCall.getTargetPhoneAccount()).thenReturn(mHandle1); 535 when(mNewCall.getTargetPhoneAccount()).thenReturn(mHandle2); 536 when(mRingingCall.getTargetPhoneAccount()).thenReturn(mHandle2); 537 setupMakeRoomForOutgoingEmergencyCallMocks(); 538 539 CompletableFuture<Boolean> future = mController.makeRoomForOutgoingCall(true, mNewCall); 540 verify(mRingingCall, timeout(SEQUENCING_TIMEOUT_MS)) 541 .reject(anyBoolean(), eq(null), anyString()); 542 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold(anyString()); 543 assertTrue(waitForFutureResult(future, false)); 544 } 545 546 @SmallTest 547 @Test testMakeRoomForOutgoingEmergencyCall_DoesNotSupportHoldingEmergency()548 public void testMakeRoomForOutgoingEmergencyCall_DoesNotSupportHoldingEmergency() { 549 setupMakeRoomForOutgoingEmergencyCallMocks(); 550 when(mCallsManager.getCalls()).thenReturn(List.of(mActiveCall, mRingingCall)); 551 when(mActiveCall.getTargetPhoneAccount()).thenReturn(mHandle1); 552 // Set the KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL carrier config to false for the active 553 // call's phone account. 554 PersistableBundle bundle = new PersistableBundle(); 555 bundle.putBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, false); 556 when(mCallsManager.getCarrierConfigForPhoneAccount(eq(mHandle1))).thenReturn(bundle); 557 when(mNewCall.getTargetPhoneAccount()).thenReturn(mHandle2); 558 when(mRingingCall.getTargetPhoneAccount()).thenReturn(mHandle2); 559 560 mController.makeRoomForOutgoingCall(true, mNewCall); 561 // Verify that the active call got disconnected as it doesn't support holding for emergency. 562 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).disconnect(anyString()); 563 } 564 565 @Test 566 @SmallTest testMakeRoomForOutgoingCall()567 public void testMakeRoomForOutgoingCall() { 568 setupMakeRoomForOutgoingCallMocks(); 569 when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(true)); 570 Analytics.CallInfo newCallAnalytics = mock(Analytics.CallInfo.class); 571 Analytics.CallInfo activeCallAnalytics = mock(Analytics.CallInfo.class); 572 when(mNewCall.getAnalytics()).thenReturn(newCallAnalytics); 573 when(mActiveCall.getAnalytics()).thenReturn(activeCallAnalytics); 574 when(mCallsManager.canHold(mActiveCall)).thenReturn(true); 575 576 CompletableFuture<Boolean> future = mController.makeRoomForOutgoingCall(false, mNewCall); 577 verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).hold(anyString()); 578 verify(newCallAnalytics).setCallIsAdditional(eq(true)); 579 verify(activeCallAnalytics).setCallIsInterrupted(eq(true)); 580 assertTrue(waitForFutureResult(future, false)); 581 } 582 583 @Test 584 @SmallTest testMakeRoomForOutgoingCallFail_MaxCalls()585 public void testMakeRoomForOutgoingCallFail_MaxCalls() { 586 setupMakeRoomForOutgoingCallMocks(); 587 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 588 when(mCallsManager.hasMaximumManagedHoldingCalls(mNewCall)).thenReturn(true); 589 590 CompletableFuture<Boolean> future = mController.makeRoomForOutgoingCall(false, mNewCall); 591 verify(mNewCall).setStartFailCause(eq(CallFailureCause.MAX_OUTGOING_CALLS)); 592 assertFalse(waitForFutureResult(future, true)); 593 } 594 595 @Test 596 @SmallTest testMakeRoomForOutgoingCallFail_CannotHold()597 public void testMakeRoomForOutgoingCallFail_CannotHold() { 598 setupMakeRoomForOutgoingCallMocks(); 599 when(mCallsManager.canHold(mActiveCall)).thenReturn(false); 600 when(mCallsManager.hasMaximumManagedHoldingCalls(mNewCall)).thenReturn(false); 601 602 CompletableFuture<Boolean> future = mController.makeRoomForOutgoingCall(false, mNewCall); 603 verify(mNewCall).setStartFailCause(eq(CallFailureCause.CANNOT_HOLD_CALL)); 604 assertFalse(waitForFutureResult(future, true)); 605 } 606 607 @Test 608 @SmallTest testMakeRoomForOutgoingCallFail_RingingCall()609 public void testMakeRoomForOutgoingCallFail_RingingCall() { 610 when(mNewCall.isSelfManaged()).thenReturn(false); 611 when(mCallsManager.hasManagedRingingOrSimulatedRingingCall()).thenReturn(true); 612 613 CompletableFuture<Boolean> future = mController.makeRoomForOutgoingCall(false, mNewCall); 614 assertFalse(waitForFutureResult(future, true)); 615 } 616 617 @Test 618 @SmallTest testDisconnectCallSuccess()619 public void testDisconnectCallSuccess() { 620 when(mActiveCall.disconnect()).thenReturn(CompletableFuture.completedFuture(true)); 621 int previousState = CallState.ACTIVE; 622 mController.disconnectCall(mActiveCall, previousState); 623 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS)) 624 .processDisconnectCallAndCleanup(eq(mActiveCall), eq(previousState)); 625 } 626 627 @Test 628 @SmallTest testDisconnectCallFail()629 public void testDisconnectCallFail() { 630 when(mActiveCall.disconnect()).thenReturn(CompletableFuture.completedFuture(false)); 631 int previousState = CallState.ACTIVE; 632 mController.disconnectCall(mActiveCall, previousState); 633 verify(mCallsManager, timeout(SEQUENCING_TIMEOUT_MS).times(0)) 634 .processDisconnectCallAndCleanup(eq(mActiveCall), eq(previousState)); 635 } 636 637 @Test 638 @SmallTest testMmiCodeRestrictionReject()639 public void testMmiCodeRestrictionReject() { 640 // Verify that when calls are detected across other phone accounts, 641 // that the MMI code is rejected. 642 when(mNewCall.getTargetPhoneAccount()).thenReturn(mHandle1); 643 when(mCallsManager.getNumCallsWithStateWithoutHandle(CALL_FILTER_ALL, mNewCall, 644 mHandle1, ONGOING_CALL_STATES)).thenReturn(1); 645 assertTrue(mController.hasMmiCodeRestriction(mNewCall)); 646 verify(mNewCall).setOverrideDisconnectCauseCode(any(DisconnectCause.class)); 647 } 648 649 @Test 650 @SmallTest testMmiCodeRestrictionAllow()651 public void testMmiCodeRestrictionAllow() { 652 // Verify that when no calls are detected across other phone accounts, 653 // that the MMI code is allowed. 654 when(mNewCall.getTargetPhoneAccount()).thenReturn(mHandle1); 655 when(mCallsManager.getNumCallsWithStateWithoutHandle(CALL_FILTER_ALL, mNewCall, 656 mHandle1, ONGOING_CALL_STATES)).thenReturn(0); 657 assertFalse(mController.hasMmiCodeRestriction(mNewCall)); 658 verify(mNewCall, times(0)).setOverrideDisconnectCauseCode(any(DisconnectCause.class)); 659 } 660 661 /* Helpers */ setPhoneAccounts(Call call1, Call call2, boolean useSamePhoneAccount)662 private void setPhoneAccounts(Call call1, Call call2, boolean useSamePhoneAccount) { 663 when(call1.getTargetPhoneAccount()).thenReturn(mHandle1); 664 when(call2.getTargetPhoneAccount()).thenReturn(useSamePhoneAccount ? mHandle1 : mHandle2); 665 } 666 setActiveCallFocus(Call call)667 private void setActiveCallFocus(Call call) { 668 when(mCallsManager.getConnectionServiceFocusManager()) 669 .thenReturn(mConnectionServiceFocusManager); 670 when(mConnectionServiceFocusManager.getCurrentFocusCall()).thenReturn(call); 671 } 672 setupMakeRoomForOutgoingEmergencyCallMocks()673 private void setupMakeRoomForOutgoingEmergencyCallMocks() { 674 when(mNewCall.isEmergencyCall()).thenReturn(true); 675 when(mCallsManager.hasRingingOrSimulatedRingingCall()).thenReturn(true); 676 when(mCallsManager.getRingingOrSimulatedRingingCall()).thenReturn(mRingingCall); 677 when(mCallsManager.hasMaximumLiveCalls(mNewCall)).thenReturn(true); 678 when(mCallsManager.getFirstCallWithLiveState()).thenReturn(mActiveCall); 679 when(mCallsManager.hasMaximumOutgoingCalls(mNewCall)).thenReturn(false); 680 when(mCallsManager.hasMaximumManagedHoldingCalls(mNewCall)).thenReturn(false); 681 when(mCallsManager.canHold(mActiveCall)).thenReturn(true); 682 683 // Setup analytics mocks 684 setupCallAnalytics(Arrays.asList(mNewCall, mActiveCall, mRingingCall)); 685 686 // Setup ecall related checks 687 setupEmergencyCallPaCapabilities(); 688 setupCarrierConfigAllowEmergencyCallHold(); 689 690 // Setup CompletableFuture mocking for call actions 691 when(mRingingCall.reject(anyBoolean(), eq(null), anyString())) 692 .thenReturn(CompletableFuture.completedFuture(true)); 693 when(mActiveCall.hold(anyString())).thenReturn( 694 CompletableFuture.completedFuture(true)); 695 } 696 setupEmergencyCallPaCapabilities()697 private void setupEmergencyCallPaCapabilities() { 698 PhoneAccount pa = mock(PhoneAccount.class); 699 PhoneAccountRegistrar paRegistrar = mock(PhoneAccountRegistrar.class); 700 when(mCallsManager.getPhoneAccountRegistrar()).thenReturn(paRegistrar); 701 when(paRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class))).thenReturn(pa); 702 when(pa.getCapabilities()).thenReturn(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS); 703 } 704 setupCarrierConfigAllowEmergencyCallHold()705 private void setupCarrierConfigAllowEmergencyCallHold() { 706 PersistableBundle bundle = mock(PersistableBundle.class); 707 when(mCallsManager.getCarrierConfigForPhoneAccount(any(PhoneAccountHandle.class))) 708 .thenReturn(bundle); 709 when(bundle.getBoolean( 710 CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true)) 711 .thenReturn(true); 712 } 713 setupMakeRoomForOutgoingCallMocks()714 private void setupMakeRoomForOutgoingCallMocks() { 715 when(mCallsManager.hasMaximumLiveCalls(mNewCall)).thenReturn(true); 716 when(mCallsManager.getFirstCallWithLiveState()).thenReturn(mActiveCall); 717 setPhoneAccounts(mActiveCall, mNewCall, false); 718 when(mActiveCall.isConference()).thenReturn(false); 719 when(mCallsManager.hasMaximumOutgoingCalls(mNewCall)).thenReturn(false); 720 } 721 setupHoldActiveCallForNewCallFailMocks()722 private void setupHoldActiveCallForNewCallFailMocks() { 723 // Setup holdActiveCallForNewCallWithSequencing to fail. 724 setPhoneAccounts(mNewCall, mActiveCall, false); 725 setActiveCallFocus(mActiveCall); 726 when(mCallsManager.canHold(mActiveCall)).thenReturn(true); 727 when(mActiveCall.hold(anyString())).thenReturn(CompletableFuture.completedFuture(false)); 728 } 729 verifyTransactionHoldActiveCallForNewCall( OutcomeReceiver<Boolean, CallException> callback, CountDownLatch latch)730 private void verifyTransactionHoldActiveCallForNewCall( 731 OutcomeReceiver<Boolean, CallException> callback, CountDownLatch latch) { 732 mController.transactionHoldPotentialActiveCallForNewCallSequencing(mNewCall, callback); 733 while (latch.getCount() > 0) { 734 try { 735 latch.await(SEQUENCING_TIMEOUT_MS, TimeUnit.MILLISECONDS); 736 } catch (InterruptedException e) { 737 // do nothing 738 } 739 } 740 assertEquals(latch.getCount(), 0); 741 } 742 getOutgoingCallAttributes()743 private CallAttributes getOutgoingCallAttributes() { 744 return new CallAttributes.Builder(mHandle1, 745 CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI) 746 .setCallType(CallAttributes.AUDIO_CALL) 747 .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE) 748 .build(); 749 } 750 setupCallAnalytics(List<Call> calls)751 private void setupCallAnalytics(List<Call> calls) { 752 for (Call call: calls) { 753 Analytics.CallInfo analyticsInfo = mock(Analytics.CallInfo.class); 754 when(call.getAnalytics()).thenReturn(analyticsInfo); 755 } 756 } 757 waitForFutureResult(CompletableFuture<Boolean> future, boolean defaultValue)758 private boolean waitForFutureResult(CompletableFuture<Boolean> future, boolean defaultValue) { 759 boolean result = defaultValue; 760 try { 761 result = future.get(SEQUENCING_TIMEOUT_MS, TimeUnit.MILLISECONDS); 762 } catch (Exception e) { 763 // Pass through 764 } 765 return result; 766 } 767 } 768 769