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.annotation.NonNull; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.os.AsyncResult; 26 import android.os.Build; 27 import android.os.Handler; 28 import android.os.Message; 29 import android.os.PowerManager; 30 import android.os.RegistrantList; 31 import android.os.SystemProperties; 32 import android.provider.DeviceConfig; 33 import android.sysprop.TelephonyProperties; 34 import android.telephony.PhoneCapability; 35 import android.telephony.SubscriptionManager; 36 import android.telephony.TelephonyManager; 37 import android.telephony.TelephonyRegistryManager; 38 import android.text.TextUtils; 39 import android.util.Log; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.telephony.flags.FeatureFlags; 43 import com.android.internal.telephony.subscription.SubscriptionManagerService; 44 import com.android.telephony.Rlog; 45 46 import java.util.HashMap; 47 import java.util.HashSet; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.NoSuchElementException; 51 import java.util.Optional; 52 import java.util.Set; 53 import java.util.concurrent.CopyOnWriteArraySet; 54 import java.util.function.Consumer; 55 import java.util.stream.Collectors; 56 57 /** 58 * This class manages phone's configuration which defines the potential capability (static) of the 59 * phone and its current activated capability (current). 60 * It gets and monitors static and current phone capability from the modem; send broadcast 61 * if they change, and sends commands to modem to enable or disable phones. 62 */ 63 public class PhoneConfigurationManager { 64 public static final String DSDA = "dsda"; 65 public static final String DSDS = "dsds"; 66 public static final String TSTS = "tsts"; 67 public static final String SSSS = ""; 68 /** DeviceConfig key for whether Virtual DSDA is enabled. */ 69 private static final String KEY_ENABLE_VIRTUAL_DSDA = "enable_virtual_dsda"; 70 private static final String LOG_TAG = "PhoneCfgMgr"; 71 private static final int EVENT_SWITCH_DSDS_CONFIG_DONE = 100; 72 private static final int EVENT_GET_MODEM_STATUS = 101; 73 private static final int EVENT_GET_MODEM_STATUS_DONE = 102; 74 private static final int EVENT_GET_PHONE_CAPABILITY_DONE = 103; 75 private static final int EVENT_DEVICE_CONFIG_CHANGED = 104; 76 private static final int EVENT_GET_SIMULTANEOUS_CALLING_SUPPORT_DONE = 105; 77 private static final int EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED = 106; 78 79 /** 80 * Listener interface for events related to the {@link PhoneConfigurationManager} which should 81 * be reported to the {@link SimultaneousCallingTracker}. 82 */ 83 public interface Listener { onPhoneCapabilityChanged()84 public void onPhoneCapabilityChanged(); onDeviceConfigChanged()85 public void onDeviceConfigChanged(); 86 } 87 88 /** 89 * Base listener implementation. 90 */ 91 public abstract static class ListenerBase implements Listener { 92 @Override onPhoneCapabilityChanged()93 public void onPhoneCapabilityChanged() {} 94 @Override onDeviceConfigChanged()95 public void onDeviceConfigChanged() {} 96 } 97 98 99 private static PhoneConfigurationManager sInstance = null; 100 private final Context mContext; 101 // Static capability retrieved from the modem - may be null in the case where no info has been 102 // retrieved yet. 103 private PhoneCapability mStaticCapability = null; 104 private final Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>(3); 105 private final Set<Integer> mSubIdsSupportingSimultaneousCellularCalls = new HashSet<>(3); 106 private final HashSet<Consumer<Set<Integer>>> mSimultaneousCellularCallingListeners = 107 new HashSet<>(1); 108 private final RadioConfig mRadioConfig; 109 private final Handler mHandler; 110 // mPhones is obtained from PhoneFactory and can have phones corresponding to inactive modems as 111 // well. That is, the array size can be 2 even if num of active modems is 1. 112 private Phone[] mPhones; 113 private final Map<Integer, Boolean> mPhoneStatusMap; 114 private MockableInterface mMi = new MockableInterface(); 115 private TelephonyManager mTelephonyManager; 116 117 /** Feature flags */ 118 @NonNull 119 private final FeatureFlags mFeatureFlags; 120 private final DefaultPhoneNotifier mNotifier; 121 public Set<Listener> mListeners = new CopyOnWriteArraySet<>(); 122 123 /** 124 * True if 'Virtual DSDA' i.e., in-call IMS connectivity on both subs with only single logical 125 * modem, is enabled. 126 */ 127 private boolean mVirtualDsdaEnabled; 128 private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList(); 129 private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem"; 130 private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem"; 131 private static final boolean DEBUG = !"user".equals(Build.TYPE); 132 /** 133 * Init method to instantiate the object 134 * Should only be called once. 135 */ init(Context context, @NonNull FeatureFlags featureFlags)136 public static PhoneConfigurationManager init(Context context, 137 @NonNull FeatureFlags featureFlags) { 138 synchronized (PhoneConfigurationManager.class) { 139 if (sInstance == null) { 140 sInstance = new PhoneConfigurationManager(context, featureFlags); 141 } else { 142 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 143 } 144 return sInstance; 145 } 146 } 147 148 /** 149 * Constructor. 150 * @param context context needed to send broadcast. 151 */ PhoneConfigurationManager(Context context, @NonNull FeatureFlags featureFlags)152 private PhoneConfigurationManager(Context context, @NonNull FeatureFlags featureFlags) { 153 mContext = context; 154 mFeatureFlags = featureFlags; 155 // TODO: send commands to modem once interface is ready. 156 mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 157 mRadioConfig = RadioConfig.getInstance(); 158 mHandler = new ConfigManagerHandler(); 159 mPhoneStatusMap = new HashMap<>(); 160 mVirtualDsdaEnabled = DeviceConfig.getBoolean( 161 DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false); 162 mNotifier = new DefaultPhoneNotifier(mContext, mFeatureFlags); 163 DeviceConfig.addOnPropertiesChangedListener( 164 DeviceConfig.NAMESPACE_TELEPHONY, Runnable::run, 165 properties -> { 166 if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY, 167 properties.getNamespace())) { 168 mHandler.sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED); 169 } 170 }); 171 172 notifyCapabilityChanged(); 173 174 mPhones = PhoneFactory.getPhones(); 175 176 for (Phone phone : mPhones) { 177 registerForRadioState(phone); 178 } 179 } 180 181 /** 182 * Assign a listener to be notified of state changes. 183 * 184 * @param listener A listener. 185 */ addListener(Listener listener)186 public void addListener(Listener listener) { 187 mListeners.add(listener); 188 } 189 190 /** 191 * Removes a listener. 192 * 193 * @param listener A listener. 194 */ removeListener(Listener listener)195 public final void removeListener(Listener listener) { 196 mListeners.remove(listener); 197 } 198 199 /** 200 * Updates the mapping between the slot IDs that support simultaneous calling and the 201 * associated sub IDs as well as notifies listeners. 202 */ updateSimultaneousSubIdsFromPhoneIdMappingAndNotify()203 private void updateSimultaneousSubIdsFromPhoneIdMappingAndNotify() { 204 if (!mFeatureFlags.simultaneousCallingIndications()) return; 205 Set<Integer> slotCandidates = mSlotsSupportingSimultaneousCellularCalls.stream() 206 .map(i -> mPhones[i].getSubId()) 207 .filter(i ->i > SubscriptionManager.INVALID_SUBSCRIPTION_ID) 208 .collect(Collectors.toSet()); 209 if (mSubIdsSupportingSimultaneousCellularCalls.equals(slotCandidates)) return; 210 log("updateSimultaneousSubIdsFromPhoneIdMapping update: " 211 + mSubIdsSupportingSimultaneousCellularCalls + " -> " + slotCandidates); 212 mSubIdsSupportingSimultaneousCellularCalls.clear(); 213 mSubIdsSupportingSimultaneousCellularCalls.addAll(slotCandidates); 214 mNotifier.notifySimultaneousCellularCallingSubscriptionsChanged( 215 mSubIdsSupportingSimultaneousCellularCalls); 216 } 217 registerForRadioState(Phone phone)218 private void registerForRadioState(Phone phone) { 219 phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone); 220 } 221 getDefaultCapability()222 private PhoneCapability getDefaultCapability() { 223 if (getPhoneCount() > 1) { 224 return PhoneCapability.DEFAULT_DSDS_CAPABILITY; 225 } else { 226 return PhoneCapability.DEFAULT_SSSS_CAPABILITY; 227 } 228 } 229 230 /** 231 * If virtual DSDA is enabled for this UE, then increase maxActiveVoiceSubscriptions to 2. 232 */ maybeOverrideMaxActiveVoiceSubscriptions( final PhoneCapability staticCapability)233 private PhoneCapability maybeOverrideMaxActiveVoiceSubscriptions( 234 final PhoneCapability staticCapability) { 235 boolean isVDsdaEnabled = staticCapability.getLogicalModemList().size() > 1 236 && mVirtualDsdaEnabled; 237 boolean isBkwdCompatDsdaEnabled = mFeatureFlags.simultaneousCallingIndications() 238 && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA); 239 if (isVDsdaEnabled || isBkwdCompatDsdaEnabled) { 240 // Since we already initialized maxActiveVoiceSubscriptions to the count the 241 // modem is capable of, we are only able to increase that count via this method. We do 242 // not allow a decrease of maxActiveVoiceSubscriptions: 243 int updatedMaxActiveVoiceSubscriptions = 244 Math.max(staticCapability.getMaxActiveVoiceSubscriptions(), 2); 245 return new PhoneCapability.Builder(staticCapability) 246 .setMaxActiveVoiceSubscriptions(updatedMaxActiveVoiceSubscriptions) 247 .build(); 248 } else { 249 return staticCapability; 250 } 251 } 252 maybeEnableCellularDSDASupport()253 private void maybeEnableCellularDSDASupport() { 254 boolean bkwdsCompatDsda = mFeatureFlags.simultaneousCallingIndications() 255 && getPhoneCount() > 1 256 && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA); 257 boolean halSupportSimulCalling = mRadioConfig != null 258 && mRadioConfig.getRadioConfigProxy(null).getVersion().greaterOrEqual( 259 RIL.RADIO_HAL_VERSION_2_2) 260 && getPhoneCount() > 1 261 && getCellularStaticPhoneCapability().getMaxActiveVoiceSubscriptions() > 1; 262 // Register for simultaneous calling support changes in the modem if the HAL supports it 263 if (halSupportSimulCalling) { 264 updateSimultaneousCallingSupport(); 265 mRadioConfig.registerForSimultaneousCallingSupportStatusChanged(mHandler, 266 EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED, null); 267 } else if (bkwdsCompatDsda) { 268 // For older devices that only declare that they support DSDA via modem config, 269 // set DSDA as capable now statically. 270 log("DSDA modem config detected - setting DSDA enabled"); 271 for (Phone p : mPhones) { 272 mSlotsSupportingSimultaneousCellularCalls.add(p.getPhoneId()); 273 } 274 updateSimultaneousSubIdsFromPhoneIdMappingAndNotify(); 275 notifySimultaneousCellularCallingSlotsChanged(); 276 } 277 // Register for subId updates to notify listeners when simultaneous calling is configured 278 if (mFeatureFlags.simultaneousCallingIndications() 279 && (bkwdsCompatDsda || halSupportSimulCalling)) { 280 mContext.getSystemService(TelephonyRegistryManager.class) 281 .addOnSubscriptionsChangedListener( 282 new SubscriptionManager.OnSubscriptionsChangedListener() { 283 @Override 284 public void onSubscriptionsChanged() { 285 updateSimultaneousSubIdsFromPhoneIdMappingAndNotify(); 286 } 287 }, mHandler::post); 288 } 289 } 290 291 /** 292 * Static method to get instance. 293 */ getInstance()294 public static PhoneConfigurationManager getInstance() { 295 if (sInstance == null) { 296 Log.wtf(LOG_TAG, "getInstance null"); 297 } 298 299 return sInstance; 300 } 301 302 /** 303 * Handler class to handle callbacks 304 */ 305 private final class ConfigManagerHandler extends Handler { 306 @Override handleMessage(Message msg)307 public void handleMessage(Message msg) { 308 AsyncResult ar; 309 Phone phone = null; 310 switch (msg.what) { 311 case Phone.EVENT_RADIO_AVAILABLE: 312 case Phone.EVENT_RADIO_ON: 313 log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON"); 314 ar = (AsyncResult) msg.obj; 315 if (ar.userObj != null && ar.userObj instanceof Phone) { 316 phone = (Phone) ar.userObj; 317 updatePhoneStatus(phone); 318 } else { 319 // phone is null 320 log("Unable to add phoneStatus to cache. " 321 + "No phone object provided for event " + msg.what); 322 } 323 updateRadioCapability(); 324 break; 325 case EVENT_SWITCH_DSDS_CONFIG_DONE: 326 ar = (AsyncResult) msg.obj; 327 if (ar != null && ar.exception == null) { 328 int numOfLiveModems = msg.arg1; 329 onMultiSimConfigChanged(numOfLiveModems); 330 } else { 331 log(msg.what + " failure. Not switching multi-sim config." + ar.exception); 332 } 333 break; 334 case EVENT_GET_MODEM_STATUS_DONE: 335 ar = (AsyncResult) msg.obj; 336 if (ar != null && ar.exception == null) { 337 int phoneId = msg.arg1; 338 boolean enabled = (boolean) ar.result; 339 // update the cache each time getModemStatus is requested 340 addToPhoneStatusCache(phoneId, enabled); 341 } else { 342 log(msg.what + " failure. Not updating modem status." + ar.exception); 343 } 344 break; 345 case EVENT_GET_PHONE_CAPABILITY_DONE: 346 ar = (AsyncResult) msg.obj; 347 if (ar != null && ar.exception == null) { 348 setStaticPhoneCapability((PhoneCapability) ar.result); 349 notifyCapabilityChanged(); 350 for (Listener l : mListeners) { 351 l.onPhoneCapabilityChanged(); 352 } 353 maybeEnableCellularDSDASupport(); 354 } else { 355 log(msg.what + " failure. Not getting phone capability." + ar.exception); 356 } 357 break; 358 case EVENT_DEVICE_CONFIG_CHANGED: 359 boolean isVirtualDsdaEnabled = DeviceConfig.getBoolean( 360 DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false); 361 if (isVirtualDsdaEnabled != mVirtualDsdaEnabled) { 362 log("EVENT_DEVICE_CONFIG_CHANGED: from " + mVirtualDsdaEnabled + " to " 363 + isVirtualDsdaEnabled); 364 mVirtualDsdaEnabled = isVirtualDsdaEnabled; 365 for (Listener l : mListeners) { 366 l.onDeviceConfigChanged(); 367 } 368 } 369 break; 370 case EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED: 371 case EVENT_GET_SIMULTANEOUS_CALLING_SUPPORT_DONE: 372 log("Received EVENT_SLOTS_SUPPORTING_SIMULTANEOUS_CALL_CHANGED/DONE"); 373 if (getPhoneCount() < 2) { 374 if (!mSlotsSupportingSimultaneousCellularCalls.isEmpty()) { 375 mSlotsSupportingSimultaneousCellularCalls.clear(); 376 } 377 break; 378 } 379 ar = (AsyncResult) msg.obj; 380 if (ar != null && ar.exception == null) { 381 List<Integer> returnedArrayList = (List<Integer>) ar.result; 382 if (!mSlotsSupportingSimultaneousCellularCalls.isEmpty()) { 383 mSlotsSupportingSimultaneousCellularCalls.clear(); 384 } 385 int maxValidPhoneSlot = getPhoneCount() - 1; 386 for (int i : returnedArrayList) { 387 if (i < 0 || i > maxValidPhoneSlot) { 388 loge("Invalid slot supporting DSDA =" + i + ". Disabling DSDA."); 389 mSlotsSupportingSimultaneousCellularCalls.clear(); 390 break; 391 } 392 mSlotsSupportingSimultaneousCellularCalls.add(i); 393 } 394 // Ensure the slots supporting cellular DSDA does not exceed the phone count 395 if (mSlotsSupportingSimultaneousCellularCalls.size() > getPhoneCount()) { 396 loge("Invalid size of DSDA slots. Disabling cellular DSDA."); 397 mSlotsSupportingSimultaneousCellularCalls.clear(); 398 } 399 } else { 400 log(msg.what + " failure. Not getting logical slots that support " 401 + "simultaneous calling." + ar.exception); 402 mSlotsSupportingSimultaneousCellularCalls.clear(); 403 } 404 if (mFeatureFlags.simultaneousCallingIndications()) { 405 updateSimultaneousSubIdsFromPhoneIdMappingAndNotify(); 406 notifySimultaneousCellularCallingSlotsChanged(); 407 } 408 break; 409 default: 410 log("Unknown event: " + msg.what); 411 } 412 } 413 } 414 415 /** 416 * Enable or disable phone 417 * 418 * @param phone which phone to operate on 419 * @param enable true or false 420 * @param result the message to sent back when it's done. 421 */ enablePhone(Phone phone, boolean enable, Message result)422 public void enablePhone(Phone phone, boolean enable, Message result) { 423 if (phone == null) { 424 log("enablePhone failed phone is null"); 425 return; 426 } 427 phone.mCi.enableModem(enable, result); 428 } 429 430 /** 431 * Get phone status (enabled/disabled) 432 * first query cache, if the status is not in cache, 433 * add it to cache and return a default value true (non-blocking). 434 * 435 * @param phone which phone to operate on 436 */ getPhoneStatus(Phone phone)437 public boolean getPhoneStatus(Phone phone) { 438 if (phone == null) { 439 log("getPhoneStatus failed phone is null"); 440 return false; 441 } 442 443 int phoneId = phone.getPhoneId(); 444 445 //use cache if the status has already been updated/queried 446 try { 447 return getPhoneStatusFromCache(phoneId); 448 } catch (NoSuchElementException ex) { 449 // Return true if modem status cannot be retrieved. For most cases, modem status 450 // is on. And for older version modems, GET_MODEM_STATUS and disable modem are not 451 // supported. Modem is always on. 452 //TODO: this should be fixed in R to support a third status UNKNOWN b/131631629 453 return true; 454 } finally { 455 //in either case send an asynchronous request to retrieve the phone status 456 updatePhoneStatus(phone); 457 } 458 } 459 460 /** 461 * Get phone status (enabled/disabled) directly from modem, and use a result Message object 462 * Note: the caller of this method is reponsible to call this in a blocking fashion as well 463 * as read the results and handle the error case. 464 * (In order to be consistent, in error case, we should return default value of true; refer 465 * to #getPhoneStatus method) 466 * 467 * @param phone which phone to operate on 468 * @param result message that will be updated with result 469 */ getPhoneStatusFromModem(Phone phone, Message result)470 public void getPhoneStatusFromModem(Phone phone, Message result) { 471 if (phone == null) { 472 log("getPhoneStatus failed phone is null"); 473 } 474 phone.mCi.getModemStatus(result); 475 } 476 477 /** 478 * return modem status from cache, NoSuchElementException if phoneId not in cache 479 * @param phoneId 480 */ getPhoneStatusFromCache(int phoneId)481 public boolean getPhoneStatusFromCache(int phoneId) throws NoSuchElementException { 482 if (mPhoneStatusMap.containsKey(phoneId)) { 483 return mPhoneStatusMap.get(phoneId); 484 } else { 485 throw new NoSuchElementException("phoneId not found: " + phoneId); 486 } 487 } 488 489 /** 490 * method to call RIL getModemStatus 491 */ updatePhoneStatus(Phone phone)492 private void updatePhoneStatus(Phone phone) { 493 Message result = Message.obtain( 494 mHandler, EVENT_GET_MODEM_STATUS_DONE, phone.getPhoneId(), 0 /**dummy arg*/); 495 phone.mCi.getModemStatus(result); 496 } 497 498 /** 499 * Add status of the phone to the status HashMap 500 * @param phoneId 501 * @param status 502 */ addToPhoneStatusCache(int phoneId, boolean status)503 public void addToPhoneStatusCache(int phoneId, boolean status) { 504 mPhoneStatusMap.put(phoneId, status); 505 } 506 507 /** 508 * Returns how many phone objects the device supports. 509 */ getPhoneCount()510 public int getPhoneCount() { 511 return mTelephonyManager.getActiveModemCount(); 512 } 513 514 /** 515 * @return The updated list of logical slots that support simultaneous cellular calling from the 516 * modem based on current network conditions. 517 */ getSlotsSupportingSimultaneousCellularCalls()518 public Set<Integer> getSlotsSupportingSimultaneousCellularCalls() { 519 return mSlotsSupportingSimultaneousCellularCalls; 520 } 521 522 /** 523 * Get the current the list of logical slots supporting simultaneous cellular calling from the 524 * modem based on current network conditions. 525 */ 526 @VisibleForTesting updateSimultaneousCallingSupport()527 public void updateSimultaneousCallingSupport() { 528 log("updateSimultaneousCallingSupport: sending the request for " 529 + "getting the list of logical slots supporting simultaneous cellular calling"); 530 Message callback = Message.obtain( 531 mHandler, EVENT_GET_SIMULTANEOUS_CALLING_SUPPORT_DONE); 532 mRadioConfig.updateSimultaneousCallingSupport(callback); 533 log("updateSimultaneousCallingSupport: " 534 + "mSlotsSupportingSimultaneousCellularCalls = " + 535 mSlotsSupportingSimultaneousCellularCalls); 536 } 537 538 /** 539 * @return static overall phone capabilities for all phones, including voice overrides. 540 */ getStaticPhoneCapability()541 public synchronized PhoneCapability getStaticPhoneCapability() { 542 boolean isDefault = mStaticCapability == null; 543 PhoneCapability caps = isDefault ? getDefaultCapability() : mStaticCapability; 544 caps = maybeOverrideMaxActiveVoiceSubscriptions(caps); 545 log("getStaticPhoneCapability: isDefault=" + isDefault + ", caps=" + caps); 546 return caps; 547 } 548 549 /** 550 * @return untouched capabilities returned from the modem 551 */ getCellularStaticPhoneCapability()552 private synchronized PhoneCapability getCellularStaticPhoneCapability() { 553 log("getCellularStaticPhoneCapability: mStaticCapability " + mStaticCapability); 554 return mStaticCapability; 555 } 556 557 /** 558 * Caches the static PhoneCapability returned by the modem 559 */ setStaticPhoneCapability(PhoneCapability capability)560 public synchronized void setStaticPhoneCapability(PhoneCapability capability) { 561 log("setStaticPhoneCapability: mStaticCapability " + capability); 562 mStaticCapability = capability; 563 } 564 565 /** 566 * Query the modem to return its static PhoneCapability and cache it 567 */ 568 @VisibleForTesting updateRadioCapability()569 public void updateRadioCapability() { 570 log("updateRadioCapability: sending the request for getting PhoneCapability"); 571 Message callback = Message.obtain(mHandler, EVENT_GET_PHONE_CAPABILITY_DONE); 572 mRadioConfig.getPhoneCapability(callback); 573 } 574 575 /** 576 * get configuration related status of each phone. 577 */ getCurrentPhoneCapability()578 public PhoneCapability getCurrentPhoneCapability() { 579 return getStaticPhoneCapability(); 580 } 581 getNumberOfModemsWithSimultaneousDataConnections()582 public int getNumberOfModemsWithSimultaneousDataConnections() { 583 return getStaticPhoneCapability().getMaxActiveDataSubscriptions(); 584 } 585 getNumberOfModemsWithSimultaneousVoiceConnections()586 public int getNumberOfModemsWithSimultaneousVoiceConnections() { 587 return getStaticPhoneCapability().getMaxActiveVoiceSubscriptions(); 588 } 589 isVirtualDsdaEnabled()590 public boolean isVirtualDsdaEnabled() { 591 return mVirtualDsdaEnabled; 592 } 593 594 /** 595 * Register to listen to changes in the Phone slots that support simultaneous calling. 596 * @param consumer A consumer that will be used to consume the new slots supporting simultaneous 597 * cellular calling when it changes. 598 */ registerForSimultaneousCellularCallingSlotsChanged( Consumer<Set<Integer>> consumer)599 public void registerForSimultaneousCellularCallingSlotsChanged( 600 Consumer<Set<Integer>> consumer) { 601 mSimultaneousCellularCallingListeners.add(consumer); 602 } 603 notifySimultaneousCellularCallingSlotsChanged()604 private void notifySimultaneousCellularCallingSlotsChanged() { 605 log("notifying listeners of changes to simultaneous cellular calling - new state:" 606 + mSlotsSupportingSimultaneousCellularCalls); 607 for (Consumer<Set<Integer>> consumer : mSimultaneousCellularCallingListeners) { 608 try { 609 consumer.accept(new HashSet<>(mSlotsSupportingSimultaneousCellularCalls)); 610 } catch (Exception e) { 611 log("Unexpected Exception encountered when notifying listener: " + e); 612 } 613 } 614 } 615 notifyCapabilityChanged()616 private void notifyCapabilityChanged() { 617 mNotifier.notifyPhoneCapabilityChanged(getStaticPhoneCapability()); 618 } 619 620 /** 621 * Switch configs to enable multi-sim or switch back to single-sim 622 * @param numOfSims number of active sims we want to switch to 623 */ switchMultiSimConfig(int numOfSims)624 public void switchMultiSimConfig(int numOfSims) { 625 log("switchMultiSimConfig: with numOfSims = " + numOfSims); 626 if (getStaticPhoneCapability().getLogicalModemList().size() < numOfSims) { 627 log("switchMultiSimConfig: Phone is not capable of enabling " 628 + numOfSims + " sims, exiting!"); 629 return; 630 } 631 if (getPhoneCount() != numOfSims) { 632 log("switchMultiSimConfig: sending the request for switching"); 633 Message callback = Message.obtain( 634 mHandler, EVENT_SWITCH_DSDS_CONFIG_DONE, numOfSims, 0 /**dummy arg*/); 635 mRadioConfig.setNumOfLiveModems(numOfSims, callback); 636 } else { 637 log("switchMultiSimConfig: No need to switch. getNumOfActiveSims is already " 638 + numOfSims); 639 } 640 } 641 642 /** 643 * Get whether reboot is required or not after making changes to modem configurations. 644 * Return value defaults to true 645 */ isRebootRequiredForModemConfigChange()646 public boolean isRebootRequiredForModemConfigChange() { 647 return mMi.isRebootRequiredForModemConfigChange(); 648 } 649 onMultiSimConfigChanged(int numOfActiveModems)650 private void onMultiSimConfigChanged(int numOfActiveModems) { 651 int oldNumOfActiveModems = getPhoneCount(); 652 setMultiSimProperties(numOfActiveModems); 653 654 if (isRebootRequiredForModemConfigChange()) { 655 log("onMultiSimConfigChanged: Rebooting."); 656 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 657 pm.reboot("Multi-SIM config changed."); 658 } else { 659 log("onMultiSimConfigChanged: Rebooting is not required."); 660 mMi.notifyPhoneFactoryOnMultiSimConfigChanged(mContext, numOfActiveModems); 661 broadcastMultiSimConfigChange(numOfActiveModems); 662 boolean subInfoCleared = false; 663 // if numOfActiveModems is decreasing, deregister old RILs 664 // eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the 665 // second phone. This loop does nothing if numOfActiveModems is increasing. 666 for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) { 667 SubscriptionManagerService.getInstance().markSubscriptionsInactive(phoneId); 668 subInfoCleared = true; 669 mPhones[phoneId].mCi.onSlotActiveStatusChange( 670 SubscriptionManager.isValidPhoneId(phoneId)); 671 } 672 if (subInfoCleared) { 673 // This triggers update of default subs. This should be done asap after 674 // setMultiSimProperties() to avoid (minimize) duration for which default sub can be 675 // invalid and can map to a non-existent phone. 676 // If forexample someone calls a TelephonyManager API on default sub after 677 // setMultiSimProperties() and before onSubscriptionsChanged() below -- they can be 678 // using an invalid sub, which can map to a non-existent phone and can cause an 679 // exception (see b/163582235). 680 MultiSimSettingController.getInstance().onPhoneRemoved(); 681 } 682 // old phone objects are not needed now; mPhones can be updated 683 mPhones = PhoneFactory.getPhones(); 684 // if numOfActiveModems is increasing, register new RILs 685 // eg if we are going from 1 phone to 2 phones, we need to register RIL for the second 686 // phone. This loop does nothing if numOfActiveModems is decreasing. 687 for (int phoneId = oldNumOfActiveModems; phoneId < numOfActiveModems; phoneId++) { 688 Phone phone = mPhones[phoneId]; 689 registerForRadioState(phone); 690 phone.mCi.onSlotActiveStatusChange(SubscriptionManager.isValidPhoneId(phoneId)); 691 } 692 693 if (numOfActiveModems > 1) { 694 // Check if cellular DSDA is supported. If it is, then send a request to the 695 // modem to refresh the list of SIM slots that currently support DSDA based on 696 // current network conditions 697 maybeEnableCellularDSDASupport(); 698 } else { 699 // The number of active modems is 0 or 1, disable cellular DSDA: 700 mSlotsSupportingSimultaneousCellularCalls.clear(); 701 if (mFeatureFlags.simultaneousCallingIndications()) { 702 updateSimultaneousSubIdsFromPhoneIdMappingAndNotify(); 703 notifySimultaneousCellularCallingSlotsChanged(); 704 } 705 } 706 707 // When the user enables DSDS mode, the default VOICE and SMS subId should be switched 708 // to "No Preference". Doing so will sync the network/sim settings and telephony. 709 // (see b/198123192) 710 if (numOfActiveModems > oldNumOfActiveModems && numOfActiveModems == 2) { 711 Log.i(LOG_TAG, " onMultiSimConfigChanged: DSDS mode enabled; " 712 + "setting VOICE & SMS subId to -1 (No Preference)"); 713 714 //Set the default VOICE subId to -1 ("No Preference") 715 SubscriptionManagerService.getInstance().setDefaultVoiceSubId( 716 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 717 718 //TODO:: Set the default SMS sub to "No Preference". Tracking this bug (b/227386042) 719 } else { 720 Log.i(LOG_TAG, 721 "onMultiSimConfigChanged: DSDS mode NOT detected. NOT setting the " 722 + "default VOICE and SMS subId to -1 (No Preference)"); 723 } 724 } 725 } 726 727 /** 728 * Helper method to set system properties for setting multi sim configs, 729 * as well as doing the phone reboot 730 * NOTE: In order to support more than 3 sims, we need to change this method. 731 * @param numOfActiveModems number of active sims 732 */ setMultiSimProperties(int numOfActiveModems)733 private void setMultiSimProperties(int numOfActiveModems) { 734 mMi.setMultiSimProperties(numOfActiveModems); 735 } 736 737 @VisibleForTesting notifyMultiSimConfigChange(int numOfActiveModems)738 public static void notifyMultiSimConfigChange(int numOfActiveModems) { 739 sMultiSimConfigChangeRegistrants.notifyResult(numOfActiveModems); 740 } 741 742 /** 743 * Register for multi-SIM configuration change, for example if the devices switched from single 744 * SIM to dual-SIM mode. 745 * 746 * It doesn't trigger callback upon registration as multi-SIM config change is in-frequent. 747 */ registerForMultiSimConfigChange(Handler h, int what, Object obj)748 public static void registerForMultiSimConfigChange(Handler h, int what, Object obj) { 749 sMultiSimConfigChangeRegistrants.addUnique(h, what, obj); 750 } 751 752 /** 753 * Unregister for multi-SIM configuration change. 754 */ unregisterForMultiSimConfigChange(Handler h)755 public static void unregisterForMultiSimConfigChange(Handler h) { 756 sMultiSimConfigChangeRegistrants.remove(h); 757 } 758 759 /** 760 * Unregister for all multi-SIM configuration change events. 761 */ unregisterAllMultiSimConfigChangeRegistrants()762 public static void unregisterAllMultiSimConfigChangeRegistrants() { 763 sMultiSimConfigChangeRegistrants.removeAll(); 764 } 765 broadcastMultiSimConfigChange(int numOfActiveModems)766 private void broadcastMultiSimConfigChange(int numOfActiveModems) { 767 log("broadcastSimSlotNumChange numOfActiveModems" + numOfActiveModems); 768 // Notify internal registrants first. 769 notifyMultiSimConfigChange(numOfActiveModems); 770 771 Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED); 772 intent.putExtra(EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, numOfActiveModems); 773 mContext.sendBroadcast(intent); 774 } 775 /** 776 * This is invoked from shell commands during CTS testing only. 777 * @return true if the modem service is set successfully, false otherwise. 778 */ setModemService(String serviceName)779 public boolean setModemService(String serviceName) { 780 log("setModemService: " + serviceName); 781 boolean statusRadioConfig = false; 782 boolean statusRil = false; 783 final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false); 784 final boolean isAllowedForBoot = 785 SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false); 786 787 // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds 788 if (isAllowed || isAllowedForBoot || DEBUG) { 789 if (mRadioConfig != null) { 790 statusRadioConfig = mRadioConfig.setModemService(serviceName); 791 } 792 793 if (!statusRadioConfig) { 794 loge("setModemService: switching modem service for radioconfig fail"); 795 return false; 796 } 797 798 for (int i = 0; i < getPhoneCount(); i++) { 799 if (mPhones[i] != null) { 800 statusRil = mPhones[i].mCi.setModemService(serviceName); 801 } 802 803 if (!statusRil) { 804 loge("setModemService: switch modem for radio " + i + " fail"); 805 806 // Disconnect the switched service 807 mRadioConfig.setModemService(null); 808 for (int t = 0; t < i; t++) { 809 mPhones[t].mCi.setModemService(null); 810 } 811 return false; 812 } 813 } 814 } else { 815 loge("setModemService is not allowed"); 816 return false; 817 } 818 819 return true; 820 } 821 822 /** 823 * This is invoked from shell commands to query during CTS testing only. 824 * @return the service name of the connected service. 825 */ getModemService()826 public String getModemService() { 827 if (mPhones[0] == null) { 828 return ""; 829 } 830 831 return mPhones[0].mCi.getModemService(); 832 } 833 834 /** 835 * A wrapper class that wraps some methods so that they can be replaced or mocked in unit-tests. 836 * 837 * For example, setting or reading system property are static native methods that can't be 838 * directly mocked. We can mock it by replacing MockableInterface object with a mock instance 839 * in unittest. 840 */ 841 @VisibleForTesting 842 public static class MockableInterface { 843 /** 844 * Wrapper function to decide whether reboot is required for modem config change. 845 */ 846 @VisibleForTesting isRebootRequiredForModemConfigChange()847 public boolean isRebootRequiredForModemConfigChange() { 848 boolean rebootRequired = TelephonyProperties.reboot_on_modem_change().orElse(false); 849 log("isRebootRequiredForModemConfigChange: isRebootRequired = " + rebootRequired); 850 return rebootRequired; 851 } 852 853 /** 854 * Wrapper function to call setMultiSimProperties. 855 */ 856 @VisibleForTesting setMultiSimProperties(int numOfActiveModems)857 public void setMultiSimProperties(int numOfActiveModems) { 858 String multiSimConfig; 859 switch(numOfActiveModems) { 860 case 3: 861 multiSimConfig = TSTS; 862 break; 863 case 2: 864 multiSimConfig = DSDS; 865 break; 866 default: 867 multiSimConfig = SSSS; 868 } 869 870 log("setMultiSimProperties to " + multiSimConfig); 871 TelephonyProperties.multi_sim_config(multiSimConfig); 872 } 873 874 /** 875 * Wrapper function to call PhoneFactory.onMultiSimConfigChanged. 876 */ 877 @VisibleForTesting notifyPhoneFactoryOnMultiSimConfigChanged( Context context, int numOfActiveModems)878 public void notifyPhoneFactoryOnMultiSimConfigChanged( 879 Context context, int numOfActiveModems) { 880 PhoneFactory.onMultiSimConfigChanged(context, numOfActiveModems); 881 } 882 883 /** 884 * Wrapper function to query the sysprop for multi_sim_config 885 */ getMultiSimProperty()886 public Optional<String> getMultiSimProperty() { 887 return TelephonyProperties.multi_sim_config(); 888 } 889 } 890 log(String s)891 private static void log(String s) { 892 Rlog.d(LOG_TAG, s); 893 } 894 loge(String s)895 private static void loge(String s) { 896 Rlog.e(LOG_TAG, s); 897 } 898 loge(String s, Exception ex)899 private static void loge(String s, Exception ex) { 900 Rlog.e(LOG_TAG, s, ex); 901 } 902 } 903