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