1 /* 2 * Copyright 2019 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.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 20 import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING; 21 import static android.telephony.SubscriptionManager.TRANSFER_STATUS_CONVERTED; 22 import static android.telephony.TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED; 23 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE; 24 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL; 25 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA; 26 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS; 27 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE; 28 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_NAMES; 29 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE; 30 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA; 31 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE; 32 import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID; 33 34 import android.annotation.CallbackExecutor; 35 import android.annotation.IntDef; 36 import android.annotation.NonNull; 37 import android.app.PendingIntent; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.pm.PackageManager; 41 import android.os.AsyncResult; 42 import android.os.Handler; 43 import android.os.Looper; 44 import android.os.Message; 45 import android.os.ParcelUuid; 46 import android.os.UserHandle; 47 import android.provider.Settings; 48 import android.provider.Settings.SettingNotFoundException; 49 import android.telephony.CarrierConfigManager; 50 import android.telephony.SubscriptionInfo; 51 import android.telephony.SubscriptionManager; 52 import android.telephony.TelephonyManager; 53 import android.telephony.euicc.EuiccManager; 54 import android.text.TextUtils; 55 import android.util.Log; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback; 59 import com.android.internal.telephony.flags.FeatureFlags; 60 import com.android.internal.telephony.satellite.SatelliteController; 61 import com.android.internal.telephony.subscription.SubscriptionInfoInternal; 62 import com.android.internal.telephony.subscription.SubscriptionManagerService; 63 import com.android.internal.telephony.util.ArrayUtils; 64 65 import java.lang.annotation.Retention; 66 import java.lang.annotation.RetentionPolicy; 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.List; 70 import java.util.concurrent.Executor; 71 import java.util.stream.Collectors; 72 73 /** 74 * This class will make sure below setting rules are coordinated across different subscriptions 75 * and phones in multi-SIM case: 76 * 77 * 1) Grouped subscriptions will have same settings for MOBILE_DATA and DATA_ROAMING. 78 * 2) Default settings updated automatically. It may be cleared or inherited within group. 79 * If default subscription A switches to profile B which is in the same group, B will 80 * become the new default. 81 * 3) For primary subscriptions, only default data subscription will have MOBILE_DATA on. 82 */ 83 public class MultiSimSettingController extends Handler { 84 private static final String LOG_TAG = "MultiSimSettingController"; 85 private static final boolean DBG = true; 86 private static final int EVENT_USER_DATA_ENABLED = 1; 87 private static final int EVENT_ROAMING_DATA_ENABLED = 2; 88 private static final int EVENT_ALL_SUBSCRIPTIONS_LOADED = 3; 89 private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4; 90 private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5; 91 private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6; 92 @VisibleForTesting 93 public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8; 94 @VisibleForTesting 95 public static final int EVENT_RADIO_STATE_CHANGED = 9; 96 97 @Retention(RetentionPolicy.SOURCE) 98 @IntDef(prefix = {"PRIMARY_SUB_"}, 99 value = { 100 PRIMARY_SUB_NO_CHANGE, 101 PRIMARY_SUB_ADDED, 102 PRIMARY_SUB_REMOVED, 103 PRIMARY_SUB_SWAPPED, 104 PRIMARY_SUB_SWAPPED_IN_GROUP, 105 PRIMARY_SUB_MARKED_OPPT, 106 PRIMARY_SUB_INITIALIZED, 107 PRIMARY_SUB_REMOVED_IN_GROUP 108 }) 109 private @interface PrimarySubChangeType {} 110 111 // Primary subscription not change. 112 private static final int PRIMARY_SUB_NO_CHANGE = 0; 113 // One or more primary subscriptions are activated. 114 private static final int PRIMARY_SUB_ADDED = 1; 115 // One or more primary subscriptions are deactivated. 116 private static final int PRIMARY_SUB_REMOVED = 2; 117 // One or more primary subscriptions are swapped. 118 private static final int PRIMARY_SUB_SWAPPED = 3; 119 // One or more primary subscriptions are swapped but within same sub group. 120 private static final int PRIMARY_SUB_SWAPPED_IN_GROUP = 4; 121 // One or more primary subscriptions are marked as opportunistic. 122 private static final int PRIMARY_SUB_MARKED_OPPT = 5; 123 // Subscription information is initially loaded. 124 private static final int PRIMARY_SUB_INITIALIZED = 6; 125 // One or more primary subscriptions are deactivated but within the same group as another active 126 // sub. 127 private static final int PRIMARY_SUB_REMOVED_IN_GROUP = 7; 128 129 protected final Context mContext; 130 private final SubscriptionManagerService mSubscriptionManagerService; 131 private final @NonNull FeatureFlags mFeatureFlags; 132 133 // Keep a record of active primary (non-opportunistic) subscription list. 134 @NonNull private List<Integer> mPrimarySubList = new ArrayList<>(); 135 136 /** The singleton instance. */ 137 protected static MultiSimSettingController sInstance = null; 138 139 // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED. 140 private boolean mSubInfoInitialized = false; 141 142 // mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot. 143 // After boot-up when things are firstly initialized (mSubInfoInitialized is changed to true 144 // and carrier configs are all loaded), we do a reEvaluateAll(). In the first reEvaluateAll(), 145 // mInitialHandling will be true and we won't pop up SIM select dialog. 146 private boolean mInitialHandling = true; 147 148 // Keep a record of which subIds have carrier config loaded. Length of the array is phone count. 149 // The index is phoneId, and value is subId. For example: 150 // If carrier config of subId 2 is loaded on phone 0,mCarrierConfigLoadedSubIds[0] = 2. 151 // Then if subId 2 is deactivated from phone 0, the value becomes INVALID, 152 // mCarrierConfigLoadedSubIds[0] = INVALID_SUBSCRIPTION_ID. 153 private int[] mCarrierConfigLoadedSubIds; 154 155 // It indicates whether "Ask every time" option for default SMS subscription is supported by the 156 // device. 157 private final boolean mIsAskEverytimeSupportedForSms; 158 159 // The number of existing DataSettingsControllerCallback 160 private int mCallbacksCount; 161 /** The number of active modem count. */ 162 private int mActiveModemCount; 163 164 private boolean mNeedSetDefaultVoice; 165 private boolean mNeedSetDefaultSms; 166 private boolean mNeedSetDefaultData; 167 private int mConvertedPsimSubId; 168 169 private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; 170 171 private static class DataSettingsControllerCallback extends DataSettingsManagerCallback { 172 private final Phone mPhone; 173 DataSettingsControllerCallback(@onNull Phone phone, @NonNull @CallbackExecutor Executor executor)174 DataSettingsControllerCallback(@NonNull Phone phone, 175 @NonNull @CallbackExecutor Executor executor) { 176 super(executor); 177 mPhone = phone; 178 } 179 180 @Override onUserDataEnabledChanged(boolean enabled, @NonNull String callingPackage)181 public void onUserDataEnabledChanged(boolean enabled, @NonNull String callingPackage) { 182 int subId = mPhone.getSubId(); 183 // only notifyUserDataEnabled if the change is called from external to avoid 184 // setUserDataEnabledForGroup infinite loop 185 if (SubscriptionManager.isValidSubscriptionId(subId) 186 && !getInstance().mContext.getOpPackageName().equals(callingPackage)) { 187 getInstance().notifyUserDataEnabled(subId, enabled); 188 } 189 } 190 191 @Override onDataRoamingEnabledChanged(boolean enabled)192 public void onDataRoamingEnabledChanged(boolean enabled) { 193 int subId = mPhone.getSubId(); 194 if (SubscriptionManager.isValidSubscriptionId(subId)) { 195 getInstance().notifyRoamingDataEnabled(mPhone.getSubId(), enabled); 196 } 197 } 198 } 199 200 /** 201 * Return the singleton or create one if not existed. 202 */ getInstance()203 public static MultiSimSettingController getInstance() { 204 synchronized (MultiSimSettingController.class) { 205 if (sInstance == null) { 206 Log.wtf(LOG_TAG, "getInstance null"); 207 } 208 209 return sInstance; 210 } 211 } 212 213 /** 214 * Init instance of MultiSimSettingController. 215 */ init(Context context, @NonNull FeatureFlags featureFlags)216 public static MultiSimSettingController init(Context context, 217 @NonNull FeatureFlags featureFlags) { 218 synchronized (MultiSimSettingController.class) { 219 if (sInstance == null) { 220 sInstance = new MultiSimSettingController(context, featureFlags); 221 } else { 222 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 223 } 224 return sInstance; 225 } 226 } 227 228 @VisibleForTesting MultiSimSettingController(Context context, @NonNull FeatureFlags featureFlags)229 public MultiSimSettingController(Context context, @NonNull FeatureFlags featureFlags) { 230 mContext = context; 231 mSubscriptionManagerService = SubscriptionManagerService.getInstance(); 232 mFeatureFlags = featureFlags; 233 234 // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change. 235 TelephonyManager telephonyManager = ((TelephonyManager) mContext.getSystemService( 236 TelephonyManager.class)); 237 final int phoneCount = telephonyManager.getSupportedModemCount(); 238 mCarrierConfigLoadedSubIds = new int[phoneCount]; 239 Arrays.fill(mCarrierConfigLoadedSubIds, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 240 241 mActiveModemCount = telephonyManager.getActiveModemCount(); 242 243 PhoneConfigurationManager.registerForMultiSimConfigChange( 244 this, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 245 246 mIsAskEverytimeSupportedForSms = mContext.getResources() 247 .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support); 248 249 CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class); 250 // Listener callback is executed on handler thread to directly handle config change 251 if (ccm != null) { 252 ccm.registerCarrierConfigChangeListener(this::post, 253 (slotIndex, subId, carrierId, specificCarrierId) -> 254 onCarrierConfigChanged(slotIndex, subId)); 255 } 256 257 mConvertedPsimSubId = getConvertedPsimSubscriptionId(); 258 } 259 hasCalling()260 private boolean hasCalling() { 261 if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true; 262 return mContext.getPackageManager().hasSystemFeature( 263 PackageManager.FEATURE_TELEPHONY_CALLING); 264 } 265 hasData()266 private boolean hasData() { 267 if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true; 268 return mContext.getPackageManager().hasSystemFeature( 269 PackageManager.FEATURE_TELEPHONY_DATA); 270 } 271 hasMessaging()272 private boolean hasMessaging() { 273 if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true; 274 return mContext.getPackageManager().hasSystemFeature( 275 PackageManager.FEATURE_TELEPHONY_MESSAGING); 276 } 277 278 /** 279 * Notify MOBILE_DATA of a subscription is changed. 280 */ notifyUserDataEnabled(int subId, boolean enable)281 public void notifyUserDataEnabled(int subId, boolean enable) { 282 if (SubscriptionManager.isValidSubscriptionId(subId)) { 283 obtainMessage(EVENT_USER_DATA_ENABLED, subId, enable ? 1 : 0).sendToTarget(); 284 } 285 } 286 287 /** 288 * Notify DATA_ROAMING of a subscription is changed. 289 */ notifyRoamingDataEnabled(int subId, boolean enable)290 public void notifyRoamingDataEnabled(int subId, boolean enable) { 291 if (SubscriptionManager.isValidSubscriptionId(subId)) { 292 obtainMessage(EVENT_ROAMING_DATA_ENABLED, subId, enable ? 1 : 0).sendToTarget(); 293 } 294 } 295 296 /** 297 * Notify that, for the first time after boot, SIMs are initialized. 298 * Should only be triggered once. 299 */ notifyAllSubscriptionLoaded()300 public void notifyAllSubscriptionLoaded() { 301 obtainMessage(EVENT_ALL_SUBSCRIPTIONS_LOADED).sendToTarget(); 302 } 303 304 /** 305 * Notify subscription info change. 306 */ notifySubscriptionInfoChanged()307 public void notifySubscriptionInfoChanged() { 308 log("notifySubscriptionInfoChanged"); 309 obtainMessage(EVENT_SUBSCRIPTION_INFO_CHANGED).sendToTarget(); 310 } 311 312 /** 313 * Notify subscription group information change. 314 */ notifySubscriptionGroupChanged(ParcelUuid groupUuid)315 public void notifySubscriptionGroupChanged(ParcelUuid groupUuid) { 316 obtainMessage(EVENT_SUBSCRIPTION_GROUP_CHANGED, groupUuid).sendToTarget(); 317 } 318 319 /** 320 * Notify default data subscription change. 321 */ notifyDefaultDataSubChanged()322 public void notifyDefaultDataSubChanged() { 323 obtainMessage(EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED).sendToTarget(); 324 } 325 326 @Override handleMessage(Message msg)327 public void handleMessage(Message msg) { 328 switch (msg.what) { 329 case EVENT_USER_DATA_ENABLED: { 330 int subId = msg.arg1; 331 boolean enable = msg.arg2 != 0; 332 onUserDataEnabled(subId, enable, true); 333 break; 334 } 335 case EVENT_ROAMING_DATA_ENABLED: { 336 int subId = msg.arg1; 337 boolean enable = msg.arg2 != 0; 338 onRoamingDataEnabled(subId, enable); 339 break; 340 } 341 case EVENT_ALL_SUBSCRIPTIONS_LOADED: 342 onAllSubscriptionsLoaded(); 343 break; 344 case EVENT_SUBSCRIPTION_INFO_CHANGED: 345 onSubscriptionsChanged(); 346 break; 347 case EVENT_SUBSCRIPTION_GROUP_CHANGED: 348 ParcelUuid groupUuid = (ParcelUuid) msg.obj; 349 onSubscriptionGroupChanged(groupUuid); 350 break; 351 case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED: 352 onDefaultDataSettingChanged(); 353 break; 354 case EVENT_MULTI_SIM_CONFIG_CHANGED: 355 int activeModems = (int) ((AsyncResult) msg.obj).result; 356 onMultiSimConfigChanged(activeModems); 357 break; 358 case EVENT_RADIO_STATE_CHANGED: 359 for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) { 360 Phone phone = PhoneFactory.getPhone(phoneId); 361 if (phone != null && phone.mCi.getRadioState() 362 == TelephonyManager.RADIO_POWER_UNAVAILABLE) { 363 if (DBG) { 364 log("Radio unavailable on phone " + phoneId 365 + ", clearing sub info initialized flag"); 366 } 367 mSubInfoInitialized = false; 368 break; 369 } 370 } 371 break; 372 } 373 } 374 375 /** 376 * Make sure MOBILE_DATA of subscriptions in same group are synced. 377 * 378 * If user is enabling a non-default non-opportunistic subscription, make it default 379 * data subscription. 380 */ onUserDataEnabled(int subId, boolean enable, boolean setDefaultData)381 private void onUserDataEnabled(int subId, boolean enable, boolean setDefaultData) { 382 if (DBG) log("[onUserDataEnabled] subId=" + subId + " enable=" + enable + 383 " setDefaultData=" + setDefaultData); 384 // Make sure MOBILE_DATA of subscriptions in same group are synced. 385 setUserDataEnabledForGroup(subId, enable); 386 387 SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); 388 int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); 389 390 // If user is enabling a non-default non-opportunistic subscription, make it default. 391 if (defaultDataSubId != subId && subInfo != null && !subInfo.isOpportunistic() && enable 392 && subInfo.isActive() && setDefaultData) { 393 android.provider.Settings.Global.putInt(mContext.getContentResolver(), 394 SETTING_USER_PREF_DATA_SUB, subId); 395 mSubscriptionManagerService.setDefaultDataSubId(subId); 396 } 397 } 398 399 /** 400 * Make sure DATA_ROAMING of subscriptions in same group are synced. 401 */ onRoamingDataEnabled(int subId, boolean enable)402 private void onRoamingDataEnabled(int subId, boolean enable) { 403 if (DBG) log("onRoamingDataEnabled"); 404 setRoamingDataEnabledForGroup(subId, enable); 405 406 // Also inform SubscriptionManagerService as it keeps another copy of user setting. 407 mSubscriptionManagerService.setDataRoaming(enable ? 1 : 0, subId); 408 } 409 410 /** 411 * Upon initialization or radio available, update defaults and mobile data enabling. 412 * Should only be triggered once. 413 */ onAllSubscriptionsLoaded()414 private void onAllSubscriptionsLoaded() { 415 if (DBG) log("onAllSubscriptionsLoaded: mSubInfoInitialized=" + mSubInfoInitialized); 416 if (!mSubInfoInitialized) { 417 mSubInfoInitialized = true; 418 mConvertedPsimSubId = getConvertedPsimSubscriptionId(); 419 for (Phone phone : PhoneFactory.getPhones()) { 420 phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); 421 } 422 reEvaluateAll(); 423 } 424 registerDataSettingsControllerCallbackAsNeeded(); 425 } 426 427 /** 428 * Make sure default values are cleaned or updated. 429 * 430 * Make sure non-default non-opportunistic subscriptions has data off. 431 */ onSubscriptionsChanged()432 private void onSubscriptionsChanged() { 433 if (DBG) log("onSubscriptionsChanged"); 434 reEvaluateAll(); 435 } 436 437 /** 438 * This method is called when a phone object is removed (for example when going from multi-sim 439 * to single-sim). 440 * NOTE: This method does not post a message to self, instead it calls reEvaluateAll() directly. 441 * so it should only be called from the main thread. The reason is to update defaults asap 442 * after multi_sim_config property has been updated (see b/163582235). 443 */ onPhoneRemoved()444 public void onPhoneRemoved() { 445 if (DBG) log("onPhoneRemoved"); 446 if (Looper.myLooper() != this.getLooper()) { 447 throw new RuntimeException("This method must be called from the same looper as " 448 + "MultiSimSettingController."); 449 } 450 reEvaluateAll(); 451 } 452 onCarrierConfigChanged(int phoneId, int subId)453 private void onCarrierConfigChanged(int phoneId, int subId) { 454 log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId); 455 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 456 loge("Carrier config change with invalid phoneId " + phoneId); 457 return; 458 } 459 460 CarrierConfigManager cm; 461 if (!SubscriptionManager.isValidSubscriptionId(subId) // record SIM absent. 462 || ((cm = mContext.getSystemService(CarrierConfigManager.class)) != null 463 && CarrierConfigManager.isConfigForIdentifiedCarrier( 464 cm.getConfigForSubId(subId)))) { 465 mCarrierConfigLoadedSubIds[phoneId] = subId; 466 reEvaluateAll(); 467 } 468 } 469 470 /** 471 * Check whether carrier config loaded for all subs 472 */ 473 @VisibleForTesting isCarrierConfigLoadedForAllSub()474 public boolean isCarrierConfigLoadedForAllSub() { 475 int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(false); 476 for (int activeSubId : activeSubIds) { 477 boolean isLoaded = false; 478 for (int configLoadedSub : mCarrierConfigLoadedSubIds) { 479 if (configLoadedSub == activeSubId) { 480 isLoaded = true; 481 break; 482 } 483 } 484 if (!isLoaded) { 485 if (DBG) log("Carrier config subId " + activeSubId + " is not loaded."); 486 return false; 487 } 488 } 489 490 return true; 491 } 492 onMultiSimConfigChanged(int activeModems)493 private void onMultiSimConfigChanged(int activeModems) { 494 mActiveModemCount = activeModems; 495 log("onMultiSimConfigChanged: current ActiveModemCount=" + mActiveModemCount); 496 // Clear mCarrierConfigLoadedSubIds. Other actions will responds to active 497 // subscription change. 498 for (int phoneId = activeModems; phoneId < mCarrierConfigLoadedSubIds.length; phoneId++) { 499 mCarrierConfigLoadedSubIds[phoneId] = INVALID_SUBSCRIPTION_ID; 500 } 501 for (Phone phone : PhoneFactory.getPhones()) { 502 phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); 503 } 504 registerDataSettingsControllerCallbackAsNeeded(); 505 } 506 507 /** 508 * Wait for subInfo initialization (after boot up or radio unavailable) and carrier config load 509 * for all active subscriptions before re-evaluate multi SIM settings. 510 */ isReadyToReevaluate()511 private boolean isReadyToReevaluate() { 512 boolean carrierConfigsLoaded = isCarrierConfigLoadedForAllSub(); 513 SatelliteController satelliteController = SatelliteController.getInstance(); 514 boolean isSatelliteEnabledOrBeingEnabled = false; 515 if (satelliteController != null) { 516 isSatelliteEnabledOrBeingEnabled = 517 satelliteController.isSatelliteEnabledOrBeingEnabled(); 518 } 519 520 if (DBG) { 521 log("isReadyToReevaluate: subInfoInitialized=" + mSubInfoInitialized 522 + ", carrierConfigsLoaded=" + carrierConfigsLoaded 523 + ", satelliteEnabledOrBeingEnabled=" + isSatelliteEnabledOrBeingEnabled); 524 } 525 return mSubInfoInitialized && carrierConfigsLoaded 526 && !isSatelliteEnabledOrBeingEnabled; 527 } 528 reEvaluateAll()529 private void reEvaluateAll() { 530 if (!isReadyToReevaluate()) return; 531 updateDefaults(); 532 disableDataForNonDefaultNonOpportunisticSubscriptions(); 533 deactivateGroupedOpportunisticSubscriptionIfNeeded(); 534 } 535 536 /** 537 * Make sure non-default non-opportunistic subscriptions has data disabled. 538 */ onDefaultDataSettingChanged()539 private void onDefaultDataSettingChanged() { 540 if (DBG) log("onDefaultDataSettingChanged"); 541 disableDataForNonDefaultNonOpportunisticSubscriptions(); 542 } 543 544 /** 545 * When a subscription group is created or new subscriptions are added in the group, make 546 * sure the settings among them are synced. 547 * TODO: b/130258159 have a separate database table for grouped subscriptions so we don't 548 * manually sync each setting. 549 */ onSubscriptionGroupChanged(ParcelUuid groupUuid)550 private void onSubscriptionGroupChanged(ParcelUuid groupUuid) { 551 if (DBG) log("onSubscriptionGroupChanged"); 552 553 List<SubscriptionInfo> infoList = mSubscriptionManagerService.getSubscriptionsInGroup( 554 groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag()); 555 if (infoList == null || infoList.isEmpty()) return; 556 557 // Get a reference subscription to copy settings from. 558 // TODO: the reference sub should be passed in from external caller. 559 int refSubId = infoList.get(0).getSubscriptionId(); 560 for (SubscriptionInfo info : infoList) { 561 int subId = info.getSubscriptionId(); 562 if (info.isActive() && !info.isOpportunistic()) { 563 refSubId = subId; 564 break; 565 } 566 } 567 568 if (DBG) log("refSubId is " + refSubId); 569 570 boolean enable = false; 571 try { 572 enable = GlobalSettingsHelper.getBoolean( 573 mContext, Settings.Global.MOBILE_DATA, refSubId); 574 } catch (SettingNotFoundException exception) { 575 //pass invalid refSubId to fetch the single-sim setting 576 enable = GlobalSettingsHelper.getBoolean( 577 mContext, Settings.Global.MOBILE_DATA, INVALID_SUBSCRIPTION_ID, enable); 578 } 579 boolean setDefaultData = true; 580 List<SubscriptionInfo> activeSubList = mSubscriptionManagerService 581 .getActiveSubscriptionInfoList(mContext.getOpPackageName(), 582 mContext.getAttributionTag(), true/*isForAllProfile*/); 583 for (SubscriptionInfo activeInfo : activeSubList) { 584 if (!(groupUuid.equals(activeInfo.getGroupUuid()))) { 585 // Do not set refSubId as defaultDataSubId if there are other active 586 // subscriptions which does not belong to this groupUuid 587 setDefaultData = false; 588 break; 589 } 590 } 591 onUserDataEnabled(refSubId, enable, setDefaultData); 592 593 enable = false; 594 try { 595 enable = GlobalSettingsHelper.getBoolean( 596 mContext, Settings.Global.DATA_ROAMING, refSubId); 597 onRoamingDataEnabled(refSubId, enable); 598 } catch (SettingNotFoundException exception) { 599 //pass invalid refSubId to fetch the single-sim setting 600 enable = GlobalSettingsHelper.getBoolean( 601 mContext, Settings.Global.DATA_ROAMING, INVALID_SUBSCRIPTION_ID, enable); 602 onRoamingDataEnabled(refSubId, enable); 603 } 604 605 mSubscriptionManagerService.syncGroupedSetting(refSubId); 606 } 607 608 /** 609 * Automatically update default settings (data / voice / sms). 610 * 611 * Opportunistic subscriptions can't be default data / voice / sms subscription. 612 * 613 * 1) If the default subscription is still active, keep it unchanged. 614 * 2) Or if there's another active primary subscription that's in the same group, 615 * make it the new default value. 616 * 3) Or if there's only one active primary subscription, automatically set default 617 * data subscription on it. Because default data in Android Q is an internal value, 618 * not a user settable value anymore. 619 * 4) If non above is met, clear the default value to INVALID. 620 * 621 */ updateDefaults()622 protected void updateDefaults() { 623 if (DBG) log("updateDefaults"); 624 if (!isReadyToReevaluate()) return; 625 626 List<SubscriptionInfo> activeSubInfos = mSubscriptionManagerService 627 .getActiveSubscriptionInfoList(mContext.getOpPackageName(), 628 mContext.getAttributionTag(), true/*isForAllProfile*/); 629 630 if (ArrayUtils.isEmpty(activeSubInfos)) { 631 mPrimarySubList.clear(); 632 if (DBG) log("updateDefaults: No active sub. Setting default to INVALID sub."); 633 mSubscriptionManagerService.setDefaultDataSubId( 634 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 635 mSubscriptionManagerService.setDefaultVoiceSubId( 636 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 637 mSubscriptionManagerService.setDefaultSmsSubId( 638 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 639 return; 640 } 641 642 int change = updatePrimarySubListAndGetChangeType(activeSubInfos); 643 if (DBG) log("updateDefaultValues: change: " + change); 644 if (change == PRIMARY_SUB_NO_CHANGE) return; 645 646 // If there's only one primary subscription active, we trigger mobile data 647 // dialog if and only if there were multiple primary SIM cards and one is removed. 648 // Otherwise, if user just inserted their first SIM, or there's one primary and one 649 // opportunistic subscription active (activeSubInfos.size() > 1), we automatically 650 // set the primary to be default SIM and return. 651 boolean conditionForOnePrimarySim = mPrimarySubList.size() == 1; 652 if (conditionForOnePrimarySim) { 653 int subId = mPrimarySubList.get(0); 654 if (DBG) log("updateDefaultValues: to only primary sub " + subId); 655 if (hasData()) mSubscriptionManagerService.setDefaultDataSubId(subId); 656 if (hasCalling()) mSubscriptionManagerService.setDefaultVoiceSubId(subId); 657 if (hasMessaging()) mSubscriptionManagerService.setDefaultSmsSubId(subId); 658 if (!mSubscriptionManagerService.isEsimBootStrapProvisioningActivated()) { 659 // Determines the appropriate notification type 660 // Preconditions: 661 // - There is only one active primary subscription. 662 // - The eSIM bootstrap is NOT activated. 663 // Behavior: 664 // - If the primary subscription is not deactivated OR the device is in single SIM 665 // mode, send a notification to dismiss the SIM dialog. 666 // - Otherwise, send a notification to trigger the preferred SIM/data pick dialog. 667 @TelephonyManager.DefaultSubscriptionSelectType 668 int type = (change != PRIMARY_SUB_REMOVED || mActiveModemCount == 1) 669 ? EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS 670 : EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL; 671 sendDefaultSubConfirmedNotification(type, subId); 672 } 673 return; 674 } 675 676 if (DBG) log("updateDefaultValues: records: " + mPrimarySubList); 677 678 boolean dataSelected = false; 679 boolean voiceSelected = false; 680 boolean smsSelected = false; 681 682 if (hasData()) { 683 // Update default data subscription. 684 if (DBG) log("updateDefaultValues: Update default data subscription"); 685 dataSelected = updateDefaultValue(mPrimarySubList, 686 mSubscriptionManagerService.getDefaultDataSubId(), 687 mSubscriptionManagerService::setDefaultDataSubId); 688 } 689 690 if (hasCalling()) { 691 // Update default voice subscription. 692 if (DBG) log("updateDefaultValues: Update default voice subscription"); 693 voiceSelected = updateDefaultValue(mPrimarySubList, 694 mSubscriptionManagerService.getDefaultVoiceSubId(), 695 mSubscriptionManagerService::setDefaultVoiceSubId); 696 } 697 698 if (hasMessaging()) { 699 // Update default sms subscription. 700 if (DBG) log("updateDefaultValues: Update default sms subscription"); 701 smsSelected = updateDefaultValue(mPrimarySubList, 702 mSubscriptionManagerService.getDefaultSmsSubId(), 703 mSubscriptionManagerService::setDefaultSmsSubId, 704 mIsAskEverytimeSupportedForSms); 705 } 706 707 boolean autoFallbackEnabled = mContext.getResources().getBoolean( 708 com.android.internal.R.bool.config_voice_data_sms_auto_fallback); 709 710 // Based on config config_voice_data_sms_auto_fallback value choose voice/data/sms 711 // preference auto selection logic or display notification for end used to 712 // select voice/data/SMS preferences. 713 if (!autoFallbackEnabled) { 714 // Hide the dialog for preferred SIM/data pick if the primary subscription change is 715 // due to the pSIM conversion. 716 if (!setDefaultForPsimConversionChanged(change, dataSelected, voiceSelected, 717 smsSelected)) { 718 sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected); 719 } 720 } else { 721 updateUserPreferences(mPrimarySubList, dataSelected, voiceSelected, smsSelected); 722 } 723 } 724 725 @PrimarySubChangeType updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList)726 private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList) { 727 // Update mPrimarySubList. Opportunistic subscriptions can't be default 728 // data / voice / sms subscription. 729 List<Integer> prevPrimarySubList = mPrimarySubList; 730 mPrimarySubList = activeSubList.stream() 731 .filter(info -> !info.isOpportunistic()) 732 .filter(info -> info.getProfileClass() != PROFILE_CLASS_PROVISIONING) 733 .map(info -> info.getSubscriptionId()) 734 .collect(Collectors.toList()); 735 736 if (mInitialHandling) { 737 mInitialHandling = false; 738 return PRIMARY_SUB_INITIALIZED; 739 } 740 if (mPrimarySubList.equals(prevPrimarySubList)) return PRIMARY_SUB_NO_CHANGE; 741 if (mPrimarySubList.size() > prevPrimarySubList.size()) return PRIMARY_SUB_ADDED; 742 743 if (mPrimarySubList.size() == prevPrimarySubList.size()) { 744 // We need to differentiate PRIMARY_SUB_SWAPPED and PRIMARY_SUB_SWAPPED_IN_GROUP: 745 // For SWAPPED_IN_GROUP, we never pop up dialog to ask data sub selection again. 746 for (int subId : mPrimarySubList) { 747 boolean swappedInSameGroup = false; 748 for (int prevSubId : prevPrimarySubList) { 749 if (areSubscriptionsInSameGroup(subId, prevSubId)) { 750 swappedInSameGroup = true; 751 break; 752 } 753 } 754 if (!swappedInSameGroup) return PRIMARY_SUB_SWAPPED; 755 } 756 return PRIMARY_SUB_SWAPPED_IN_GROUP; 757 } else /* mPrimarySubList.size() < prevPrimarySubList.size() */ { 758 // We need to differentiate whether the missing subscription is removed or marked as 759 // opportunistic. Usually only one subscription may change at a time, But to be safe, if 760 // any previous primary subscription becomes inactive, we consider it 761 for (int subId : prevPrimarySubList) { 762 if (mPrimarySubList.contains(subId)) continue; 763 SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); 764 765 if (subInfo == null || !subInfo.isActive()) { 766 for (int currentSubId : mPrimarySubList) { 767 if (areSubscriptionsInSameGroup(currentSubId, subId)) { 768 return PRIMARY_SUB_REMOVED_IN_GROUP; 769 } 770 } 771 return PRIMARY_SUB_REMOVED; 772 } 773 if (!subInfo.isOpportunistic()) { 774 // Should never happen. 775 loge("[updatePrimarySubListAndGetChangeType]: missing active primary " 776 + "subId " + subId); 777 } 778 } 779 return PRIMARY_SUB_MARKED_OPPT; 780 } 781 } 782 sendDefaultSubConfirmedNotification( @elephonyManager.DefaultSubscriptionSelectType int type, int defaultSubId)783 private void sendDefaultSubConfirmedNotification( 784 @TelephonyManager.DefaultSubscriptionSelectType int type, int defaultSubId) { 785 Intent intent = new Intent(); 786 intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED); 787 intent.setClassName("com.android.settings", 788 "com.android.settings.sim.SimSelectNotification"); 789 790 intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, type); 791 intent.putExtra(EXTRA_SUBSCRIPTION_ID, defaultSubId); 792 793 if (mFeatureFlags.hsumBroadcast()) { 794 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 795 } else { 796 mContext.sendBroadcast(intent); 797 } 798 } 799 sendSubChangeNotificationIfNeeded(int change, boolean dataSelected, boolean voiceSelected, boolean smsSelected)800 private void sendSubChangeNotificationIfNeeded(int change, boolean dataSelected, 801 boolean voiceSelected, boolean smsSelected) { 802 803 if (mSubscriptionManagerService.isEsimBootStrapProvisioningActivated()) { 804 log("esim bootstrap activation in progress, skip notification"); 805 return; 806 } 807 808 @TelephonyManager.DefaultSubscriptionSelectType 809 int simSelectDialogType = getSimSelectDialogType( 810 change, dataSelected, voiceSelected, smsSelected); 811 log("sendSubChangeNotificationIfNeeded: simSelectDialogType=" + simSelectDialogType); 812 SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change); 813 814 if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE 815 || simCombinationParams.mWarningType != EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE) { 816 log("[sendSubChangeNotificationIfNeeded] showing dialog type " 817 + simSelectDialogType); 818 log("[sendSubChangeNotificationIfNeeded] showing sim warning " 819 + simCombinationParams.mWarningType); 820 Intent intent = new Intent(); 821 intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED); 822 intent.setClassName("com.android.settings", 823 "com.android.settings.sim.SimSelectNotification"); 824 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 825 826 intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, simSelectDialogType); 827 if (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL) { 828 intent.putExtra(EXTRA_SUBSCRIPTION_ID, mPrimarySubList.get(0)); 829 } 830 831 intent.putExtra(EXTRA_SIM_COMBINATION_WARNING_TYPE, simCombinationParams.mWarningType); 832 if (simCombinationParams.mWarningType == EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA) { 833 intent.putExtra(EXTRA_SIM_COMBINATION_NAMES, simCombinationParams.mSimNames); 834 } 835 if (mFeatureFlags.hsumBroadcast()) { 836 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 837 } else { 838 mContext.sendBroadcast(intent); 839 } 840 } 841 } 842 843 /** 844 * Check that the primary subscription has changed due to the pSIM conversion. 845 * @param change Whether to update the mPrimarySubList. 846 * @param dataSelected Whether the default data subscription is updated 847 * @param voiceSelected Whether the default voice subscription is updated 848 * @param smsSelected Whether the default sms subscription is updated 849 * @return {@code true} if the primary subscription has changed due to the pSIM conversion, 850 * {@code false} otherwise. 851 */ setDefaultForPsimConversionChanged(int change, boolean dataSelected, boolean voiceSelected, boolean smsSelected)852 private boolean setDefaultForPsimConversionChanged(int change, boolean dataSelected, 853 boolean voiceSelected, boolean smsSelected) { 854 if (!mFeatureFlags.supportPsimToEsimConversion()) { 855 log("pSIM to eSIM conversion is not supported"); 856 return false; 857 } 858 if (mSubscriptionManagerService.isEsimBootStrapProvisioningActivated()) { 859 log("esim bootstrap activation in progress, skip notification"); 860 return false; 861 } 862 863 @TelephonyManager.DefaultSubscriptionSelectType 864 int simSelectDialogType = getSimSelectDialogType( 865 change, dataSelected, voiceSelected, smsSelected); 866 SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change); 867 log("[setDefaultForPsimConversionChanged]showing dialog type:" + simSelectDialogType); 868 if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE 869 || simCombinationParams.mWarningType != EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE) { 870 log("[setDefaultForPsimConversionChanged]Converted pSIM:" + mConvertedPsimSubId); 871 int subId = getConvertedPsimSubscriptionId(); 872 if (subId != INVALID_SUBSCRIPTION_ID && subId != mConvertedPsimSubId) { 873 // If a primary subscription is removed and only one is left active, ask user 874 // for preferred sub selection if any default setting is not set. 875 if (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL) { 876 // check if pSIM's preference is voice. 877 if (mSubscriptionManagerService.getDefaultVoiceSubId() 878 == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 879 mNeedSetDefaultVoice = true; 880 } 881 // check if pSIM's preference is sms. 882 if (mSubscriptionManagerService.getDefaultSmsSubId() 883 == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 884 mNeedSetDefaultSms = true; 885 } 886 // check if pSIM's preference is data. 887 if (mSubscriptionManagerService.getDefaultDataSubId() 888 == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 889 mNeedSetDefaultData = true; 890 } 891 log("select type all, set preferred SIM :" + mPrimarySubList.get(0)); 892 mSubscriptionManagerService.setDefaultVoiceSubId(mPrimarySubList.get(0)); 893 mSubscriptionManagerService.setDefaultSmsSubId(mPrimarySubList.get(0)); 894 mSubscriptionManagerService.setDefaultDataSubId(mPrimarySubList.get(0)); 895 return true; 896 } else if (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA) { 897 // If another primary subscription is added or default data is not selected, ask 898 // user to select default for data as it's most important. 899 int newSubId = mPrimarySubList.get(0); 900 log("need to set voice:" + mNeedSetDefaultVoice 901 + ", sms:" + mNeedSetDefaultSms 902 + ", data:" + mNeedSetDefaultData); 903 // if the converted pSIM's preference is voice, set the default 904 // setting for the changed primary subscription to voice. 905 if (mNeedSetDefaultVoice) { 906 log("set preferred call, subId:" + newSubId); 907 mSubscriptionManagerService.setDefaultVoiceSubId(newSubId); 908 mNeedSetDefaultVoice = false; 909 } 910 // if the converted pSIM's preference is sms, set the default 911 // setting for the changed primary subscription to sms. 912 if (mNeedSetDefaultSms) { 913 log("set preferred sms, subId:" + newSubId); 914 mSubscriptionManagerService.setDefaultSmsSubId(newSubId); 915 mNeedSetDefaultSms = false; 916 } 917 // if the converted pSIM's preference is data, set the default 918 // setting for the changed primary subscription to data. 919 if (mNeedSetDefaultData) { 920 log("set preferred data, subId:" + newSubId); 921 mSubscriptionManagerService.setDefaultDataSubId(newSubId); 922 mNeedSetDefaultData = false; 923 } 924 mConvertedPsimSubId = subId; 925 log("set converted pSIM subId:" + mConvertedPsimSubId); 926 return true; 927 } 928 } 929 } 930 return false; 931 } 932 getConvertedPsimSubscriptionId()933 private int getConvertedPsimSubscriptionId() { 934 // Check to see if any subscription has been converted due to the pSIM conversion. 935 // When the primary subscription is changed, if it is the same subscription as 936 // the previously converted subscription, it is not due to the pSIM conversion. 937 // So the dialog for preferred SIM/data pick should show. 938 // TODO(b/332261793): On Android W, we need to add CONVERTING status. 939 // The CONVERTING status allows us to determine if pSIM is in the process of converting, 940 // so we don't need to check for information about previously converted subscriptions. 941 int convertedSubId = INVALID_SUBSCRIPTION_ID; 942 if (mFeatureFlags.supportPsimToEsimConversion()) { 943 List<SubscriptionInfo> infos = 944 mSubscriptionManagerService.getAvailableSubscriptionInfoList( 945 mContext.getOpPackageName(), mContext.getAttributionTag()); 946 for (SubscriptionInfo info : infos) { 947 if (!info.isEmbedded() && info.getTransferStatus() == TRANSFER_STATUS_CONVERTED) { 948 convertedSubId = info.getSubscriptionId(); 949 } 950 } 951 } 952 log("getConvertedPsimSubscriptionId: convertedSubId=" + convertedSubId); 953 return convertedSubId; 954 } 955 getSimSelectDialogType(int change, boolean dataSelected, boolean voiceSelected, boolean smsSelected)956 private int getSimSelectDialogType(int change, boolean dataSelected, 957 boolean voiceSelected, boolean smsSelected) { 958 int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE; 959 960 // Do not show preference selection dialog during SuW as there is fullscreen activity to 961 // choose preference. 962 if (Settings.Global.getInt(mContext.getContentResolver(), 963 Settings.Global.DEVICE_PROVISIONED, 0) == 0) { 964 return dialogType; 965 } 966 // If a primary subscription is removed and only one is left active, ask user 967 // for preferred sub selection if any default setting is not set. 968 // If another primary subscription is added or default data is not selected, ask 969 // user to select default for data as it's most important. 970 if (mPrimarySubList.size() == 1 && change == PRIMARY_SUB_REMOVED 971 && (!dataSelected || !smsSelected || !voiceSelected)) { 972 dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL; 973 } else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change) 974 || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) { 975 // If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again. 976 dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA; 977 } 978 979 return dialogType; 980 } 981 982 private class SimCombinationWarningParams { 983 @TelephonyManager.SimCombinationWarningType 984 int mWarningType = EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE; 985 String mSimNames; 986 } 987 getSimCombinationWarningParams(int change)988 private SimCombinationWarningParams getSimCombinationWarningParams(int change) { 989 SimCombinationWarningParams params = new SimCombinationWarningParams(); 990 // If it's single SIM active, no SIM combination warning is needed. 991 if (mPrimarySubList.size() <= 1) return params; 992 // If it's no primary SIM change or it's not user visible change 993 // (initialized or swapped in a group), no SIM combination warning is needed. 994 if (!isUserVisibleChange(change)) return params; 995 996 List<String> simNames = new ArrayList<>(); 997 int cdmaPhoneCount = 0; 998 for (int subId : mPrimarySubList) { 999 Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 1000 // If a dual CDMA SIM combination warning is needed. 1001 if (phone != null && phone.isCdmaSubscriptionAppPresent()) { 1002 cdmaPhoneCount++; 1003 String simName = null; 1004 SubscriptionInfoInternal subInfo = mSubscriptionManagerService 1005 .getSubscriptionInfoInternal(subId); 1006 if (subInfo != null) { 1007 simName = subInfo.getDisplayName(); 1008 } 1009 if (TextUtils.isEmpty(simName)) { 1010 // Fall back to carrier name. 1011 simName = phone.getCarrierName(); 1012 } 1013 simNames.add(simName); 1014 } 1015 } 1016 1017 if (cdmaPhoneCount > 1) { 1018 params.mWarningType = EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA; 1019 params.mSimNames = String.join(" & ", simNames); 1020 } 1021 1022 return params; 1023 } 1024 isUserVisibleChange(int change)1025 private boolean isUserVisibleChange(int change) { 1026 return (change == PRIMARY_SUB_ADDED || change == PRIMARY_SUB_REMOVED 1027 || change == PRIMARY_SUB_SWAPPED); 1028 } 1029 disableDataForNonDefaultNonOpportunisticSubscriptions()1030 protected void disableDataForNonDefaultNonOpportunisticSubscriptions() { 1031 if (!isReadyToReevaluate()) return; 1032 1033 int defaultDataSub = mSubscriptionManagerService.getDefaultDataSubId(); 1034 1035 for (Phone phone : PhoneFactory.getPhones()) { 1036 SubscriptionInfoInternal subInfo = mSubscriptionManagerService 1037 .getSubscriptionInfoInternal(phone.getSubId()); 1038 boolean isOpportunistic = subInfo != null && subInfo.isOpportunistic(); 1039 if (phone.getSubId() != defaultDataSub 1040 && SubscriptionManager.isValidSubscriptionId(phone.getSubId()) 1041 && !isOpportunistic 1042 && phone.isUserDataEnabled() 1043 && !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) { 1044 log("setting data to false on " + phone.getSubId()); 1045 phone.getDataSettingsManager().setDataEnabled( 1046 TelephonyManager.DATA_ENABLED_REASON_USER, false, 1047 mContext.getOpPackageName()); 1048 } 1049 } 1050 } 1051 areSubscriptionsInSameGroup(int subId1, int subId2)1052 private boolean areSubscriptionsInSameGroup(int subId1, int subId2) { 1053 if (!SubscriptionManager.isUsableSubscriptionId(subId1) 1054 || !SubscriptionManager.isUsableSubscriptionId(subId2)) return false; 1055 if (subId1 == subId2) return true; 1056 1057 SubscriptionInfoInternal subInfo1 = 1058 mSubscriptionManagerService.getSubscriptionInfoInternal(subId1); 1059 SubscriptionInfoInternal subInfo2 = 1060 mSubscriptionManagerService.getSubscriptionInfoInternal(subId2); 1061 return subInfo1 != null && subInfo2 != null 1062 && !TextUtils.isEmpty(subInfo1.getGroupUuid()) 1063 && subInfo1.getGroupUuid().equals(subInfo2.getGroupUuid()); 1064 } 1065 1066 /** 1067 * Make sure MOBILE_DATA of subscriptions in the same group with the subId 1068 * are synced. 1069 */ setUserDataEnabledForGroup(int subId, boolean enable)1070 protected void setUserDataEnabledForGroup(int subId, boolean enable) { 1071 log("setUserDataEnabledForGroup subId " + subId + " enable " + enable); 1072 List<SubscriptionInfo> infoList = null; 1073 SubscriptionInfoInternal subInfo = mSubscriptionManagerService 1074 .getSubscriptionInfoInternal(subId); 1075 if (subInfo != null && !subInfo.getGroupUuid().isEmpty()) { 1076 infoList = mSubscriptionManagerService.getSubscriptionsInGroup( 1077 ParcelUuid.fromString(subInfo.getGroupUuid()), mContext.getOpPackageName(), 1078 mContext.getAttributionTag()); 1079 } 1080 1081 if (infoList == null) return; 1082 1083 for (SubscriptionInfo info : infoList) { 1084 int currentSubId = info.getSubscriptionId(); 1085 // TODO: simplify when setUserDataEnabled becomes singleton 1086 if (info.isActive()) { 1087 // For active subscription, call setUserDataEnabled through DataSettingsManager. 1088 Phone phone = PhoneFactory.getPhone(mSubscriptionManagerService 1089 .getPhoneId(currentSubId)); 1090 // If enable is true and it's not opportunistic subscription, we don't enable it, 1091 // as there can't be two 1092 if (phone != null) { 1093 phone.getDataSettingsManager().setDataEnabled( 1094 TelephonyManager.DATA_ENABLED_REASON_USER, enable, 1095 mContext.getOpPackageName()); 1096 } 1097 } else { 1098 // For inactive subscription, directly write into global settings. 1099 GlobalSettingsHelper.setBoolean( 1100 mContext, Settings.Global.MOBILE_DATA, currentSubId, enable); 1101 } 1102 } 1103 } 1104 1105 /** 1106 * Make sure DATA_ROAMING of subscriptions in the same group with the subId 1107 * are synced. 1108 */ setRoamingDataEnabledForGroup(int subId, boolean enable)1109 private void setRoamingDataEnabledForGroup(int subId, boolean enable) { 1110 SubscriptionInfoInternal subInfo = mSubscriptionManagerService 1111 .getSubscriptionInfoInternal(subId); 1112 if (subInfo == null || subInfo.getGroupUuid().isEmpty()) return; 1113 List<SubscriptionInfo> infoList = SubscriptionManagerService.getInstance() 1114 .getSubscriptionsInGroup(ParcelUuid.fromString(subInfo.getGroupUuid()), 1115 mContext.getOpPackageName(), mContext.getAttributionTag()); 1116 if (infoList == null) return; 1117 1118 for (SubscriptionInfo info : infoList) { 1119 // For inactive subscription, directly write into global settings. 1120 GlobalSettingsHelper.setBoolean( 1121 mContext, Settings.Global.DATA_ROAMING, info.getSubscriptionId(), enable); 1122 } 1123 } 1124 1125 private interface UpdateDefaultAction { update(int newValue)1126 void update(int newValue); 1127 } 1128 1129 // Returns whether the new default value is valid. updateDefaultValue(List<Integer> primarySubList, int oldValue, UpdateDefaultAction action)1130 private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue, 1131 UpdateDefaultAction action) { 1132 return updateDefaultValue(primarySubList, oldValue, action, true); 1133 } 1134 updateDefaultValue(List<Integer> primarySubList, int oldValue, UpdateDefaultAction action, boolean allowInvalidSubId)1135 private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue, 1136 UpdateDefaultAction action, boolean allowInvalidSubId) { 1137 int newValue = INVALID_SUBSCRIPTION_ID; 1138 1139 if (primarySubList.size() > 0) { 1140 for (int subId : primarySubList) { 1141 if (DBG) log("[updateDefaultValue] Record.id: " + subId); 1142 // 1) If the old subId is still active, or there's another active primary 1143 // subscription that is in the same group, that should become the new default 1144 // subscription. 1145 // 2) If the old subId is INVALID_SUBSCRIPTION_ID and allowInvalidSubId is false, 1146 // first active subscription is used for new default always. 1147 if (areSubscriptionsInSameGroup(subId, oldValue) 1148 || (!allowInvalidSubId && oldValue == INVALID_SUBSCRIPTION_ID)) { 1149 newValue = subId; 1150 log("[updateDefaultValue] updates to subId=" + newValue); 1151 break; 1152 } 1153 } 1154 } 1155 1156 if (oldValue != newValue) { 1157 if (DBG) log("[updateDefaultValue: subId] from " + oldValue + " to " + newValue); 1158 action.update(newValue); 1159 } 1160 1161 return SubscriptionManager.isValidSubscriptionId(newValue); 1162 } 1163 1164 // When a primary and its grouped opportunistic subscriptions were active, and the primary 1165 // subscription gets deactivated or removed, we need to automatically disable the grouped 1166 // opportunistic subscription, which will be marked isGroupDisabled as true by SubController. deactivateGroupedOpportunisticSubscriptionIfNeeded()1167 private void deactivateGroupedOpportunisticSubscriptionIfNeeded() { 1168 List<SubscriptionInfo> opptSubList = mSubscriptionManagerService.getAllSubInfoList( 1169 mContext.getOpPackageName(), mContext.getAttributionTag()).stream() 1170 .filter(SubscriptionInfo::isOpportunistic) 1171 .collect(Collectors.toList()); 1172 1173 if (ArrayUtils.isEmpty(opptSubList)) return; 1174 1175 for (SubscriptionInfo info : opptSubList) { 1176 if (info.isGroupDisabled() && info.isActive()) { 1177 log("deactivateGroupedOpportunisticSubscriptionIfNeeded: " 1178 + "Deactivating grouped opportunistic subscription " 1179 + info.getSubscriptionId()); 1180 deactivateSubscription(info); 1181 } 1182 } 1183 } 1184 deactivateSubscription(SubscriptionInfo info)1185 private void deactivateSubscription(SubscriptionInfo info) { 1186 // TODO: b/133379187 have a way to deactivate pSIM. 1187 if (info.isEmbedded()) { 1188 log("[deactivateSubscription] eSIM profile " + info.getSubscriptionId()); 1189 EuiccManager euiccManager = (EuiccManager) 1190 mContext.getSystemService(Context.EUICC_SERVICE); 1191 euiccManager.switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 1192 info.getPortIndex(), PendingIntent.getService( 1193 mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)); 1194 } 1195 } 1196 1197 // Voice/Data/SMS preferences would be auto selected without any user 1198 // confirmation in following scenarios, 1199 // 1. When device powered-up with only one SIM Inserted or while two SIM cards 1200 // present if one SIM is removed(or turned OFF) the reaiming SIM would be 1201 // selected as preferred voice/data/sms SIM. 1202 // 2. When device powered-up with two SIM cards or if two SIM cards 1203 // present on device with new SIM insert(or SIM turn ON) the first inserted SIM 1204 // would be selected as preferred voice/data/sms SIM. updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, boolean voiceSelected, boolean smsSelected)1205 private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, 1206 boolean voiceSelected, boolean smsSelected) { 1207 // In Single SIM case or if there are no activated subs available, no need to update. 1208 // EXIT. 1209 if ((primarySubList.isEmpty()) || (mSubscriptionManagerService 1210 .getActiveSubInfoCountMax() == 1)) { 1211 return; 1212 } 1213 1214 if (!isRadioAvailableOnAllSubs()) { 1215 log("Radio is in Invalid state, Ignore Updating User Preference!!!"); 1216 return; 1217 } 1218 final int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); 1219 1220 if (DBG) { 1221 log("updateUserPreferences: dds = " + defaultDataSubId + " voice = " 1222 + mSubscriptionManagerService.getDefaultVoiceSubId() 1223 + " sms = " + mSubscriptionManagerService.getDefaultSmsSubId()); 1224 } 1225 1226 int autoDefaultSubId = primarySubList.get(0); 1227 1228 if (hasMessaging() && (primarySubList.size() == 1) && !smsSelected) { 1229 mSubscriptionManagerService.setDefaultSmsSubId(autoDefaultSubId); 1230 } 1231 1232 if (hasCalling() && (primarySubList.size() == 1) && !voiceSelected) { 1233 mSubscriptionManagerService.setDefaultVoiceSubId(autoDefaultSubId); 1234 } 1235 1236 int userPrefDataSubId = getUserPrefDataSubIdFromDB(); 1237 1238 log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId 1239 + " next active subId " + autoDefaultSubId); 1240 1241 if (hasData()) { 1242 // If earlier user selected DDS is now available, set that as DDS subId. 1243 if (primarySubList.contains(userPrefDataSubId) 1244 && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) 1245 && (defaultDataSubId != userPrefDataSubId)) { 1246 mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId); 1247 } else if (!dataSelected) { 1248 mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId); 1249 } 1250 } 1251 1252 if (DBG) { 1253 log("updateUserPreferences: after dds = " 1254 + mSubscriptionManagerService.getDefaultDataSubId() + " voice = " 1255 + mSubscriptionManagerService.getDefaultVoiceSubId() + " sms = " 1256 + mSubscriptionManagerService.getDefaultSmsSubId()); 1257 } 1258 } 1259 getUserPrefDataSubIdFromDB()1260 private int getUserPrefDataSubIdFromDB() { 1261 return android.provider.Settings.Global.getInt(mContext.getContentResolver(), 1262 SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1263 } 1264 isRadioAvailableOnAllSubs()1265 private boolean isRadioAvailableOnAllSubs() { 1266 for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) { 1267 Phone phone = PhoneFactory.getPhone(phoneId); 1268 if (phone != null 1269 && (phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE 1270 || phone.isShuttingDown())) { 1271 return false; 1272 } 1273 } 1274 return true; 1275 } 1276 registerDataSettingsControllerCallbackAsNeeded()1277 private void registerDataSettingsControllerCallbackAsNeeded() { 1278 // Only register callbacks for new phone instance as PhoneFactory does not remove 1279 // existing phone instance. 1280 Phone[] phones = PhoneFactory.getPhones(); 1281 for (int i = mCallbacksCount; i < phones.length; i++) { 1282 phones[i].getDataSettingsManager().registerCallback( 1283 new DataSettingsControllerCallback(phones[i], this::post)); 1284 } 1285 mCallbacksCount = phones.length; 1286 } 1287 log(String msg)1288 private void log(String msg) { 1289 Log.d(LOG_TAG, msg); 1290 } 1291 loge(String msg)1292 private void loge(String msg) { 1293 Log.e(LOG_TAG, msg); 1294 } 1295 } 1296