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