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