1 /* 2 * Copyright (C) 2022 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 com.android.services.telephony.domainselection; 18 19 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING; 20 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_UT; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.doNothing; 27 import static org.mockito.Mockito.doReturn; 28 29 import android.annotation.NonNull; 30 import android.content.Context; 31 import android.os.CancellationSignal; 32 import android.os.HandlerThread; 33 import android.os.Looper; 34 import android.os.PersistableBundle; 35 import android.telecom.TelecomManager; 36 import android.telephony.AccessNetworkConstants; 37 import android.telephony.CarrierConfigManager; 38 import android.telephony.DisconnectCause; 39 import android.telephony.DomainSelectionService; 40 import android.telephony.DomainSelector; 41 import android.telephony.EmergencyRegResult; 42 import android.telephony.NetworkRegistrationInfo; 43 import android.telephony.ServiceState; 44 import android.telephony.SubscriptionManager; 45 import android.telephony.TransportSelectorCallback; 46 import android.telephony.WwanSelectorCallback; 47 import android.telephony.ims.ImsManager; 48 import android.telephony.ims.ImsMmTelManager; 49 import android.telephony.ims.ImsReasonInfo; 50 import android.util.Log; 51 52 import androidx.test.runner.AndroidJUnit4; 53 54 import org.junit.After; 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.mockito.Mock; 59 import org.mockito.MockitoAnnotations; 60 61 import java.util.List; 62 import java.util.concurrent.Executors; 63 import java.util.function.Consumer; 64 65 /** 66 * Unit tests for DomainSelectorBase. 67 */ 68 @RunWith(AndroidJUnit4.class) 69 public class NormalCallDomainSelectorTest { 70 private static final String TAG = "NormalCallDomainSelectorTest"; 71 72 private static final int SLOT_ID = 0; 73 private static final int SUB_ID_1 = 1; 74 private static final int SUB_ID_2 = 2; 75 private static final String TEST_CALLID = "01234"; 76 77 private HandlerThread mHandlerThread; 78 private NormalCallDomainSelector mNormalCallDomainSelector; 79 80 @Mock private Context mMockContext; 81 @Mock private CarrierConfigManager mMockCarrierConfigMgr; 82 @Mock private ImsManager mMockImsManager; 83 @Mock private ImsMmTelManager mMockMmTelManager; 84 @Mock private ImsStateTracker mMockImsStateTracker; 85 @Mock private DomainSelectorBase.DestroyListener mMockDestroyListener; 86 @Mock private TelecomManager mMockTelecomManager; 87 88 @Before setUp()89 public void setUp() throws Exception { 90 MockitoAnnotations.initMocks(this); 91 92 doReturn(Context.TELEPHONY_IMS_SERVICE).when(mMockContext) 93 .getSystemServiceName(ImsManager.class); 94 doReturn(mMockImsManager).when(mMockContext) 95 .getSystemService(Context.TELEPHONY_IMS_SERVICE); 96 97 doReturn(Context.CARRIER_CONFIG_SERVICE).when(mMockContext) 98 .getSystemServiceName(CarrierConfigManager.class); 99 doReturn(mMockCarrierConfigMgr).when(mMockContext) 100 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 101 102 doReturn(Context.TELECOM_SERVICE).when(mMockContext) 103 .getSystemServiceName(TelecomManager.class); 104 doReturn(mMockTelecomManager).when(mMockContext) 105 .getSystemService(Context.TELECOM_SERVICE); 106 107 doReturn(mMockMmTelManager).when(mMockImsManager).getImsMmTelManager(SUB_ID_1); 108 doReturn(mMockMmTelManager).when(mMockImsManager).getImsMmTelManager(SUB_ID_2); 109 doNothing().when(mMockImsStateTracker).removeServiceStateListener(any()); 110 doNothing().when(mMockImsStateTracker).removeImsStateListener(any()); 111 doReturn(true).when(mMockImsStateTracker).isMmTelFeatureAvailable(); 112 113 // Set up the looper if it does not exist on the test thread. 114 if (Looper.myLooper() == null) { 115 Looper.prepare(); 116 } 117 118 mHandlerThread = new HandlerThread( 119 NormalCallDomainSelectorTest.class.getSimpleName()); 120 mHandlerThread.start(); 121 122 mNormalCallDomainSelector = new NormalCallDomainSelector(mMockContext, SLOT_ID, SUB_ID_1, 123 mHandlerThread.getLooper(), mMockImsStateTracker, mMockDestroyListener); 124 } 125 126 @After tearDown()127 public void tearDown() throws Exception { 128 if (mHandlerThread != null) { 129 mHandlerThread.quit(); 130 } 131 } 132 initialize(ServiceState serviceState, boolean isImsRegistered, boolean isImsRegisteredOverWlan, boolean isImsVoiceCapable, boolean isImsVideoCapable)133 private void initialize(ServiceState serviceState, boolean isImsRegistered, 134 boolean isImsRegisteredOverWlan, boolean isImsVoiceCapable, 135 boolean isImsVideoCapable) { 136 if (serviceState != null) mNormalCallDomainSelector.onServiceStateUpdated(serviceState); 137 doReturn(isImsRegistered).when(mMockImsStateTracker).isImsStateReady(); 138 doReturn(isImsRegistered).when(mMockImsStateTracker).isImsRegistered(); 139 doReturn(isImsVoiceCapable).when(mMockImsStateTracker).isImsVoiceCapable(); 140 doReturn(isImsVideoCapable).when(mMockImsStateTracker).isImsVideoCapable(); 141 doReturn(isImsRegisteredOverWlan).when(mMockImsStateTracker).isImsRegisteredOverWlan(); 142 mNormalCallDomainSelector.onImsRegistrationStateChanged(); 143 mNormalCallDomainSelector.onImsMmTelCapabilitiesChanged(); 144 } 145 146 @Test testInit()147 public void testInit() { 148 assertEquals(SLOT_ID, mNormalCallDomainSelector.getSlotId()); 149 assertEquals(SUB_ID_1, mNormalCallDomainSelector.getSubId()); 150 } 151 152 @Test testSelectDomainInputParams()153 public void testSelectDomainInputParams() { 154 MockTransportSelectorCallback transportSelectorCallback = 155 new MockTransportSelectorCallback(); 156 157 DomainSelectionService.SelectionAttributes attributes = 158 new DomainSelectionService.SelectionAttributes.Builder( 159 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 160 .setCallId(TEST_CALLID) 161 .setEmergency(false) 162 .setVideoCall(true) 163 .setExitedFromAirplaneMode(false) 164 .build(); 165 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 166 167 168 // Case 1: null inputs 169 try { 170 mNormalCallDomainSelector.selectDomain(null, null); 171 } catch (Exception e) { 172 fail("Invalid input params not handled." + e.getMessage()); 173 } 174 175 // Case 2: null TransportSelectorCallback 176 try { 177 mNormalCallDomainSelector.selectDomain(attributes, null); 178 } catch (Exception e) { 179 fail("Invalid params (SelectionAttributes) not handled." + e.getMessage()); 180 } 181 182 // Case 3: null SelectionAttributes 183 transportSelectorCallback.mSelectionTerminated = false; 184 try { 185 mNormalCallDomainSelector.selectDomain(null, transportSelectorCallback); 186 } catch (Exception e) { 187 fail("Invalid params (SelectionAttributes) not handled." + e.getMessage()); 188 } 189 190 assertTrue(transportSelectorCallback 191 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE)); 192 193 // Case 4: Invalid Subscription-id 194 attributes = new DomainSelectionService.SelectionAttributes.Builder( 195 SLOT_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID, SELECTOR_TYPE_CALLING) 196 .setCallId(TEST_CALLID) 197 .setEmergency(false) 198 .setVideoCall(true) 199 .setExitedFromAirplaneMode(false) 200 .build(); 201 try { 202 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 203 } catch (Exception e) { 204 fail("Invalid params (SelectionAttributes) not handled." + e.getMessage()); 205 } 206 207 assertTrue(transportSelectorCallback 208 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE)); 209 210 // Case 5: Invalid SELECTOR_TYPE 211 attributes = 212 new DomainSelectionService.SelectionAttributes.Builder( 213 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_UT) 214 .setCallId(TEST_CALLID) 215 .setEmergency(false) 216 .setVideoCall(true) 217 .setExitedFromAirplaneMode(false) 218 .build(); 219 try { 220 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 221 } catch (Exception e) { 222 fail("Invalid params (SelectionAttributes) not handled." + e.getMessage()); 223 } 224 225 assertTrue(transportSelectorCallback 226 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE)); 227 228 // Case 6: Emergency Call 229 attributes = new DomainSelectionService.SelectionAttributes.Builder( 230 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 231 .setCallId(TEST_CALLID) 232 .setEmergency(true) 233 .setVideoCall(true) 234 .setExitedFromAirplaneMode(false) 235 .build(); 236 try { 237 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 238 } catch (Exception e) { 239 fail("Invalid params (SelectionAttributes) not handled." + e.getMessage()); 240 } 241 242 assertTrue(transportSelectorCallback 243 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE)); 244 } 245 246 @Test testOutOfService()247 public void testOutOfService() { 248 MockTransportSelectorCallback transportSelectorCallback = 249 new MockTransportSelectorCallback(); 250 DomainSelectionService.SelectionAttributes attributes = 251 new DomainSelectionService.SelectionAttributes.Builder( 252 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 253 .setCallId(TEST_CALLID) 254 .setEmergency(false) 255 .setVideoCall(true) 256 .setExitedFromAirplaneMode(false) 257 .build(); 258 ServiceState serviceState = new ServiceState(); 259 serviceState.setStateOutOfService(); 260 initialize(serviceState, false, false, false, false); 261 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 262 assertTrue(transportSelectorCallback 263 .verifyOnSelectionTerminated(DisconnectCause.OUT_OF_SERVICE)); 264 } 265 266 @Test testDomainSelection()267 public void testDomainSelection() { 268 MockTransportSelectorCallback transportSelectorCallback = 269 new MockTransportSelectorCallback(); 270 DomainSelectionService.SelectionAttributes attributes = 271 new DomainSelectionService.SelectionAttributes.Builder( 272 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 273 .setCallId(TEST_CALLID) 274 .setEmergency(false) 275 .setVideoCall(false) 276 .setExitedFromAirplaneMode(false) 277 .build(); 278 279 // Case 1: WLAN 280 ServiceState serviceState = new ServiceState(); 281 serviceState.setState(ServiceState.STATE_IN_SERVICE); 282 initialize(serviceState, true, true, true, true); 283 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 284 assertTrue(transportSelectorCallback.verifyOnWlanSelected()); 285 286 // Case 2: 5G 287 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 288 initialize(serviceState, true, false, true, true); 289 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 290 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 291 assertTrue(transportSelectorCallback 292 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS)); 293 294 // Case 3: PS -> CS redial 295 ImsReasonInfo imsReasonInfo = new ImsReasonInfo(); 296 imsReasonInfo.mCode = ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED; 297 attributes = new DomainSelectionService.SelectionAttributes.Builder( 298 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 299 .setCallId(TEST_CALLID) 300 .setEmergency(false) 301 .setVideoCall(false) 302 .setExitedFromAirplaneMode(false) 303 .setPsDisconnectCause(imsReasonInfo) 304 .build(); 305 mNormalCallDomainSelector.reselectDomain(attributes); 306 assertTrue(transportSelectorCallback 307 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS)); 308 309 // Case 4: CS call 310 NetworkRegistrationInfo nwRegistrationInfo = new NetworkRegistrationInfo( 311 NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 312 NetworkRegistrationInfo.REGISTRATION_STATE_HOME, 313 AccessNetworkConstants.AccessNetworkType.UTRAN, 0, false, 314 null, null, null, false, 0, 0, 0); 315 serviceState.addNetworkRegistrationInfo(nwRegistrationInfo); 316 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 317 initialize(serviceState, false, false, false, false); 318 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 319 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 320 assertTrue(transportSelectorCallback 321 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS)); 322 323 //Case 5: Backup calling 324 serviceState.setStateOutOfService(); 325 initialize(serviceState, true, true, true, true); 326 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 327 assertTrue(transportSelectorCallback.verifyOnWlanSelected()); 328 } 329 330 @Test testWPSCallDomainSelection()331 public void testWPSCallDomainSelection() { 332 MockTransportSelectorCallback transportSelectorCallback = 333 new MockTransportSelectorCallback(); 334 DomainSelectionService.SelectionAttributes attributes = 335 new DomainSelectionService.SelectionAttributes.Builder( 336 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 337 .setNumber("*272121") 338 .setCallId(TEST_CALLID) 339 .setEmergency(false) 340 .setVideoCall(false) 341 .setExitedFromAirplaneMode(false) 342 .build(); 343 344 //Case 1: WPS not supported by IMS 345 PersistableBundle config = new PersistableBundle(); 346 config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, false); 347 doReturn(config).when(mMockCarrierConfigMgr).getConfigForSubId(SUB_ID_1); 348 ServiceState serviceState = new ServiceState(); 349 serviceState.setState(ServiceState.STATE_IN_SERVICE); 350 initialize(serviceState, true, true, true, true); 351 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 352 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 353 assertTrue(transportSelectorCallback 354 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS)); 355 356 //Case 2: WPS supported by IMS and WLAN registered 357 config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); 358 serviceState.setState(ServiceState.STATE_IN_SERVICE); 359 initialize(serviceState, true, true, true, true); 360 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 361 assertTrue(transportSelectorCallback.verifyOnWlanSelected()); 362 363 //Case 2: WPS supported by IMS and LTE registered 364 config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); 365 serviceState.setState(ServiceState.STATE_IN_SERVICE); 366 initialize(serviceState, true, false, true, true); 367 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 368 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 369 assertTrue(transportSelectorCallback 370 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS)); 371 } 372 373 @Test testTtyCallDomainSelection()374 public void testTtyCallDomainSelection() { 375 MockTransportSelectorCallback transportSelectorCallback = 376 new MockTransportSelectorCallback(); 377 DomainSelectionService.SelectionAttributes attributes = 378 new DomainSelectionService.SelectionAttributes.Builder( 379 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING) 380 .setCallId(TEST_CALLID) 381 .setEmergency(false) 382 .setVideoCall(false) 383 .setExitedFromAirplaneMode(false) 384 .build(); 385 386 //Case 1: TTY not supported by IMS and TTY enabled 387 doReturn(TelecomManager.TTY_MODE_FULL).when(mMockTelecomManager).getCurrentTtyMode(); 388 PersistableBundle config = new PersistableBundle(); 389 config.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false); 390 doReturn(config).when(mMockCarrierConfigMgr).getConfigForSubId(SUB_ID_1); 391 ServiceState serviceState = new ServiceState(); 392 serviceState.setState(ServiceState.STATE_IN_SERVICE); 393 initialize(serviceState, true, false, true, true); 394 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 395 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 396 assertTrue(transportSelectorCallback 397 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS)); 398 399 //Case 2: TTY supported by IMS and TTY enabled 400 config.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true); 401 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 402 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 403 assertTrue(transportSelectorCallback 404 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS)); 405 406 //Case 3: TTY supported by IMS and TTY disabled 407 doReturn(TelecomManager.TTY_MODE_OFF).when(mMockTelecomManager).getCurrentTtyMode(); 408 mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback); 409 assertTrue(transportSelectorCallback.verifyOnWwanSelected()); 410 assertTrue(transportSelectorCallback 411 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS)); 412 } 413 414 static class MockTransportSelectorCallback implements TransportSelectorCallback, 415 WwanSelectorCallback { 416 public boolean mCreated; 417 public boolean mWlanSelected; 418 public boolean mWwanSelected; 419 public boolean mSelectionTerminated; 420 public boolean mDomainSelected; 421 int mCauseCode; 422 int mSelectedDomain; 423 424 @Override onCreated(DomainSelector selector)425 public synchronized void onCreated(DomainSelector selector) { 426 Log.d(TAG, "onCreated"); 427 mCreated = true; 428 notifyAll(); 429 } 430 verifyOnCreated()431 public boolean verifyOnCreated() { 432 mCreated = false; 433 Log.d(TAG, "verifyOnCreated"); 434 waitForCallback(mCreated); 435 return mCreated; 436 } 437 438 @Override onWlanSelected(boolean useEmergencyPdn)439 public synchronized void onWlanSelected(boolean useEmergencyPdn) { 440 Log.d(TAG, "onWlanSelected"); 441 mWlanSelected = true; 442 notifyAll(); 443 } 444 verifyOnWlanSelected()445 public boolean verifyOnWlanSelected() { 446 Log.d(TAG, "verifyOnWlanSelected"); 447 waitForCallback(mWlanSelected); 448 return mWlanSelected; 449 } 450 451 @Override onWwanSelected()452 public synchronized WwanSelectorCallback onWwanSelected() { 453 mWwanSelected = true; 454 notifyAll(); 455 return (WwanSelectorCallback) this; 456 } 457 458 @Override onWwanSelected(final Consumer<WwanSelectorCallback> consumer)459 public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) { 460 mWwanSelected = true; 461 Executors.newSingleThreadExecutor().execute(() -> { 462 consumer.accept(this); 463 }); 464 } 465 verifyOnWwanSelected()466 public boolean verifyOnWwanSelected() { 467 waitForCallback(mWwanSelected); 468 return mWwanSelected; 469 } 470 471 @Override onSelectionTerminated(int cause)472 public synchronized void onSelectionTerminated(int cause) { 473 Log.i(TAG, "onSelectionTerminated - called"); 474 mCauseCode = cause; 475 mSelectionTerminated = true; 476 notifyAll(); 477 } 478 verifyOnSelectionTerminated(int cause)479 public boolean verifyOnSelectionTerminated(int cause) { 480 Log.i(TAG, "verifyOnSelectionTerminated - called"); 481 waitForCallback(mSelectionTerminated); 482 return (mSelectionTerminated && cause == mCauseCode); 483 } 484 waitForCallback(boolean condition)485 private synchronized void waitForCallback(boolean condition) { 486 long now = System.currentTimeMillis(); 487 long deadline = now + 1000; 488 try { 489 while (!condition && now < deadline) { 490 wait(deadline - now); 491 now = System.currentTimeMillis(); 492 } 493 } catch (Exception e) { 494 Log.i(TAG, e.getMessage()); 495 } 496 } 497 498 @Override onRequestEmergencyNetworkScan(@onNull List<Integer> preferredNetworks, int scanType, @NonNull CancellationSignal signal, @NonNull Consumer<EmergencyRegResult> consumer)499 public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks, 500 int scanType, 501 @NonNull CancellationSignal signal, 502 @NonNull Consumer<EmergencyRegResult> consumer) { 503 Log.i(TAG, "onRequestEmergencyNetworkScan - called"); 504 505 } 506 onDomainSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)507 public synchronized void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, 508 boolean useEmergencyPdn) { 509 Log.i(TAG, "onDomainSelected - called"); 510 mSelectedDomain = domain; 511 mDomainSelected = true; 512 notifyAll(); 513 } 514 verifyOnDomainSelected(int domain)515 public boolean verifyOnDomainSelected(int domain) { 516 Log.i(TAG, "verifyOnDomainSelected - called"); 517 mDomainSelected = false; 518 waitForCallback(mDomainSelected); 519 return (domain == mSelectedDomain); 520 } 521 } 522 } 523