1 /* 2 * Copyright (C) 2024 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.telephony.cts; 18 19 import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT; 20 import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_FET; 21 22 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER; 23 24 import static junit.framework.Assert.assertNotNull; 25 import static junit.framework.Assert.assertTrue; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.fail; 29 import static org.junit.Assume.assumeTrue; 30 31 import android.app.UiAutomation; 32 import android.content.Context; 33 import android.os.Build; 34 import android.os.SystemProperties; 35 import android.platform.test.annotations.RequiresFlagsEnabled; 36 import android.platform.test.flag.junit.CheckFlagsRule; 37 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 38 import android.telecom.PhoneAccount; 39 import android.telecom.PhoneAccountHandle; 40 import android.telecom.TelecomManager; 41 import android.telephony.AccessNetworkConstants; 42 import android.telephony.SubscriptionManager; 43 import android.telephony.TelephonyCallback; 44 import android.telephony.TelephonyManager; 45 import android.telephony.ims.ImsException; 46 import android.telephony.ims.ImsManager; 47 import android.telephony.ims.ImsMmTelManager; 48 import android.telephony.ims.ImsReasonInfo; 49 import android.telephony.ims.ImsRegistrationAttributes; 50 import android.telephony.ims.ImsService; 51 import android.telephony.ims.RegistrationManager; 52 import android.telephony.ims.cts.ImsServiceConnector; 53 import android.telephony.ims.cts.ImsUtils; 54 import android.telephony.ims.cts.TestImsService; 55 import android.telephony.ims.feature.ImsFeature; 56 import android.telephony.ims.stub.ImsFeatureConfiguration; 57 import android.telephony.ims.stub.ImsRegistrationImplBase; 58 import android.telephony.mockmodem.MockModemManager; 59 import android.util.Log; 60 import android.util.Pair; 61 62 import androidx.annotation.NonNull; 63 import androidx.test.ext.junit.runners.AndroidJUnit4; 64 import androidx.test.platform.app.InstrumentationRegistry; 65 66 import com.android.compatibility.common.util.ShellIdentityUtils; 67 import com.android.internal.telephony.flags.Flags; 68 69 import org.junit.After; 70 import org.junit.AfterClass; 71 import org.junit.Assert; 72 import org.junit.Before; 73 import org.junit.BeforeClass; 74 import org.junit.Rule; 75 import org.junit.Test; 76 import org.junit.runner.RunWith; 77 78 import java.util.HashSet; 79 import java.util.List; 80 import java.util.Set; 81 import java.util.concurrent.LinkedBlockingQueue; 82 import java.util.concurrent.TimeUnit; 83 import java.util.stream.Collectors; 84 85 @RunWith(AndroidJUnit4.class) 86 @RequiresFlagsEnabled(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) 87 public class SimultaneousCallingRestrictionsTest { 88 @Rule 89 public final CheckFlagsRule mCheckFlagsRule = 90 DeviceFlagsValueProvider.createCheckFlagsRule(); 91 private static ImsServiceConnector sServiceConnectorSlot0; 92 private static ImsServiceConnector sServiceConnectorSlot1; 93 private static TelephonyManager sTelephonyManager; 94 private static TelecomManager sTelecomManager; 95 private static MockModemManager sMockModemManager; 96 private static SimultaneousCallingListener sSimultaneousCallingListener; 97 private static List<PhoneAccountHandle> sCallCapablePhoneAccounts; 98 private static UiAutomation sUiAutomation; 99 private static boolean sIsMultiSimDevice; 100 private static boolean sIsMockModemAllowed; 101 private static Throwable sCapturedSetupThrowable; 102 private static boolean sFeatureEnabled; 103 private static int sTestSubSlot0 = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 104 private static int sTestSubSlot1 = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 105 private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem"; 106 private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem"; 107 private static final boolean DEBUG = !"user".equals(Build.TYPE); 108 public static final int TEST_TIMEOUT_MS = 5000; 109 private static final int TEST_SLOT_0 = 0; 110 private static final int TEST_SLOT_1 = 1; 111 private static final String TAG = "SimultaneousCallingRestrictionsTest"; 112 private static final int IMS_REGI_TECH_LTE = ImsRegistrationImplBase.REGISTRATION_TECH_LTE; 113 private static final int IMS_REGI_TECH_IWLAN = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN; 114 115 private static class SimultaneousCallingListener extends TelephonyCallback implements 116 TelephonyCallback.SimultaneousCellularCallingSupportListener { 117 private Set<Integer> mSimultaneousCallingSubIds = new HashSet<>(2); 118 119 @Override onSimultaneousCellularCallingSubscriptionsChanged( @onNull Set<Integer> simultaneousCallingSubscriptionIds)120 public void onSimultaneousCellularCallingSubscriptionsChanged( 121 @NonNull Set<Integer> simultaneousCallingSubscriptionIds) { 122 Log.d(TAG, "onSimultaneousCellularCallingSubscriptionsChanged from [" 123 + mSimultaneousCallingSubIds + "]to[" + simultaneousCallingSubscriptionIds 124 + "]"); 125 mSimultaneousCallingSubIds.clear(); 126 mSimultaneousCallingSubIds = simultaneousCallingSubscriptionIds; 127 } 128 getSimultaneousCallingSubIds()129 public Set<Integer> getSimultaneousCallingSubIds() { 130 return mSimultaneousCallingSubIds; 131 } 132 } 133 134 // NOTE: BeforeClass can NOT throw exceptions 135 @BeforeClass beforeAllTests()136 public static void beforeAllTests() { 137 // @Rule doesn't support skipping @BeforeClass, so we need to do this manually so we 138 // can skip setting up the mock modem if not needed. 139 sFeatureEnabled = Flags.simultaneousCallingIndications(); 140 if (!ImsUtils.shouldTestTelephony()) { 141 Log.d(TAG, "beforeAllTests: Telephony Feature is not enabled on this device. "); 142 return; 143 } 144 if (!sFeatureEnabled) { 145 Log.d(TAG, "beforeAllTests: Simultaneous Calling is not enabled on this device "); 146 return; 147 } 148 Log.d(TAG, "beforeAllTests: begin"); 149 // Configure the MockModem: 150 sTelephonyManager = (TelephonyManager) getContext() 151 .getSystemService(Context.TELEPHONY_SERVICE); 152 sIsMultiSimDevice = isMultiSim(sTelephonyManager); 153 if (!sIsMultiSimDevice) { 154 Log.d(TAG, "beforeAllTests: Device is not multi-SIM, skipping all tests."); 155 return; 156 } 157 // We can not throw exceptions here - instead capture and throw in @Before 158 sIsMockModemAllowed = isMockModemAllowed(); 159 if (!sIsMockModemAllowed) { 160 Log.w(TAG, "beforeAllTests: Mock modem is not allowed - skipping"); 161 return; 162 } 163 sUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 164 165 // We can not actually throw anything from @BeforeClass, because it can cause undefined 166 // behavior - instead, we should catch it here and rethrow in @Before and fail the 167 // associated @Tests. 168 try { 169 sMockModemManager = new MockModemManager(); 170 assertNotNull(sMockModemManager); 171 assertTrue(sMockModemManager.connectMockModemService()); 172 sMockModemManager.insertSimCard(TEST_SLOT_0, MOCK_SIM_PROFILE_ID_TWN_CHT); 173 waitForSimStateReadyOrTimeout(TEST_SLOT_0); 174 sMockModemManager.insertSimCard(TEST_SLOT_1, MOCK_SIM_PROFILE_ID_TWN_FET); 175 waitForSimStateReadyOrTimeout(TEST_SLOT_1); 176 sTestSubSlot0 = waitForActiveSubIdOrTimeout(TEST_SLOT_0); 177 sTestSubSlot1 = waitForActiveSubIdOrTimeout(TEST_SLOT_1); 178 179 // Cache the list of call capable phone accounts after both SIMs have been added: 180 sTelecomManager = (TelecomManager) getContext() 181 .getSystemService(Context.TELECOM_SERVICE); 182 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelecomManager, tm -> { 183 updateCallCapablePhAcctsAfterSubAdded(sTestSubSlot0, tm); 184 updateCallCapablePhAcctsAfterSubAdded(sTestSubSlot1, tm); 185 sCallCapablePhoneAccounts.removeIf(h -> !h.getComponentName().getShortClassName() 186 .equals("com.android.services.telephony.TelephonyConnectionService")); 187 188 }); 189 sSimultaneousCallingListener = registerNewSimultaneousCallingListener(); 190 191 if (!ImsUtils.shouldTestImsService()) { 192 Log.d(TAG, "beforeAllTests: IMS feature not supported, skipping IMS setup."); 193 return; 194 } 195 sServiceConnectorSlot0 = new ImsServiceConnector( 196 InstrumentationRegistry.getInstrumentation()); 197 sServiceConnectorSlot1 = new ImsServiceConnector( 198 InstrumentationRegistry.getInstrumentation()); 199 // Remove all live ImsServices until after these tests are done 200 sServiceConnectorSlot0.clearAllActiveImsServices(TEST_SLOT_0); 201 sServiceConnectorSlot1.clearAllActiveImsServices(TEST_SLOT_1); 202 } catch (Throwable th) { 203 sCapturedSetupThrowable = th; 204 } 205 } 206 207 // NOTE: AfterClass can NOT throw Exceptions. 208 @AfterClass afterAllTests()209 public static void afterAllTests() { 210 if (!ImsUtils.shouldTestTelephony() || !sIsMultiSimDevice || !sFeatureEnabled 211 || !sIsMockModemAllowed) { 212 Log.d(TAG, "afterAllTests: Skipping - previous assumption failures"); 213 return; 214 } 215 Log.d(TAG, "afterAllTests"); 216 217 // Restore all ImsService configurations that existed before the test: 218 try { 219 if (sServiceConnectorSlot0 != null) { 220 sServiceConnectorSlot0.disconnectServices(); 221 } 222 if (sServiceConnectorSlot1 != null) { 223 sServiceConnectorSlot1.disconnectServices(); 224 } 225 } catch (Exception e) { 226 Log.w(TAG, "afterAllTests, IMS couldn't be torn down: " + e); 227 } 228 sServiceConnectorSlot0 = null; 229 sServiceConnectorSlot1 = null; 230 231 if (sSimultaneousCallingListener != null) { 232 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager, 233 (tm) -> tm.unregisterTelephonyCallback(sSimultaneousCallingListener)); 234 } 235 236 sCallCapablePhoneAccounts = null; 237 238 // Rebind all interfaces which is binding to MockModemService to default: 239 if (sMockModemManager == null) { 240 Log.w(TAG, "afterAllTests: MockModemManager is null!"); 241 return; 242 } 243 try { 244 // Remove the SIMs: 245 sMockModemManager.removeSimCard(TEST_SLOT_0); 246 sMockModemManager.removeSimCard(TEST_SLOT_1); 247 } catch (Exception e) { 248 Log.w(TAG, "afterAllTests, MockModem couldn't remove SIMs: " + e); 249 } 250 try { 251 // Reset the modified error response of RIL_REQUEST_RADIO_POWER to the original behavior 252 // and -1 means to disable the modified mechanism in MockModem: 253 sMockModemManager.forceErrorResponse(0, RIL_REQUEST_RADIO_POWER, -1); 254 if (!sMockModemManager.disconnectMockModemService()) { 255 Log.w(TAG, "afterAllTests: disconnectMockModemService did not return" 256 + " successfully!"); 257 } 258 } catch (Exception e) { 259 Log.w(TAG, "afterAllTests, MockModem couldn't be torn down: " + e); 260 } 261 sMockModemManager = null; 262 } 263 264 @Before beforeTest()265 public void beforeTest() throws Throwable { 266 if (!ImsUtils.shouldTestImsService() || !sIsMultiSimDevice) { 267 return; 268 } 269 Log.d(TAG, "beforeTest"); 270 if (sCapturedSetupThrowable != null) { 271 // Throw the captured error from @BeforeClass, which will print the stack trace and 272 // fail this test. 273 throw sCapturedSetupThrowable; 274 } 275 if (!sIsMockModemAllowed) { 276 fail("!! Enable Mock Modem before running this test !! " 277 + "Developer options => Allow Mock Modem"); 278 } 279 if (sTelephonyManager.getSimState(TEST_SLOT_0) != TelephonyManager.SIM_STATE_READY 280 || sTelephonyManager.getSimState(TEST_SLOT_1) != TelephonyManager.SIM_STATE_READY 281 ) { 282 fail("This test requires that there are two SIMs in the device!"); 283 } 284 // Correctness check: ensure that the subscription hasn't changed between tests. 285 int subId_0 = SubscriptionManager.getSubscriptionId(TEST_SLOT_0); 286 if (subId_0 != sTestSubSlot0) { 287 fail("The found subId " + subId_0 + " does not match the test sub id " + sTestSubSlot0); 288 } 289 int subId_1 = SubscriptionManager.getSubscriptionId(TEST_SLOT_1); 290 if (subId_1 != sTestSubSlot1) { 291 fail("The found subId " + subId_1 + " does not match the test sub id " + sTestSubSlot1); 292 } 293 } 294 295 @After afterTest()296 public void afterTest() throws Exception { 297 if (!ImsUtils.shouldTestImsService() || !sIsMultiSimDevice) { 298 return; 299 } 300 Log.d(TAG, "afterTest"); 301 302 // Unbind the ImsService after the test completes. 303 if (sServiceConnectorSlot0 != null) { 304 sServiceConnectorSlot0.disconnectCarrierImsService(); 305 sServiceConnectorSlot0.disconnectDeviceImsService(); 306 } 307 if (sServiceConnectorSlot1 != null) { 308 sServiceConnectorSlot1.disconnectCarrierImsService(); 309 sServiceConnectorSlot1.disconnectDeviceImsService(); 310 } 311 } 312 313 /** 314 * Test the case where the modem reports that cellular simultaneous calling is supported and 315 * ensure that the framework marks the subIds as simultaneous calling supported. 316 */ 317 @Test testCellularDSDASupported_IMSNotRegistered()318 public void testCellularDSDASupported_IMSNotRegistered() throws Throwable { 319 Log.d(TAG, "testCellularDSDASupported_SimultaneousCallingEnabled"); 320 assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice); 321 assumeTrue("Skip test: FEATURE_TELEPHONY not setup", 322 ImsUtils.shouldTestTelephony()); 323 324 // Set the enabled logical slots to be returned from the modem: 325 setSimultaneousCallingEnabledLogicalSlots(new int[]{TEST_SLOT_0, TEST_SLOT_1}); 326 327 try { 328 verifyCellularSimultaneousCallingSupport(true, sSimultaneousCallingListener); 329 verifySimultaneousCallingRestrictions(true); 330 } finally { 331 // Reset an empty array as the enabled logical slots to be returned from the modem: 332 setSimultaneousCallingEnabledLogicalSlots(new int[]{}); 333 } 334 } 335 336 /** 337 * Test the case where the modem reports that cellular simultaneous calling is not supported and 338 * ensure that the framework marks the subIds as not simultaneous calling supported. 339 */ 340 @Test testCellularDSDANotSupported_IMSNotRegistered()341 public void testCellularDSDANotSupported_IMSNotRegistered() throws Throwable { 342 Log.d(TAG, "testCellularDSDASupported_SimultaneousCallingEnabled"); 343 assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice); 344 assumeTrue("Skip test: FEATURE_TELEPHONY not setup", 345 ImsUtils.shouldTestTelephony()); 346 347 // Set an empty array as the enabled logical slots to be returned from the modem: 348 setSimultaneousCallingEnabledLogicalSlots(new int[]{}); 349 350 verifyCellularSimultaneousCallingSupport(false, sSimultaneousCallingListener); 351 verifySimultaneousCallingRestrictions(false); 352 } 353 354 /** 355 * Test that when IMS is registered over WWAN & cellular simultaneous calling is supported that 356 * the framework marks simultaneous calling as enabled. 357 */ 358 @Test testCellularDSDASupported_ImsRegisteredWWAN()359 public void testCellularDSDASupported_ImsRegisteredWWAN() throws Exception { 360 Log.d(TAG, "testImsRegisteredWWANCellularDSDASupported_SimultaneousCallingEnabled"); 361 assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice); 362 assumeTrue("Skip test: ImsService and/or FEATURE_TELEPHONY are not setup", 363 ImsUtils.shouldTestImsService()); 364 365 // Set the enabled logical slots to be returned from the modem: 366 setSimultaneousCallingEnabledLogicalSlots(new int[]{TEST_SLOT_0, TEST_SLOT_1}); 367 368 Pair<RegistrationManager.RegistrationCallback, 369 LinkedBlockingQueue<ImsRegistrationAttributes>> result_0 = null; 370 Pair<RegistrationManager.RegistrationCallback, 371 LinkedBlockingQueue<ImsRegistrationAttributes>> result_1 = null; 372 373 try { 374 // Ensure IMS for Sub 0 starts unregistered: 375 result_0 = attachCarrierImsServiceAndSetUnregistered(); 376 377 // Ensure IMS for Sub 1 starts unregistered: 378 result_1 = attachDeviceImsServiceAndSetUnregistered(); 379 380 verifyCellularSimultaneousCallingSupport(true, 381 sSimultaneousCallingListener); 382 // Register IMS via WWAN for both subs and then verify that DSDA is enabled via 383 // cellular: 384 registerImsForBothSubsAndVerifyAttributes(IMS_REGI_TECH_LTE, IMS_REGI_TECH_LTE, 385 result_0.second, result_1.second); 386 verifySimultaneousCallingRestrictions(true); 387 } finally { 388 // Reset an empty array as the enabled logical slots to be returned from the modem: 389 setSimultaneousCallingEnabledLogicalSlots(new int[]{}); 390 // Unregister IMS callbacks if they were registered successfully: 391 if (result_0 != null) { 392 unregisterImsCallback(result_0.first, sTestSubSlot0); 393 } 394 if (result_1 != null) { 395 unregisterImsCallback(result_1.first, sTestSubSlot1); 396 } 397 } 398 } 399 400 /** 401 * Test that when IMS is registered over WWAN and cellular simultaneous calling is not enabled, 402 * the framework marks simultaneous calling as disabled. 403 */ 404 @Test testCellularDSDANotSupported_ImsRegisteredWWAN()405 public void testCellularDSDANotSupported_ImsRegisteredWWAN() throws Exception { 406 Log.d(TAG, "testImsRegisteredWWAN_SimultaneousCallingDisabled"); 407 assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice); 408 assumeTrue("Skip test: ImsService and/or FEATURE_TELEPHONY are not setup", 409 ImsUtils.shouldTestImsService()); 410 411 Pair<RegistrationManager.RegistrationCallback, 412 LinkedBlockingQueue<ImsRegistrationAttributes>> result_0 = null; 413 Pair<RegistrationManager.RegistrationCallback, 414 LinkedBlockingQueue<ImsRegistrationAttributes>> result_1 = null; 415 416 try { 417 // Ensure IMS for Sub 0 starts unregistered: 418 result_0 = attachCarrierImsServiceAndSetUnregistered(); 419 420 // Ensure IMS for Sub 1 starts unregistered: 421 result_1 = attachDeviceImsServiceAndSetUnregistered(); 422 423 verifyCellularSimultaneousCallingSupport(false, sSimultaneousCallingListener); 424 // Register IMS via WWAN for both subs and then verify that DSDA is disabled: 425 registerImsForBothSubsAndVerifyAttributes(IMS_REGI_TECH_LTE, IMS_REGI_TECH_LTE, 426 result_0.second, result_1.second); 427 verifySimultaneousCallingRestrictions(false); 428 } finally { 429 if (result_0 != null) { 430 unregisterImsCallback(result_0.first, sTestSubSlot0); 431 } 432 if (result_1 != null) { 433 unregisterImsCallback(result_1.first, sTestSubSlot1); 434 } 435 } 436 } 437 438 /** 439 * Test that when IMS is registered over WLAN, the framework marks simultaneous calling as 440 * enabled. 441 */ 442 @Test testImsRegisteredWLAN()443 public void testImsRegisteredWLAN() throws Exception { 444 Log.d(TAG, "testImsRegisteredWLAN"); 445 assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice); 446 assumeTrue("Skip test: ImsService and/or FEATURE_TELEPHONY are not setup", 447 ImsUtils.shouldTestImsService()); 448 449 Pair<RegistrationManager.RegistrationCallback, 450 LinkedBlockingQueue<ImsRegistrationAttributes>> result_0 = null; 451 Pair<RegistrationManager.RegistrationCallback, 452 LinkedBlockingQueue<ImsRegistrationAttributes>> result_1 = null; 453 454 try { 455 // Ensure IMS for Sub 0 starts unregistered: 456 result_0 = attachCarrierImsServiceAndSetUnregistered(); 457 458 // Ensure IMS for Sub 1 starts unregistered: 459 result_1 = attachDeviceImsServiceAndSetUnregistered(); 460 461 verifyCellularSimultaneousCallingSupport(false, 462 sSimultaneousCallingListener); 463 // Register IMS via WWAN for both subs: 464 registerImsForBothSubsAndVerifyAttributes(IMS_REGI_TECH_IWLAN, IMS_REGI_TECH_IWLAN, 465 result_0.second, result_1.second); 466 waitUntilPhAccountDsdaRestrictionsSetOrTimeout(); 467 468 // verify that DSDA is enabled via IMS even though it is disabled via cellular: 469 verifySimultaneousCallingRestrictions(true); 470 } finally { 471 if (result_0 != null) { 472 unregisterImsCallback(result_0.first, sTestSubSlot0); 473 } 474 if (result_1 != null) { 475 unregisterImsCallback(result_1.first, sTestSubSlot1); 476 } 477 } 478 } 479 waitForActiveSubIdOrTimeout(int phoneId)480 private static int waitForActiveSubIdOrTimeout(int phoneId) throws Exception { 481 assertTrue("Timed out waiting for valid active subId. Current subId=[" 482 + getActiveSubId(phoneId) + "] for slot=[" + phoneId + "].", 483 ImsUtils.retryUntilTrue(() -> getActiveSubId(phoneId) >= 0, TEST_TIMEOUT_MS, 50)); 484 return getActiveSubId(phoneId); 485 } 486 waitForSimStateReadyOrTimeout(int phoneId)487 private static void waitForSimStateReadyOrTimeout(int phoneId) throws Exception { 488 assertTrue("Timed out waiting for SIM_STATE_READY. Current sim state=[" 489 + sTelephonyManager.getSimState(phoneId) + "] for slot=[" + phoneId + "].", 490 ImsUtils.retryUntilTrue(() -> (sTelephonyManager.getSimState(phoneId) 491 == TelephonyManager.SIM_STATE_READY), TEST_TIMEOUT_MS, 50)); 492 } 493 updateCallCapablePhAcctsAfterSubAdded(int subId, TelecomManager tm)494 private static void updateCallCapablePhAcctsAfterSubAdded(int subId, TelecomManager tm) { 495 try { 496 assertTrue("Timed out waiting for subId=[" + subId + "] to be added to " 497 + "sCallCapablePhoneAccounts.", ImsUtils.retryUntilTrue(() -> 498 updateCallCapablePhAcctsAndCheckForSubId(subId, tm), TEST_TIMEOUT_MS, 50)); 499 } catch (Exception e) { 500 throw new RuntimeException(e); 501 } 502 } 503 updateCallCapablePhAcctsAndCheckForSubId(int subId, TelecomManager tm)504 private static boolean updateCallCapablePhAcctsAndCheckForSubId(int subId, TelecomManager tm) { 505 sCallCapablePhoneAccounts = tm.getCallCapablePhoneAccounts(); 506 for (PhoneAccountHandle accountHandle : sCallCapablePhoneAccounts) { 507 if (accountHandle.getId().equals(String.valueOf(subId))) { 508 return true; 509 } 510 } 511 return false; 512 } 513 514 private Pair<RegistrationManager.RegistrationCallback, 515 LinkedBlockingQueue<ImsRegistrationAttributes>> attachCarrierImsServiceAndSetUnregistered()516 attachCarrierImsServiceAndSetUnregistered() throws Exception { 517 // Setup IMS Service: 518 triggerFrameworkConnectToCarrierImsService(sServiceConnectorSlot0, TEST_SLOT_0); 519 // Move IMS state to deregistered: 520 sServiceConnectorSlot0.getCarrierService().getImsRegistration().onDeregistered( 521 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, 522 ImsReasonInfo.CODE_UNSPECIFIED, "")); 523 // Register IMS callbacks 524 LinkedBlockingQueue<ImsRegistrationAttributes> mRegQueue = 525 new LinkedBlockingQueue<>(); 526 LinkedBlockingQueue<ImsReasonInfo> mDeregQueue = 527 new LinkedBlockingQueue<>(); 528 RegistrationManager.RegistrationCallback callback = 529 createImsRegistrationCallback(mRegQueue, mDeregQueue); 530 registerImsCallbackAndWaitForImsUnregister(sTestSubSlot0, callback, mDeregQueue); 531 return new Pair<>(callback, mRegQueue); 532 } 533 534 private Pair<RegistrationManager.RegistrationCallback, 535 LinkedBlockingQueue<ImsRegistrationAttributes>> attachDeviceImsServiceAndSetUnregistered()536 attachDeviceImsServiceAndSetUnregistered() throws Exception { 537 triggerFrameworkConnectToDeviceImsService(sServiceConnectorSlot1, TEST_SLOT_1); 538 sServiceConnectorSlot1.getExternalService().onDeregistered( 539 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, 540 ImsReasonInfo.CODE_UNSPECIFIED, "")); 541 // Wait for IMS to be setup and unregistered for sServiceConnector_1: 542 assertTrue("ImsService state is ready, but STATE_READY is not reported.", 543 ImsUtils.retryUntilTrue(() -> (getFeatureState(sTestSubSlot1) 544 == ImsFeature.STATE_READY), TEST_TIMEOUT_MS, 50)); 545 LinkedBlockingQueue<ImsRegistrationAttributes> mRegQueue = 546 new LinkedBlockingQueue<>(); 547 LinkedBlockingQueue<ImsReasonInfo> mDeregQueue = 548 new LinkedBlockingQueue<>(); 549 RegistrationManager.RegistrationCallback callback = 550 createImsRegistrationCallback(mRegQueue, mDeregQueue); 551 registerImsCallbackAndWaitForImsUnregister(sTestSubSlot1, callback, mDeregQueue); 552 return new Pair<>(callback, mRegQueue); 553 } 554 getFeatureState(int testSub)555 private static Integer getFeatureState(int testSub) throws Exception { 556 ImsManager imsManager = getContext().getSystemService(ImsManager.class); 557 assertNotNull(imsManager); 558 ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(testSub); 559 LinkedBlockingQueue<Integer> state = new LinkedBlockingQueue<>(1); 560 ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mmTelManager, 561 (m) -> m.getFeatureState(Runnable::run, state::offer), ImsException.class); 562 return state.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 563 } 564 registerImsForBothSubsAndVerifyAttributes(int regTechSlot0, int regTechSlot1, LinkedBlockingQueue<ImsRegistrationAttributes> regQueueSlot0, LinkedBlockingQueue<ImsRegistrationAttributes> regQueueSlot1)565 private void registerImsForBothSubsAndVerifyAttributes(int regTechSlot0, int regTechSlot1, 566 LinkedBlockingQueue<ImsRegistrationAttributes> regQueueSlot0, 567 LinkedBlockingQueue<ImsRegistrationAttributes> regQueueSlot1) throws Exception { 568 569 int expectedTransportType_0 = getExpectedTransportType(regTechSlot0); 570 int expectedTransportType_1 = getExpectedTransportType(regTechSlot1); 571 572 // IMS Registered for Sub 0: 573 sServiceConnectorSlot0.getCarrierService().getImsRegistration().onRegistered(regTechSlot0); 574 waitForAttributesAndVerify(regTechSlot0, regQueueSlot0, expectedTransportType_0, 0); 575 576 // IMS Registered for Sub 1: 577 sServiceConnectorSlot1.getExternalService().onRegistered(regTechSlot1); 578 waitForAttributesAndVerify(regTechSlot1, regQueueSlot1, expectedTransportType_1, 0); 579 } 580 getExpectedTransportType(int imsRegTech)581 private int getExpectedTransportType(int imsRegTech) { 582 int expectedTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 583 switch (imsRegTech) { 584 case IMS_REGI_TECH_IWLAN -> 585 expectedTransportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN; 586 case IMS_REGI_TECH_LTE -> 587 expectedTransportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN; 588 } 589 return expectedTransportType; 590 } 591 registerImsCallbackAndWaitForImsUnregister(int subId, RegistrationManager.RegistrationCallback callback, LinkedBlockingQueue<ImsReasonInfo> deRegQueue)592 private void registerImsCallbackAndWaitForImsUnregister(int subId, 593 RegistrationManager.RegistrationCallback callback, 594 LinkedBlockingQueue<ImsReasonInfo> deRegQueue) throws Exception { 595 registerImsRegistrationCallback(subId, callback); 596 ImsReasonInfo deregResult = waitForResult(deRegQueue); 597 assertNotNull(deregResult); 598 assertEquals(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, deregResult.getCode()); 599 } 600 601 /** 602 * Due to race conditions between the subId getting set and the ImsService coming up, 603 * registering callbacks can sometimes spuriously cause ImsExceptions. 604 * Poll every second if this condition occurs for up to 5 seconds. 605 */ registerImsRegistrationCallback(int subId, RegistrationManager.RegistrationCallback callback)606 private void registerImsRegistrationCallback(int subId, 607 RegistrationManager.RegistrationCallback callback) throws Exception { 608 ImsManager imsManager = getContext().getSystemService(ImsManager.class); 609 assertNotNull(imsManager); 610 ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(subId); 611 try { 612 sUiAutomation.adoptShellPermissionIdentity(); 613 assertTrue("Failed to register for IMS registration", ImsUtils.retryUntilTrue(() -> { 614 boolean result; 615 try { 616 mmTelManager.registerImsRegistrationCallback(getContext().getMainExecutor(), 617 callback); 618 result = true; 619 } catch (ImsException e) { 620 result = false; 621 Log.w(TAG, "pollRegisterImsRegistrationCallback: failed to register:" + e); 622 } 623 return result; 624 }, TEST_TIMEOUT_MS, 5)); 625 } finally { 626 sUiAutomation.dropShellPermissionIdentity(); 627 } 628 } 629 createImsRegistrationCallback( LinkedBlockingQueue<ImsRegistrationAttributes> regQueue, LinkedBlockingQueue<ImsReasonInfo> deRegQueue)630 private RegistrationManager.RegistrationCallback createImsRegistrationCallback( 631 LinkedBlockingQueue<ImsRegistrationAttributes> regQueue, 632 LinkedBlockingQueue<ImsReasonInfo> deRegQueue) { 633 RegistrationManager.RegistrationCallback callback = 634 new RegistrationManager.RegistrationCallback() { 635 @Override 636 public void onRegistered(ImsRegistrationAttributes attributes) { 637 regQueue.offer(attributes); 638 } 639 640 @Override 641 public void onRegistering(ImsRegistrationAttributes attributes) { 642 regQueue.offer(attributes); 643 } 644 645 @Override 646 public void onUnregistered(ImsReasonInfo info) { 647 deRegQueue.offer(info); 648 } 649 }; 650 return callback; 651 } 652 registerNewSimultaneousCallingListener()653 private static SimultaneousCallingListener registerNewSimultaneousCallingListener() { 654 // Configure and register a new SimultaneousCallingListener: 655 SimultaneousCallingListener listener = new SimultaneousCallingListener(); 656 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager, 657 (tm) -> tm.registerTelephonyCallback(getContext().getMainExecutor(), listener), 658 "android.permission.READ_PRIVILEGED_PHONE_STATE"); 659 return listener; 660 } 661 waitUntilPhAccountDsdaRestrictionsSetOrTimeout()662 private void waitUntilPhAccountDsdaRestrictionsSetOrTimeout() throws Exception { 663 assertTrue("Phone accounts simultaneous calling restrictions were not updated.", 664 ImsUtils.retryUntilTrue(() -> isDsdaAccountRestrictionsSet(getDsdaPhoneAccounts()), 665 TEST_TIMEOUT_MS, 10)); 666 } 667 668 /** 669 * @return true when DSDA is enabled and the cached PSTN PhoneAccount simultaneous calling 670 * restrictions contain each other's accont handles, false if DSDA is not enabled. 671 */ isDsdaAccountRestrictionsSet(Pair<PhoneAccount, PhoneAccount> accts)672 private boolean isDsdaAccountRestrictionsSet(Pair<PhoneAccount, PhoneAccount> accts) { 673 return accts.first.hasSimultaneousCallingRestriction() 674 && accts.second.hasSimultaneousCallingRestriction() 675 && accts.first.getSimultaneousCallingRestriction() 676 .contains(accts.second.getAccountHandle()) 677 && accts.second.getSimultaneousCallingRestriction() 678 .contains(accts.first.getAccountHandle()); 679 } 680 getDsdaPhoneAccounts()681 private Pair<PhoneAccount, PhoneAccount> getDsdaPhoneAccounts() { 682 List<PhoneAccount> dsdaAccts = ShellIdentityUtils.invokeMethodWithShellPermissions( 683 sTelecomManager, tm -> sCallCapablePhoneAccounts.stream() 684 .filter(handle -> handle.getId().equals(String.valueOf(sTestSubSlot0)) 685 || handle.getId().equals(String.valueOf(sTestSubSlot1))) 686 .map(tm::getPhoneAccount) 687 .collect(Collectors.toList())); 688 assertEquals("Unexpected number of DSDS accts:" + dsdaAccts, 2, 689 dsdaAccts.size()); 690 return new Pair<>(dsdaAccts.get(0), dsdaAccts.get(1)); 691 } 692 verifySimultaneousCallingRestrictions(boolean simultaneousCallingEnabled)693 private void verifySimultaneousCallingRestrictions(boolean simultaneousCallingEnabled) { 694 Pair<PhoneAccount, PhoneAccount> accts = getDsdaPhoneAccounts(); 695 if (simultaneousCallingEnabled) { 696 // Check that the simultaneous calling restrictions were set for each phone account: 697 assertTrue(accts.first.hasSimultaneousCallingRestriction()); 698 assertTrue(accts.second.hasSimultaneousCallingRestriction()); 699 assertEquals(1, accts.first.getSimultaneousCallingRestriction().size()); 700 assertEquals(1, accts.second.getSimultaneousCallingRestriction().size()); 701 Assert.assertTrue(accts.first.getSimultaneousCallingRestriction().contains( 702 accts.second.getAccountHandle())); 703 Assert.assertTrue(accts.second.getSimultaneousCallingRestriction().contains( 704 accts.first.getAccountHandle())); 705 } else { 706 // Check that simultaneous calling is disabled for both phone accounts: 707 assertTrue(accts.first.hasSimultaneousCallingRestriction()); 708 assertTrue(accts.second.hasSimultaneousCallingRestriction()); 709 assertEquals(0, accts.first.getSimultaneousCallingRestriction().size()); 710 assertEquals(0, accts.second.getSimultaneousCallingRestriction().size()); 711 } 712 } 713 verifyCellularSimultaneousCallingSupport( boolean cellularSimultaneousCallingSupported, SimultaneousCallingListener listener)714 private void verifyCellularSimultaneousCallingSupport( 715 boolean cellularSimultaneousCallingSupported, 716 SimultaneousCallingListener listener) throws Exception { 717 if (cellularSimultaneousCallingSupported) { 718 // Check that the expected cellular supported slots have been reported by the modem: 719 Set<Integer> expectedSimultaneousCallingSubIds = new HashSet<>(); 720 expectedSimultaneousCallingSubIds.add(sTestSubSlot0); 721 expectedSimultaneousCallingSubIds.add(sTestSubSlot1); 722 assertTrue("Never received cellular simultaneous calling subId update", 723 ImsUtils.retryUntilTrue(() -> expectedSimultaneousCallingSubIds.equals( 724 listener.getSimultaneousCallingSubIds()), TEST_TIMEOUT_MS, 5)); 725 } else { 726 assertTrue("Unexpected simultaneous calling subIds reported ", 727 ImsUtils.retryUntilTrue(() -> listener.getSimultaneousCallingSubIds().isEmpty(), 728 TEST_TIMEOUT_MS, 5)); 729 // Check that the modem reported no sub IDs support cellular simultaneous calling: 730 assertEquals(0, listener.getSimultaneousCallingSubIds().size()); 731 } 732 } 733 isMockModemAllowed()734 private static boolean isMockModemAllowed() { 735 // Always allow for debug builds 736 if (DEBUG) return true; 737 boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false); 738 boolean isAllowedForBoot = 739 SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false); 740 // Check for developer settings for user build. 741 return isAllowed || isAllowedForBoot; 742 } 743 unregisterImsCallback(RegistrationManager.RegistrationCallback callback, int testSub)744 private void unregisterImsCallback(RegistrationManager.RegistrationCallback callback, 745 int testSub) { 746 try { 747 sUiAutomation.adoptShellPermissionIdentity(); 748 ImsManager imsManager = getContext().getSystemService(ImsManager.class); 749 assertNotNull(imsManager); 750 ImsMmTelManager mmTelManager = imsManager.getImsMmTelManager(testSub); 751 mmTelManager.unregisterImsRegistrationCallback(callback); 752 } finally { 753 sUiAutomation.dropShellPermissionIdentity(); 754 } 755 } 756 getActiveSubId(int phoneId)757 private static int getActiveSubId(int phoneId) { 758 int[] allSubs; 759 try { 760 sUiAutomation.adoptShellPermissionIdentity( 761 "android.permission.READ_PRIVILEGED_PHONE_STATE"); 762 allSubs = getContext().getSystemService(SubscriptionManager.class) 763 .getActiveSubscriptionIdList(); 764 } finally { 765 sUiAutomation.dropShellPermissionIdentity(); 766 } 767 assertNotNull("Couldn't resolve subIds", allSubs); 768 int subsLength = allSubs.length; 769 return (phoneId < subsLength) ? allSubs[phoneId] : -1; 770 } 771 setSimultaneousCallingEnabledLogicalSlots(int[] enabledLogicalSlots)772 private void setSimultaneousCallingEnabledLogicalSlots(int[] enabledLogicalSlots) 773 throws Exception { 774 sMockModemManager.setSimulCallingEnabledLogicalSlots(TEST_SLOT_0, enabledLogicalSlots); 775 } 776 getContext()777 private static Context getContext() { 778 return InstrumentationRegistry.getInstrumentation().getContext(); 779 } 780 isMultiSim(TelephonyManager tm)781 private static boolean isMultiSim(TelephonyManager tm) { 782 return tm != null && tm.getActiveModemCount() > 1; 783 } 784 waitForResult(LinkedBlockingQueue<T> queue)785 private <T> T waitForResult(LinkedBlockingQueue<T> queue) throws Exception { 786 return queue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 787 } 788 triggerFrameworkConnectToCarrierImsService(ImsServiceConnector serviceConnector, int slotId)789 private void triggerFrameworkConnectToCarrierImsService(ImsServiceConnector serviceConnector, 790 int slotId) throws Exception { 791 Log.i(TAG, "triggerFrameworkConnectToCarrierImsService: slotId = " + slotId); 792 793 // Add the simultaneous calling capability to the ImsService. 794 assertTrue(serviceConnector.connectCarrierImsServiceLocally()); 795 serviceConnector.getCarrierService().addCapabilities( 796 ImsService.CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING); 797 798 // Connect to the ImsService with the MmTel feature. 799 assertTrue(serviceConnector.triggerFrameworkConnectionToCarrierImsService( 800 new ImsFeatureConfiguration.Builder() 801 .addFeature(slotId, ImsFeature.FEATURE_MMTEL) 802 .build())); 803 // The MmTelFeature is created when the ImsService is bound. If it wasn't created, then the 804 // Framework did not call it. 805 assertTrue("Did not receive createMmTelFeature", serviceConnector.getCarrierService() 806 .waitForLatchCountdown(TestImsService.LATCH_CREATE_MMTEL)); 807 assertTrue("Did not receive MmTelFeature#onReady", serviceConnector.getCarrierService() 808 .waitForLatchCountdown(TestImsService.LATCH_MMTEL_READY)); 809 assertNotNull("ImsService created, but ImsService#createMmTelFeature was not called!", 810 serviceConnector.getCarrierService().getMmTelFeature()); 811 int serviceSlot = serviceConnector.getCarrierService().getMmTelFeature().getSlotIndex(); 812 assertEquals("The slot specified for the test (" + slotId + ") does not match the " 813 + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature", 814 slotId, serviceSlot); 815 } 816 triggerFrameworkConnectToDeviceImsService(ImsServiceConnector serviceConnector, int slotId)817 private void triggerFrameworkConnectToDeviceImsService(ImsServiceConnector serviceConnector, 818 int slotId) throws Exception { 819 Log.i(TAG, "triggerFrameworkConnectToDeviceImsService: slotId = " + slotId); 820 821 // Connect to Device the ImsService with the MmTel feature and simultaneous call cap. 822 assertTrue(serviceConnector.connectDeviceImsService( 823 ImsService.CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING, 824 new ImsFeatureConfiguration.Builder() 825 .addFeature(slotId, ImsFeature.FEATURE_MMTEL) 826 .build())); 827 //First MMTEL feature is created on device ImsService. 828 assertTrue(serviceConnector.getExternalService().waitForLatchCountdown( 829 TestImsService.LATCH_CREATE_MMTEL)); 830 assertTrue("Device ImsService created, but TestDeviceImsService#createMmTelFeature was " 831 + "not called!", serviceConnector.getExternalService().isMmTelFeatureCreated()); 832 } 833 waitForAttributesAndVerify(int tech, LinkedBlockingQueue<ImsRegistrationAttributes> attrQueue, int expectedTransport, int expectedAttrFlags)834 private void waitForAttributesAndVerify(int tech, LinkedBlockingQueue<ImsRegistrationAttributes> 835 attrQueue, int expectedTransport, int expectedAttrFlags) throws Exception { 836 ImsRegistrationAttributes attrResult = waitForResult(attrQueue); 837 assertNotNull(attrResult); 838 assertEquals(tech, attrResult.getRegistrationTechnology()); 839 assertEquals(expectedTransport, attrResult.getTransportType()); 840 assertEquals(expectedAttrFlags, attrResult.getAttributeFlags()); 841 } 842 } 843