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