• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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