1 /* 2 * Copyright (C) 2018 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.internal.telephony; 18 19 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED; 20 import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.os.AsyncResult; 25 import android.os.Build; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.PowerManager; 29 import android.os.RegistrantList; 30 import android.os.SystemProperties; 31 import android.provider.DeviceConfig; 32 import android.sysprop.TelephonyProperties; 33 import android.telephony.PhoneCapability; 34 import android.telephony.SubscriptionManager; 35 import android.telephony.TelephonyManager; 36 import android.text.TextUtils; 37 import android.util.Log; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.telephony.subscription.SubscriptionManagerService; 41 import com.android.telephony.Rlog; 42 43 import java.util.HashMap; 44 import java.util.Map; 45 import java.util.NoSuchElementException; 46 47 /** 48 * This class manages phone's configuration which defines the potential capability (static) of the 49 * phone and its current activated capability (current). 50 * It gets and monitors static and current phone capability from the modem; send broadcast 51 * if they change, and sends commands to modem to enable or disable phones. 52 */ 53 public class PhoneConfigurationManager { 54 public static final String DSDA = "dsda"; 55 public static final String DSDS = "dsds"; 56 public static final String TSTS = "tsts"; 57 public static final String SSSS = ""; 58 /** DeviceConfig key for whether Virtual DSDA is enabled. */ 59 private static final String KEY_ENABLE_VIRTUAL_DSDA = "enable_virtual_dsda"; 60 private static final String LOG_TAG = "PhoneCfgMgr"; 61 private static final int EVENT_SWITCH_DSDS_CONFIG_DONE = 100; 62 private static final int EVENT_GET_MODEM_STATUS = 101; 63 private static final int EVENT_GET_MODEM_STATUS_DONE = 102; 64 private static final int EVENT_GET_PHONE_CAPABILITY_DONE = 103; 65 private static final int EVENT_DEVICE_CONFIG_CHANGED = 104; 66 67 private static PhoneConfigurationManager sInstance = null; 68 private final Context mContext; 69 private PhoneCapability mStaticCapability; 70 private final RadioConfig mRadioConfig; 71 private final Handler mHandler; 72 // mPhones is obtained from PhoneFactory and can have phones corresponding to inactive modems as 73 // well. That is, the array size can be 2 even if num of active modems is 1. 74 private Phone[] mPhones; 75 private final Map<Integer, Boolean> mPhoneStatusMap; 76 private MockableInterface mMi = new MockableInterface(); 77 private TelephonyManager mTelephonyManager; 78 /** 79 * True if 'Virtual DSDA' i.e., in-call IMS connectivity on both subs with only single logical 80 * modem, is enabled. 81 */ 82 private boolean mVirtualDsdaEnabled; 83 private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList(); 84 private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem"; 85 private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem"; 86 private static final boolean DEBUG = !"user".equals(Build.TYPE); 87 /** 88 * Init method to instantiate the object 89 * Should only be called once. 90 */ init(Context context)91 public static PhoneConfigurationManager init(Context context) { 92 synchronized (PhoneConfigurationManager.class) { 93 if (sInstance == null) { 94 sInstance = new PhoneConfigurationManager(context); 95 } else { 96 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 97 } 98 return sInstance; 99 } 100 } 101 102 /** 103 * Constructor. 104 * @param context context needed to send broadcast. 105 */ PhoneConfigurationManager(Context context)106 private PhoneConfigurationManager(Context context) { 107 mContext = context; 108 // TODO: send commands to modem once interface is ready. 109 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 110 //initialize with default, it'll get updated when RADIO is ON/AVAILABLE 111 mStaticCapability = getDefaultCapability(); 112 mRadioConfig = RadioConfig.getInstance(); 113 mHandler = new ConfigManagerHandler(); 114 mPhoneStatusMap = new HashMap<>(); 115 mVirtualDsdaEnabled = DeviceConfig.getBoolean( 116 DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false); 117 DeviceConfig.addOnPropertiesChangedListener( 118 DeviceConfig.NAMESPACE_TELEPHONY, Runnable::run, 119 properties -> { 120 if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY, 121 properties.getNamespace())) { 122 mHandler.sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED); 123 } 124 }); 125 126 notifyCapabilityChanged(); 127 128 mPhones = PhoneFactory.getPhones(); 129 130 for (Phone phone : mPhones) { 131 registerForRadioState(phone); 132 } 133 } 134 registerForRadioState(Phone phone)135 private void registerForRadioState(Phone phone) { 136 phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone); 137 } 138 getDefaultCapability()139 private PhoneCapability getDefaultCapability() { 140 if (getPhoneCount() > 1) { 141 return PhoneCapability.DEFAULT_DSDS_CAPABILITY; 142 } else { 143 return PhoneCapability.DEFAULT_SSSS_CAPABILITY; 144 } 145 } 146 147 // If virtual DSDA is enabled for this UE, then updates maxActiveVoiceSubscriptions to 2. maybeUpdateMaxActiveVoiceSubscriptions( final PhoneCapability staticCapability)148 private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions( 149 final PhoneCapability staticCapability) { 150 if (staticCapability.getLogicalModemList().size() > 1 && mVirtualDsdaEnabled) { 151 return new PhoneCapability.Builder(staticCapability) 152 .setMaxActiveVoiceSubscriptions(2) 153 .build(); 154 } else { 155 return staticCapability; 156 } 157 } 158 159 /** 160 * Static method to get instance. 161 */ getInstance()162 public static PhoneConfigurationManager getInstance() { 163 if (sInstance == null) { 164 Log.wtf(LOG_TAG, "getInstance null"); 165 } 166 167 return sInstance; 168 } 169 170 /** 171 * Handler class to handle callbacks 172 */ 173 private final class ConfigManagerHandler extends Handler { 174 @Override handleMessage(Message msg)175 public void handleMessage(Message msg) { 176 AsyncResult ar; 177 Phone phone = null; 178 switch (msg.what) { 179 case Phone.EVENT_RADIO_AVAILABLE: 180 case Phone.EVENT_RADIO_ON: 181 log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON"); 182 ar = (AsyncResult) msg.obj; 183 if (ar.userObj != null && ar.userObj instanceof Phone) { 184 phone = (Phone) ar.userObj; 185 updatePhoneStatus(phone); 186 } else { 187 // phone is null 188 log("Unable to add phoneStatus to cache. " 189 + "No phone object provided for event " + msg.what); 190 } 191 getStaticPhoneCapability(); 192 break; 193 case EVENT_SWITCH_DSDS_CONFIG_DONE: 194 ar = (AsyncResult) msg.obj; 195 if (ar != null && ar.exception == null) { 196 int numOfLiveModems = msg.arg1; 197 onMultiSimConfigChanged(numOfLiveModems); 198 } else { 199 log(msg.what + " failure. Not switching multi-sim config." + ar.exception); 200 } 201 break; 202 case EVENT_GET_MODEM_STATUS_DONE: 203 ar = (AsyncResult) msg.obj; 204 if (ar != null && ar.exception == null) { 205 int phoneId = msg.arg1; 206 boolean enabled = (boolean) ar.result; 207 // update the cache each time getModemStatus is requested 208 addToPhoneStatusCache(phoneId, enabled); 209 } else { 210 log(msg.what + " failure. Not updating modem status." + ar.exception); 211 } 212 break; 213 case EVENT_GET_PHONE_CAPABILITY_DONE: 214 ar = (AsyncResult) msg.obj; 215 if (ar != null && ar.exception == null) { 216 mStaticCapability = (PhoneCapability) ar.result; 217 notifyCapabilityChanged(); 218 } else { 219 log(msg.what + " failure. Not getting phone capability." + ar.exception); 220 } 221 break; 222 case EVENT_DEVICE_CONFIG_CHANGED: 223 boolean isVirtualDsdaEnabled = DeviceConfig.getBoolean( 224 DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false); 225 if (isVirtualDsdaEnabled != mVirtualDsdaEnabled) { 226 log("EVENT_DEVICE_CONFIG_CHANGED: from " + mVirtualDsdaEnabled + " to " 227 + isVirtualDsdaEnabled); 228 mVirtualDsdaEnabled = isVirtualDsdaEnabled; 229 } 230 break; 231 default: 232 log("Unknown event: " + msg.what); 233 } 234 } 235 } 236 237 /** 238 * Enable or disable phone 239 * 240 * @param phone which phone to operate on 241 * @param enable true or false 242 * @param result the message to sent back when it's done. 243 */ enablePhone(Phone phone, boolean enable, Message result)244 public void enablePhone(Phone phone, boolean enable, Message result) { 245 if (phone == null) { 246 log("enablePhone failed phone is null"); 247 return; 248 } 249 phone.mCi.enableModem(enable, result); 250 } 251 252 /** 253 * Get phone status (enabled/disabled) 254 * first query cache, if the status is not in cache, 255 * add it to cache and return a default value true (non-blocking). 256 * 257 * @param phone which phone to operate on 258 */ getPhoneStatus(Phone phone)259 public boolean getPhoneStatus(Phone phone) { 260 if (phone == null) { 261 log("getPhoneStatus failed phone is null"); 262 return false; 263 } 264 265 int phoneId = phone.getPhoneId(); 266 267 //use cache if the status has already been updated/queried 268 try { 269 return getPhoneStatusFromCache(phoneId); 270 } catch (NoSuchElementException ex) { 271 // Return true if modem status cannot be retrieved. For most cases, modem status 272 // is on. And for older version modems, GET_MODEM_STATUS and disable modem are not 273 // supported. Modem is always on. 274 //TODO: this should be fixed in R to support a third status UNKNOWN b/131631629 275 return true; 276 } finally { 277 //in either case send an asynchronous request to retrieve the phone status 278 updatePhoneStatus(phone); 279 } 280 } 281 282 /** 283 * Get phone status (enabled/disabled) directly from modem, and use a result Message object 284 * Note: the caller of this method is reponsible to call this in a blocking fashion as well 285 * as read the results and handle the error case. 286 * (In order to be consistent, in error case, we should return default value of true; refer 287 * to #getPhoneStatus method) 288 * 289 * @param phone which phone to operate on 290 * @param result message that will be updated with result 291 */ getPhoneStatusFromModem(Phone phone, Message result)292 public void getPhoneStatusFromModem(Phone phone, Message result) { 293 if (phone == null) { 294 log("getPhoneStatus failed phone is null"); 295 } 296 phone.mCi.getModemStatus(result); 297 } 298 299 /** 300 * return modem status from cache, NoSuchElementException if phoneId not in cache 301 * @param phoneId 302 */ getPhoneStatusFromCache(int phoneId)303 public boolean getPhoneStatusFromCache(int phoneId) throws NoSuchElementException { 304 if (mPhoneStatusMap.containsKey(phoneId)) { 305 return mPhoneStatusMap.get(phoneId); 306 } else { 307 throw new NoSuchElementException("phoneId not found: " + phoneId); 308 } 309 } 310 311 /** 312 * method to call RIL getModemStatus 313 */ updatePhoneStatus(Phone phone)314 private void updatePhoneStatus(Phone phone) { 315 Message result = Message.obtain( 316 mHandler, EVENT_GET_MODEM_STATUS_DONE, phone.getPhoneId(), 0 /**dummy arg*/); 317 phone.mCi.getModemStatus(result); 318 } 319 320 /** 321 * Add status of the phone to the status HashMap 322 * @param phoneId 323 * @param status 324 */ addToPhoneStatusCache(int phoneId, boolean status)325 public void addToPhoneStatusCache(int phoneId, boolean status) { 326 mPhoneStatusMap.put(phoneId, status); 327 } 328 329 /** 330 * Returns how many phone objects the device supports. 331 */ getPhoneCount()332 public int getPhoneCount() { 333 return mTelephonyManager.getActiveModemCount(); 334 } 335 336 /** 337 * get static overall phone capabilities for all phones. 338 */ getStaticPhoneCapability()339 public synchronized PhoneCapability getStaticPhoneCapability() { 340 if (getDefaultCapability().equals(mStaticCapability)) { 341 log("getStaticPhoneCapability: sending the request for getting PhoneCapability"); 342 Message callback = Message.obtain( 343 mHandler, EVENT_GET_PHONE_CAPABILITY_DONE); 344 mRadioConfig.getPhoneCapability(callback); 345 } 346 mStaticCapability = maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability); 347 log("getStaticPhoneCapability: mStaticCapability " + mStaticCapability); 348 return mStaticCapability; 349 } 350 351 /** 352 * get configuration related status of each phone. 353 */ getCurrentPhoneCapability()354 public PhoneCapability getCurrentPhoneCapability() { 355 return getStaticPhoneCapability(); 356 } 357 getNumberOfModemsWithSimultaneousDataConnections()358 public int getNumberOfModemsWithSimultaneousDataConnections() { 359 return mStaticCapability.getMaxActiveDataSubscriptions(); 360 } 361 notifyCapabilityChanged()362 private void notifyCapabilityChanged() { 363 PhoneNotifier notifier = new DefaultPhoneNotifier(mContext); 364 365 notifier.notifyPhoneCapabilityChanged(mStaticCapability); 366 } 367 368 /** 369 * Switch configs to enable multi-sim or switch back to single-sim 370 * @param numOfSims number of active sims we want to switch to 371 */ switchMultiSimConfig(int numOfSims)372 public void switchMultiSimConfig(int numOfSims) { 373 log("switchMultiSimConfig: with numOfSims = " + numOfSims); 374 if (getStaticPhoneCapability().getLogicalModemList().size() < numOfSims) { 375 log("switchMultiSimConfig: Phone is not capable of enabling " 376 + numOfSims + " sims, exiting!"); 377 return; 378 } 379 if (getPhoneCount() != numOfSims) { 380 log("switchMultiSimConfig: sending the request for switching"); 381 Message callback = Message.obtain( 382 mHandler, EVENT_SWITCH_DSDS_CONFIG_DONE, numOfSims, 0 /**dummy arg*/); 383 mRadioConfig.setNumOfLiveModems(numOfSims, callback); 384 } else { 385 log("switchMultiSimConfig: No need to switch. getNumOfActiveSims is already " 386 + numOfSims); 387 } 388 } 389 390 /** 391 * Get whether reboot is required or not after making changes to modem configurations. 392 * Return value defaults to true 393 */ isRebootRequiredForModemConfigChange()394 public boolean isRebootRequiredForModemConfigChange() { 395 return mMi.isRebootRequiredForModemConfigChange(); 396 } 397 onMultiSimConfigChanged(int numOfActiveModems)398 private void onMultiSimConfigChanged(int numOfActiveModems) { 399 int oldNumOfActiveModems = getPhoneCount(); 400 setMultiSimProperties(numOfActiveModems); 401 402 if (isRebootRequiredForModemConfigChange()) { 403 log("onMultiSimConfigChanged: Rebooting."); 404 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 405 pm.reboot("Multi-SIM config changed."); 406 } else { 407 log("onMultiSimConfigChanged: Rebooting is not required."); 408 mMi.notifyPhoneFactoryOnMultiSimConfigChanged(mContext, numOfActiveModems); 409 broadcastMultiSimConfigChange(numOfActiveModems); 410 boolean subInfoCleared = false; 411 // if numOfActiveModems is decreasing, deregister old RILs 412 // eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the 413 // second phone. This loop does nothing if numOfActiveModems is increasing. 414 for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) { 415 SubscriptionManagerService.getInstance().markSubscriptionsInactive(phoneId); 416 subInfoCleared = true; 417 mPhones[phoneId].mCi.onSlotActiveStatusChange( 418 SubscriptionManager.isValidPhoneId(phoneId)); 419 } 420 if (subInfoCleared) { 421 // This triggers update of default subs. This should be done asap after 422 // setMultiSimProperties() to avoid (minimize) duration for which default sub can be 423 // invalid and can map to a non-existent phone. 424 // If forexample someone calls a TelephonyManager API on default sub after 425 // setMultiSimProperties() and before onSubscriptionsChanged() below -- they can be 426 // using an invalid sub, which can map to a non-existent phone and can cause an 427 // exception (see b/163582235). 428 MultiSimSettingController.getInstance().onPhoneRemoved(); 429 } 430 // old phone objects are not needed now; mPhones can be updated 431 mPhones = PhoneFactory.getPhones(); 432 // if numOfActiveModems is increasing, register new RILs 433 // eg if we are going from 1 phone to 2 phones, we need to register RIL for the second 434 // phone. This loop does nothing if numOfActiveModems is decreasing. 435 for (int phoneId = oldNumOfActiveModems; phoneId < numOfActiveModems; phoneId++) { 436 Phone phone = mPhones[phoneId]; 437 registerForRadioState(phone); 438 phone.mCi.onSlotActiveStatusChange(SubscriptionManager.isValidPhoneId(phoneId)); 439 } 440 441 // When the user enables DSDS mode, the default VOICE and SMS subId should be switched 442 // to "No Preference". Doing so will sync the network/sim settings and telephony. 443 // (see b/198123192) 444 if (numOfActiveModems > oldNumOfActiveModems && numOfActiveModems == 2) { 445 Log.i(LOG_TAG, " onMultiSimConfigChanged: DSDS mode enabled; " 446 + "setting VOICE & SMS subId to -1 (No Preference)"); 447 448 //Set the default VOICE subId to -1 ("No Preference") 449 SubscriptionManagerService.getInstance().setDefaultVoiceSubId( 450 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 451 452 //TODO:: Set the default SMS sub to "No Preference". Tracking this bug (b/227386042) 453 } else { 454 Log.i(LOG_TAG, 455 "onMultiSimConfigChanged: DSDS mode NOT detected. NOT setting the " 456 + "default VOICE and SMS subId to -1 (No Preference)"); 457 } 458 } 459 } 460 461 /** 462 * Helper method to set system properties for setting multi sim configs, 463 * as well as doing the phone reboot 464 * NOTE: In order to support more than 3 sims, we need to change this method. 465 * @param numOfActiveModems number of active sims 466 */ setMultiSimProperties(int numOfActiveModems)467 private void setMultiSimProperties(int numOfActiveModems) { 468 mMi.setMultiSimProperties(numOfActiveModems); 469 } 470 471 @VisibleForTesting notifyMultiSimConfigChange(int numOfActiveModems)472 public static void notifyMultiSimConfigChange(int numOfActiveModems) { 473 sMultiSimConfigChangeRegistrants.notifyResult(numOfActiveModems); 474 } 475 476 /** 477 * Register for multi-SIM configuration change, for example if the devices switched from single 478 * SIM to dual-SIM mode. 479 * 480 * It doesn't trigger callback upon registration as multi-SIM config change is in-frequent. 481 */ registerForMultiSimConfigChange(Handler h, int what, Object obj)482 public static void registerForMultiSimConfigChange(Handler h, int what, Object obj) { 483 sMultiSimConfigChangeRegistrants.addUnique(h, what, obj); 484 } 485 486 /** 487 * Unregister for multi-SIM configuration change. 488 */ unregisterForMultiSimConfigChange(Handler h)489 public static void unregisterForMultiSimConfigChange(Handler h) { 490 sMultiSimConfigChangeRegistrants.remove(h); 491 } 492 493 /** 494 * Unregister for all multi-SIM configuration change events. 495 */ unregisterAllMultiSimConfigChangeRegistrants()496 public static void unregisterAllMultiSimConfigChangeRegistrants() { 497 sMultiSimConfigChangeRegistrants.removeAll(); 498 } 499 broadcastMultiSimConfigChange(int numOfActiveModems)500 private void broadcastMultiSimConfigChange(int numOfActiveModems) { 501 log("broadcastSimSlotNumChange numOfActiveModems" + numOfActiveModems); 502 // Notify internal registrants first. 503 notifyMultiSimConfigChange(numOfActiveModems); 504 505 Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED); 506 intent.putExtra(EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, numOfActiveModems); 507 mContext.sendBroadcast(intent); 508 } 509 /** 510 * This is invoked from shell commands during CTS testing only. 511 * @return true if the modem service is set successfully, false otherwise. 512 */ setModemService(String serviceName)513 public boolean setModemService(String serviceName) { 514 log("setModemService: " + serviceName); 515 boolean statusRadioConfig = false; 516 boolean statusRil = false; 517 final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false); 518 final boolean isAllowedForBoot = 519 SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false); 520 521 // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds 522 if (isAllowed || isAllowedForBoot || DEBUG) { 523 if (mRadioConfig != null) { 524 statusRadioConfig = mRadioConfig.setModemService(serviceName); 525 } 526 527 if (!statusRadioConfig) { 528 loge("setModemService: switching modem service for radioconfig fail"); 529 return false; 530 } 531 532 for (int i = 0; i < getPhoneCount(); i++) { 533 if (mPhones[i] != null) { 534 statusRil = mPhones[i].mCi.setModemService(serviceName); 535 } 536 537 if (!statusRil) { 538 loge("setModemService: switch modem for radio " + i + " fail"); 539 540 // Disconnect the switched service 541 mRadioConfig.setModemService(null); 542 for (int t = 0; t < i; t++) { 543 mPhones[t].mCi.setModemService(null); 544 } 545 return false; 546 } 547 } 548 } else { 549 loge("setModemService is not allowed"); 550 return false; 551 } 552 553 return true; 554 } 555 556 /** 557 * This is invoked from shell commands to query during CTS testing only. 558 * @return the service name of the connected service. 559 */ getModemService()560 public String getModemService() { 561 if (mPhones[0] == null) { 562 return ""; 563 } 564 565 return mPhones[0].mCi.getModemService(); 566 } 567 568 /** 569 * A wrapper class that wraps some methods so that they can be replaced or mocked in unit-tests. 570 * 571 * For example, setting or reading system property are static native methods that can't be 572 * directly mocked. We can mock it by replacing MockableInterface object with a mock instance 573 * in unittest. 574 */ 575 @VisibleForTesting 576 public static class MockableInterface { 577 /** 578 * Wrapper function to decide whether reboot is required for modem config change. 579 */ 580 @VisibleForTesting isRebootRequiredForModemConfigChange()581 public boolean isRebootRequiredForModemConfigChange() { 582 boolean rebootRequired = TelephonyProperties.reboot_on_modem_change().orElse(false); 583 log("isRebootRequiredForModemConfigChange: isRebootRequired = " + rebootRequired); 584 return rebootRequired; 585 } 586 587 /** 588 * Wrapper function to call setMultiSimProperties. 589 */ 590 @VisibleForTesting setMultiSimProperties(int numOfActiveModems)591 public void setMultiSimProperties(int numOfActiveModems) { 592 String multiSimConfig; 593 switch(numOfActiveModems) { 594 case 3: 595 multiSimConfig = TSTS; 596 break; 597 case 2: 598 multiSimConfig = DSDS; 599 break; 600 default: 601 multiSimConfig = SSSS; 602 } 603 604 log("setMultiSimProperties to " + multiSimConfig); 605 TelephonyProperties.multi_sim_config(multiSimConfig); 606 } 607 608 /** 609 * Wrapper function to call PhoneFactory.onMultiSimConfigChanged. 610 */ 611 @VisibleForTesting notifyPhoneFactoryOnMultiSimConfigChanged( Context context, int numOfActiveModems)612 public void notifyPhoneFactoryOnMultiSimConfigChanged( 613 Context context, int numOfActiveModems) { 614 PhoneFactory.onMultiSimConfigChanged(context, numOfActiveModems); 615 } 616 } 617 log(String s)618 private static void log(String s) { 619 Rlog.d(LOG_TAG, s); 620 } 621 loge(String s)622 private static void loge(String s) { 623 Rlog.e(LOG_TAG, s); 624 } 625 loge(String s, Exception ex)626 private static void loge(String s, Exception ex) { 627 Rlog.e(LOG_TAG, s, ex); 628 } 629 } 630