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 android.telecom.cts.cuj.app.integration; 18 19 import static android.telecom.Call.STATE_ACTIVE; 20 import static android.telecom.Call.STATE_DIALING; 21 import static android.telecom.Call.STATE_DISCONNECTED; 22 import static android.telecom.Call.STATE_HOLDING; 23 import static android.telecom.Call.STATE_RINGING; 24 import static android.telecom.cts.apps.TelecomTestApp.ConnectionServiceVoipAppMain; 25 import static android.telecom.cts.apps.TelecomTestApp.ManagedConnectionServiceApp; 26 import static android.telecom.cts.apps.TelecomTestApp.ManagedConnectionServiceAppClone; 27 import static android.telecom.cts.apps.TelecomTestApp.TransactionalVoipAppMain; 28 29 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.assertNotNull; 31 import static org.junit.Assume.assumeTrue; 32 33 import android.platform.test.annotations.RequiresFlagsEnabled; 34 import android.telecom.cts.apps.AppControlWrapper; 35 import android.telecom.cts.apps.CallSequencingValidator; 36 import android.telecom.cts.apps.CallStateTransitionOperation; 37 import android.telecom.cts.apps.TelecomTestApp; 38 import android.telecom.cts.cuj.BaseAppVerifier; 39 import android.telecom.cts.cuj.TestUtils; 40 41 import com.android.server.telecom.flags.Flags; 42 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.junit.runners.Parameterized; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 50 /** Call sequencing multi-call tests dealing with ECC */ 51 @RunWith(Parameterized.class) 52 @RequiresFlagsEnabled({Flags.FLAG_ENABLE_CALL_SEQUENCING}) 53 public class CallSequencingMultiCallEmergencyCallTest extends BaseAppVerifier { 54 /** 55 * Test parameters that consist of the following information: (1) telecom test apps being used 56 * for the normal + normal + ECC (2) the call holdable state of the 1st + 2nd calls and if the 57 * 2nd call is an outgoing call. (3) the expected call states of the 1st + 2nd calls after 58 * placing the 2nd call and the call states of the same calls after the ECC is placed. The last 59 * value indicates how many calls should be disconnected as a result of placing the ECC. 60 */ 61 private static TelecomTestApp[][] sTelecomTestApps = { 62 /* 1st call test app 63 * 2nd call test app 64 * ECC test app (expectation) 65 */ 66 { 67 ManagedConnectionServiceAppClone, 68 ManagedConnectionServiceApp, 69 ManagedConnectionServiceApp 70 }, // 1 71 { 72 ManagedConnectionServiceApp, 73 ManagedConnectionServiceAppClone, 74 ManagedConnectionServiceAppClone 75 }, // 2 76 { 77 ManagedConnectionServiceApp, ManagedConnectionServiceApp, ManagedConnectionServiceApp 78 }, // 3 79 {TransactionalVoipAppMain, ManagedConnectionServiceApp, ManagedConnectionServiceApp}, // 4 80 {ManagedConnectionServiceApp, TransactionalVoipAppMain, ManagedConnectionServiceApp}, // 5 81 { 82 ConnectionServiceVoipAppMain, ManagedConnectionServiceApp, ManagedConnectionServiceApp 83 }, // 6 84 { 85 ManagedConnectionServiceApp, ConnectionServiceVoipAppMain, ManagedConnectionServiceApp 86 }, // 7 87 { 88 ManagedConnectionServiceApp, 89 ManagedConnectionServiceAppClone, 90 ManagedConnectionServiceApp 91 }, // 8 92 { 93 ManagedConnectionServiceAppClone, 94 ManagedConnectionServiceApp, 95 ManagedConnectionServiceAppClone 96 }, // 9 97 { 98 ManagedConnectionServiceApp, ManagedConnectionServiceApp, ManagedConnectionServiceApp 99 }, // 10 100 { 101 ManagedConnectionServiceApp, 102 ManagedConnectionServiceAppClone, 103 ManagedConnectionServiceApp 104 }, // 11 105 { 106 ManagedConnectionServiceAppClone, 107 ManagedConnectionServiceApp, 108 ManagedConnectionServiceAppClone 109 }, // 12 110 // Todo: - Disabling this test for now (this will be corrected by b/403620519) 111 // { 112 // ManagedConnectionServiceApp, ManagedConnectionServiceApp, 113 // ManagedConnectionServiceApp 114 // }, // 13 115 { 116 ManagedConnectionServiceApp, 117 ManagedConnectionServiceAppClone, 118 ManagedConnectionServiceApp 119 }, // 14 120 { 121 ManagedConnectionServiceAppClone, 122 ManagedConnectionServiceApp, 123 ManagedConnectionServiceAppClone 124 }, // 15 125 {TransactionalVoipAppMain, ManagedConnectionServiceApp, ManagedConnectionServiceApp}, // 16 126 { 127 ConnectionServiceVoipAppMain, ManagedConnectionServiceApp, ManagedConnectionServiceApp 128 }, // 17 129 { 130 ManagedConnectionServiceApp, 131 ManagedConnectionServiceAppClone, 132 ManagedConnectionServiceApp 133 }, // 18 134 { 135 ManagedConnectionServiceAppClone, 136 ManagedConnectionServiceApp, 137 ManagedConnectionServiceAppClone 138 }, // 19 139 { 140 ManagedConnectionServiceApp, ManagedConnectionServiceApp, ManagedConnectionServiceApp 141 }, // 20 142 { 143 ManagedConnectionServiceApp, ManagedConnectionServiceApp, ManagedConnectionServiceApp 144 }, // 21 145 }; 146 147 private static boolean[][] sCallsHoldableAndSecondCallOutgoing = { 148 /* is1stCallHoldable, 149 * is2ndCallHoldable, 150 * is2ndCallOutgoing 151 */ 152 /* holdable tests */ 153 {true, true, true}, // 1 154 {true, true, true}, // 2 155 {true, true, true}, // 3 156 {true, true, true}, // 4 157 {true, true, true}, // 5 158 {true, true, true}, // 6 159 {true, true, true}, // 7 160 {true, true, true}, // 8 161 {true, true, true}, // 9 162 {true, true, true}, // 10 163 {true, true, false}, // 11 164 {true, true, false}, // 12 165 // {true, true, false}, // 13 166 /* non-holdable tests */ 167 {true, false, true}, // 14 168 {true, false, true}, // 15 169 {true, false, true}, // 16 170 {true, false, true}, // 17 171 {false, true, false}, // 18 172 {false, true, false}, // 19 173 {false, false, false}, // 20 174 {false, false, true}, // 21 175 }; 176 private static int[][] sExpectedCallStatesAndNumCallsDisconnected = { 177 /* expected1stCallStateAfter2nd, 178 * expected2ndCallStateAfter1st, 179 * expected1stCallStateAfterEcc, 180 * expected2ndCallStateAfterEcc, 181 * numDisconnectDueToEcc 182 */ 183 {STATE_HOLDING, STATE_ACTIVE, STATE_DISCONNECTED, STATE_HOLDING, 1}, // 1 184 {STATE_HOLDING, STATE_ACTIVE, STATE_DISCONNECTED, STATE_HOLDING, 1}, // 2 185 {STATE_HOLDING, STATE_ACTIVE, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 3 186 {STATE_HOLDING, STATE_ACTIVE, STATE_DISCONNECTED, STATE_HOLDING, 1}, // 4 187 {STATE_HOLDING, STATE_ACTIVE, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 5 188 {STATE_HOLDING, STATE_ACTIVE, STATE_DISCONNECTED, STATE_HOLDING, 1}, // 6 189 {STATE_HOLDING, STATE_ACTIVE, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 7 190 {STATE_ACTIVE, STATE_DIALING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 8 191 {STATE_ACTIVE, STATE_DIALING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 9 192 {STATE_ACTIVE, STATE_DIALING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 10 193 {STATE_ACTIVE, STATE_RINGING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 11 194 {STATE_ACTIVE, STATE_RINGING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 12 195 // {STATE_ACTIVE, STATE_RINGING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 13 196 {STATE_HOLDING, STATE_ACTIVE, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 14 197 {STATE_HOLDING, STATE_ACTIVE, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 15 198 {STATE_HOLDING, STATE_ACTIVE, STATE_DISCONNECTED, STATE_HOLDING, 1}, // 16 199 {STATE_HOLDING, STATE_ACTIVE, STATE_DISCONNECTED, STATE_HOLDING, 1}, // 17 200 {STATE_ACTIVE, STATE_RINGING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 18 201 {STATE_ACTIVE, STATE_RINGING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 19 202 {STATE_ACTIVE, STATE_RINGING, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 20 203 {STATE_HOLDING, STATE_ACTIVE, STATE_HOLDING, STATE_DISCONNECTED, 1}, // 21 204 }; 205 206 private static class CallParameters { 207 private TelecomTestApp[] mTestApps; 208 private int[] mExpectedCallStatesAndNumCallsDisconnected; 209 private boolean[] mCallsHoldableAndSecondCallOutgoing; 210 CallParameters( TelecomTestApp[] testApps, int[] expectedCallStatesAndNumCallsDisconnected, boolean[] callsHoldableAndSecondCallOutgoing)211 CallParameters( 212 TelecomTestApp[] testApps, 213 int[] expectedCallStatesAndNumCallsDisconnected, 214 boolean[] callsHoldableAndSecondCallOutgoing) { 215 mTestApps = testApps; 216 mExpectedCallStatesAndNumCallsDisconnected = expectedCallStatesAndNumCallsDisconnected; 217 mCallsHoldableAndSecondCallOutgoing = callsHoldableAndSecondCallOutgoing; 218 } 219 getTestApps()220 private TelecomTestApp[] getTestApps() { 221 return mTestApps; 222 } 223 getExpectedCallStatesAndNumsCallsDisconnected()224 private int[] getExpectedCallStatesAndNumsCallsDisconnected() { 225 return mExpectedCallStatesAndNumCallsDisconnected; 226 } 227 getCallsHoldableAndSecondCallOutgoing()228 private boolean[] getCallsHoldableAndSecondCallOutgoing() { 229 return mCallsHoldableAndSecondCallOutgoing; 230 } 231 232 @Override toString()233 public String toString() { 234 StringBuilder nameBuilder = new StringBuilder(); 235 nameBuilder.append("\nTelecom test apps for 1st + 2nd + 3rd call: "); 236 for (TelecomTestApp testApp : mTestApps) { 237 nameBuilder.append(testApp); 238 nameBuilder.append(", "); 239 } 240 nameBuilder.append( 241 "\nFirst and second call holdability and second " + "call is outgoing: "); 242 for (boolean holdabilityOrOutgoingState : mCallsHoldableAndSecondCallOutgoing) { 243 nameBuilder.append(holdabilityOrOutgoingState); 244 nameBuilder.append(", "); 245 } 246 nameBuilder.append( 247 "\n" 248 + "Expected call states of 1st + 2nd call after placing 2nd call, 1st + 2nd" 249 + " call after placing the ECC, and expected number of calls disconnected" 250 + " as a result of placing the ECC: "); 251 for (int expectedCallStateOrNumsCallsDisconnected : 252 mExpectedCallStatesAndNumCallsDisconnected) { 253 nameBuilder.append(expectedCallStateOrNumsCallsDisconnected); 254 nameBuilder.append(", "); 255 } 256 return nameBuilder.toString(); 257 } 258 } 259 260 @Override setUp()261 public void setUp() throws Exception { 262 super.setUp(); 263 // Emergency calling is not supported on devices without FEATURE_TELEPHONY 264 // or FEATURE_TELECOM (i.e. Tangor). 265 assumeTrue(mShouldTestTelecom && TestUtils.hasTelephonyFeature(mContext)); 266 setupForEmergencyCalling(); 267 } 268 269 @Override tearDown()270 public void tearDown() throws Exception { 271 super.tearDown(); 272 tearDownEmergencyCalling(); 273 } 274 275 @Parameterized.Parameters(name = "{0}") data()276 public static Iterable<CallParameters> data() { 277 List<CallParameters> params = new ArrayList<>(); 278 for (int i = 0; i < sTelecomTestApps.length; i++) { 279 CallParameters param = 280 new CallParameters( 281 sTelecomTestApps[i], 282 sExpectedCallStatesAndNumCallsDisconnected[i], 283 sCallsHoldableAndSecondCallOutgoing[i]); 284 params.add(param); 285 } 286 return params; 287 } 288 289 public final CallParameters mParams; 290 CallSequencingMultiCallEmergencyCallTest(CallParameters params)291 public CallSequencingMultiCallEmergencyCallTest(CallParameters params) { 292 mParams = params; 293 } 294 295 @Test testMultiCallEcc()296 public void testMultiCallEcc() throws Exception { 297 TelecomTestApp[] testApps = mParams.getTestApps(); 298 int[] expectedCallStatesAndNumCallsDisconnected = 299 mParams.getExpectedCallStatesAndNumsCallsDisconnected(); 300 boolean[] callsHoldableAndSecondCallOutgoing = 301 mParams.getCallsHoldableAndSecondCallOutgoing(); 302 assertEquals(testApps.length, 3); 303 assertEquals(callsHoldableAndSecondCallOutgoing.length, 3); 304 assertEquals(expectedCallStatesAndNumCallsDisconnected.length, 5); 305 verifyAddEccWithTwoCalls( 306 testApps[0], 307 testApps[1], 308 testApps[2], 309 callsHoldableAndSecondCallOutgoing[0], 310 callsHoldableAndSecondCallOutgoing[1], 311 callsHoldableAndSecondCallOutgoing[2], 312 expectedCallStatesAndNumCallsDisconnected[0], 313 expectedCallStatesAndNumCallsDisconnected[1], 314 expectedCallStatesAndNumCallsDisconnected[2], 315 expectedCallStatesAndNumCallsDisconnected[3], 316 expectedCallStatesAndNumCallsDisconnected[4]); 317 } 318 verifyAddEccWithTwoCalls( TelecomTestApp app1, TelecomTestApp app2, TelecomTestApp app3, boolean is1stCallHoldable, boolean is2ndCallHoldable, boolean is2ndCallOutgoing, int expected1stCallStateAfter2nd, int expected2ndCallStateAfter1st, int expected1stCallStateAfterEcc, int expected2ndCallStateAfterEcc, int numDisconnectDueToEcc)319 private void verifyAddEccWithTwoCalls( 320 TelecomTestApp app1, 321 TelecomTestApp app2, 322 TelecomTestApp app3, 323 boolean is1stCallHoldable, 324 boolean is2ndCallHoldable, 325 boolean is2ndCallOutgoing, 326 int expected1stCallStateAfter2nd, 327 int expected2ndCallStateAfter1st, 328 int expected1stCallStateAfterEcc, 329 int expected2ndCallStateAfterEcc, 330 int numDisconnectDueToEcc) 331 throws Exception { 332 AppControlWrapper controlWrapperApp1 = null; 333 AppControlWrapper controlWrapperApp2 = null; 334 AppControlWrapper controlWrapperApp3 = null; 335 CallSequencingValidator c1Validator = new CallSequencingValidator(); 336 CallSequencingValidator c2Validator = new CallSequencingValidator(); 337 CallSequencingValidator c3Validator = new CallSequencingValidator(); 338 339 try { 340 controlWrapperApp1 = bindToApp(app1); 341 controlWrapperApp2 = bindToApp(app2); 342 controlWrapperApp3 = bindToApp(app3); 343 // Place an outgoing managed call. 344 String call1 = 345 addCallAndVerify( 346 controlWrapperApp1, 347 getRandomAttributes(app1, true /* isOutgoing */, is1stCallHoldable), 348 c1Validator); 349 verifyCallIsInState(call1, STATE_DIALING); 350 verifyCallStateTransition(c1Validator, call1, controlWrapperApp1, STATE_ACTIVE); 351 352 // Place another managed call and verify the existing call is either held or active (in 353 // the case of a dialing/ringing call). 354 String call2 = 355 addCallAndVerify( 356 controlWrapperApp2, 357 getRandomAttributes(app2, is2ndCallOutgoing, is2ndCallHoldable), 358 c2Validator); 359 if (expected1stCallStateAfter2nd != STATE_ACTIVE) { 360 verifyCallStateTransition( 361 c1Validator, call1, controlWrapperApp1, expected1stCallStateAfter2nd); 362 } 363 verifyCallStateTransition( 364 c2Validator, call2, controlWrapperApp2, expected2ndCallStateAfter1st); 365 366 // Place emergency call and verify the 1st and 2nd calls are either held/disconnected 367 // depending on the condition (Refer to the expected behavior from the test parameters 368 // above). The emergency call should go through the ManagedConnectionServiceApp phone 369 // account. 370 String emergencyCall = 371 addEmergencyCallAndVerify( 372 controlWrapperApp3, c3Validator, numDisconnectDueToEcc); 373 if (expected1stCallStateAfterEcc != expected1stCallStateAfter2nd) { 374 verifyCallStateTransition( 375 c1Validator, call1, controlWrapperApp1, expected1stCallStateAfterEcc); 376 } 377 if (expected2ndCallStateAfterEcc != expected2ndCallStateAfter1st) { 378 verifyCallStateTransition( 379 c2Validator, call2, controlWrapperApp2, expected2ndCallStateAfterEcc); 380 } 381 verifyCallIsInState(emergencyCall, STATE_DIALING); 382 setCallStateAndVerify(controlWrapperApp3, emergencyCall, STATE_ACTIVE); 383 384 // Clean up calls 385 if (expected1stCallStateAfterEcc != STATE_DISCONNECTED) { 386 setCallStateAndVerify(controlWrapperApp1, call1, STATE_DISCONNECTED); 387 } 388 if (expected2ndCallStateAfterEcc != STATE_DISCONNECTED) { 389 setCallStateAndVerify(controlWrapperApp2, call2, STATE_DISCONNECTED); 390 } 391 setCallStateAndVerify(controlWrapperApp3, emergencyCall, STATE_DISCONNECTED); 392 } finally { 393 List<AppControlWrapper> controls = new ArrayList<>(); 394 controls.add(controlWrapperApp1); 395 controls.add(controlWrapperApp2); 396 controls.add(controlWrapperApp3); 397 tearDownApps(controls); 398 } 399 } 400 verifyCallStateTransition( CallSequencingValidator validator, String call, AppControlWrapper app, int expectedCallState)401 private void verifyCallStateTransition( 402 CallSequencingValidator validator, 403 String call, 404 AppControlWrapper app, 405 int expectedCallState) 406 throws Exception { 407 if (expectedCallState == STATE_ACTIVE) { 408 verifyCallIsInState(call, STATE_DIALING); 409 setCallStateAndVerify(app, call, STATE_ACTIVE); 410 return; 411 } else if (expectedCallState == STATE_RINGING || expectedCallState == STATE_DIALING) { 412 verifyCallIsInState(call, expectedCallState); 413 return; 414 } 415 int transitionOp = -1; 416 String opString = ""; 417 if (expectedCallState == STATE_DISCONNECTED) { 418 transitionOp = CallStateTransitionOperation.OPERATION_DISCONNECT; 419 opString = "DISCONNECT"; 420 } else if (expectedCallState == STATE_HOLDING) { 421 transitionOp = CallStateTransitionOperation.OPERATION_HOLD; 422 opString = "HOLD"; 423 } 424 CallStateTransitionOperation op = validator.completePendingOperationOrTimeout(transitionOp); 425 assertNotNull(opString + " operation never received for call " + call, op); 426 verifyCallIsInState(call, expectedCallState); 427 } 428 } 429