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.TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED; 21 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE; 22 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL; 23 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA; 24 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS; 25 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE; 26 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_NAMES; 27 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE; 28 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA; 29 import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE; 30 import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID; 31 32 import android.annotation.IntDef; 33 import android.annotation.NonNull; 34 import android.app.PendingIntent; 35 import android.content.BroadcastReceiver; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.IntentFilter; 39 import android.os.AsyncResult; 40 import android.os.Handler; 41 import android.os.Looper; 42 import android.os.Message; 43 import android.os.ParcelUuid; 44 import android.provider.Settings; 45 import android.provider.Settings.SettingNotFoundException; 46 import android.telephony.CarrierConfigManager; 47 import android.telephony.SubscriptionInfo; 48 import android.telephony.SubscriptionManager; 49 import android.telephony.TelephonyManager; 50 import android.telephony.euicc.EuiccManager; 51 import android.text.TextUtils; 52 import android.util.Log; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.telephony.util.ArrayUtils; 56 57 import java.lang.annotation.Retention; 58 import java.lang.annotation.RetentionPolicy; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.List; 62 import java.util.stream.Collectors; 63 64 /** 65 * This class will make sure below setting rules are coordinated across different subscriptions 66 * and phones in multi-SIM case: 67 * 68 * 1) Grouped subscriptions will have same settings for MOBILE_DATA and DATA_ROAMING. 69 * 2) Default settings updated automatically. It may be cleared or inherited within group. 70 * If default subscription A switches to profile B which is in the same group, B will 71 * become the new default. 72 * 3) For primary subscriptions, only default data subscription will have MOBILE_DATA on. 73 */ 74 public class MultiSimSettingController extends Handler { 75 private static final String LOG_TAG = "MultiSimSettingController"; 76 private static final boolean DBG = true; 77 private static final int EVENT_USER_DATA_ENABLED = 1; 78 private static final int EVENT_ROAMING_DATA_ENABLED = 2; 79 private static final int EVENT_ALL_SUBSCRIPTIONS_LOADED = 3; 80 private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4; 81 private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5; 82 private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6; 83 private static final int EVENT_CARRIER_CONFIG_CHANGED = 7; 84 private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8; 85 @VisibleForTesting 86 public static final int EVENT_RADIO_STATE_CHANGED = 9; 87 88 @Retention(RetentionPolicy.SOURCE) 89 @IntDef(prefix = {"PRIMARY_SUB_"}, 90 value = { 91 PRIMARY_SUB_NO_CHANGE, 92 PRIMARY_SUB_ADDED, 93 PRIMARY_SUB_REMOVED, 94 PRIMARY_SUB_SWAPPED, 95 PRIMARY_SUB_SWAPPED_IN_GROUP, 96 PRIMARY_SUB_MARKED_OPPT, 97 PRIMARY_SUB_INITIALIZED, 98 PRIMARY_SUB_REMOVED_IN_GROUP 99 }) 100 private @interface PrimarySubChangeType {} 101 102 // Primary subscription not change. 103 private static final int PRIMARY_SUB_NO_CHANGE = 0; 104 // One or more primary subscriptions are activated. 105 private static final int PRIMARY_SUB_ADDED = 1; 106 // One or more primary subscriptions are deactivated. 107 private static final int PRIMARY_SUB_REMOVED = 2; 108 // One or more primary subscriptions are swapped. 109 private static final int PRIMARY_SUB_SWAPPED = 3; 110 // One or more primary subscriptions are swapped but within same sub group. 111 private static final int PRIMARY_SUB_SWAPPED_IN_GROUP = 4; 112 // One or more primary subscriptions are marked as opportunistic. 113 private static final int PRIMARY_SUB_MARKED_OPPT = 5; 114 // Subscription information is initially loaded. 115 private static final int PRIMARY_SUB_INITIALIZED = 6; 116 // One or more primary subscriptions are deactivated but within the same group as another active 117 // sub. 118 private static final int PRIMARY_SUB_REMOVED_IN_GROUP = 7; 119 120 protected final Context mContext; 121 protected final SubscriptionController mSubController; 122 // Keep a record of active primary (non-opportunistic) subscription list. 123 @NonNull private List<Integer> mPrimarySubList = new ArrayList<>(); 124 125 /** The singleton instance. */ 126 protected static MultiSimSettingController sInstance = null; 127 128 // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED. The reason of keeping 129 // a local variable instead of calling SubscriptionInfoUpdater#isSubInfoInitialized is, there 130 // might be a race condition that we receive EVENT_SUBSCRIPTION_INFO_CHANGED first, then 131 // EVENT_ALL_SUBSCRIPTIONS_LOADED. And calling SubscriptionInfoUpdater#isSubInfoInitialized 132 // will make us handle EVENT_SUBSCRIPTION_INFO_CHANGED unexpectedly and causing us to believe 133 // the SIMs are newly inserted instead of being initialized. 134 private boolean mSubInfoInitialized = false; 135 136 // mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot. 137 // After boot-up when things are firstly initialized (mSubInfoInitialized is changed to true 138 // and carrier configs are all loaded), we do a reEvaluateAll(). In the first reEvaluateAll(), 139 // mInitialHandling will be true and we won't pop up SIM select dialog. 140 private boolean mInitialHandling = true; 141 142 // Keep a record of which subIds have carrier config loaded. Length of the array is phone count. 143 // The index is phoneId, and value is subId. For example: 144 // If carrier config of subId 2 is loaded on phone 0,mCarrierConfigLoadedSubIds[0] = 2. 145 // Then if subId 2 is deactivated from phone 0, the value becomes INVALID, 146 // mCarrierConfigLoadedSubIds[0] = INVALID_SUBSCRIPTION_ID. 147 private int[] mCarrierConfigLoadedSubIds; 148 149 // It indicates whether "Ask every time" option for default SMS subscription is supported by the 150 // device. 151 private final boolean mIsAskEverytimeSupportedForSms; 152 153 private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub"; 154 155 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 156 @Override 157 public void onReceive(Context context, Intent intent) { 158 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { 159 int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 160 SubscriptionManager.INVALID_SIM_SLOT_INDEX); 161 int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 162 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 163 notifyCarrierConfigChanged(phoneId, subId); 164 } 165 } 166 }; 167 168 /** 169 * Return the singleton or create one if not existed. 170 */ getInstance()171 public static MultiSimSettingController getInstance() { 172 synchronized (MultiSimSettingController.class) { 173 if (sInstance == null) { 174 Log.wtf(LOG_TAG, "getInstance null"); 175 } 176 177 return sInstance; 178 } 179 } 180 181 /** 182 * Init instance of MultiSimSettingController. 183 */ init(Context context, SubscriptionController sc)184 public static MultiSimSettingController init(Context context, SubscriptionController sc) { 185 synchronized (MultiSimSettingController.class) { 186 if (sInstance == null) { 187 sInstance = new MultiSimSettingController(context, sc); 188 } else { 189 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 190 } 191 return sInstance; 192 } 193 } 194 195 @VisibleForTesting MultiSimSettingController(Context context, SubscriptionController sc)196 public MultiSimSettingController(Context context, SubscriptionController sc) { 197 mContext = context; 198 mSubController = sc; 199 200 // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change. 201 final int phoneCount = ((TelephonyManager) mContext.getSystemService( 202 Context.TELEPHONY_SERVICE)).getSupportedModemCount(); 203 mCarrierConfigLoadedSubIds = new int[phoneCount]; 204 Arrays.fill(mCarrierConfigLoadedSubIds, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 205 206 PhoneConfigurationManager.registerForMultiSimConfigChange( 207 this, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 208 209 mIsAskEverytimeSupportedForSms = mContext.getResources() 210 .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support); 211 context.registerReceiver(mIntentReceiver, new IntentFilter( 212 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 213 } 214 215 /** 216 * Notify MOBILE_DATA of a subscription is changed. 217 */ notifyUserDataEnabled(int subId, boolean enable)218 public void notifyUserDataEnabled(int subId, boolean enable) { 219 if (SubscriptionManager.isValidSubscriptionId(subId)) { 220 obtainMessage(EVENT_USER_DATA_ENABLED, subId, enable ? 1 : 0).sendToTarget(); 221 } 222 } 223 224 /** 225 * Notify DATA_ROAMING of a subscription is changed. 226 */ notifyRoamingDataEnabled(int subId, boolean enable)227 public void notifyRoamingDataEnabled(int subId, boolean enable) { 228 if (SubscriptionManager.isValidSubscriptionId(subId)) { 229 obtainMessage(EVENT_ROAMING_DATA_ENABLED, subId, enable ? 1 : 0).sendToTarget(); 230 } 231 } 232 233 /** 234 * Notify that, for the first time after boot, SIMs are initialized. 235 * Should only be triggered once. 236 */ notifyAllSubscriptionLoaded()237 public void notifyAllSubscriptionLoaded() { 238 obtainMessage(EVENT_ALL_SUBSCRIPTIONS_LOADED).sendToTarget(); 239 } 240 241 /** 242 * Notify subscription info change. 243 */ notifySubscriptionInfoChanged()244 public void notifySubscriptionInfoChanged() { 245 log("notifySubscriptionInfoChanged"); 246 obtainMessage(EVENT_SUBSCRIPTION_INFO_CHANGED).sendToTarget(); 247 } 248 249 /** 250 * Notify subscription group information change. 251 */ notifySubscriptionGroupChanged(ParcelUuid groupUuid)252 public void notifySubscriptionGroupChanged(ParcelUuid groupUuid) { 253 obtainMessage(EVENT_SUBSCRIPTION_GROUP_CHANGED, groupUuid).sendToTarget(); 254 } 255 256 /** 257 * Notify default data subscription change. 258 */ notifyDefaultDataSubChanged()259 public void notifyDefaultDataSubChanged() { 260 obtainMessage(EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED).sendToTarget(); 261 } 262 263 @Override handleMessage(Message msg)264 public void handleMessage(Message msg) { 265 switch (msg.what) { 266 case EVENT_USER_DATA_ENABLED: { 267 int subId = msg.arg1; 268 boolean enable = msg.arg2 != 0; 269 onUserDataEnabled(subId, enable); 270 break; 271 } 272 case EVENT_ROAMING_DATA_ENABLED: { 273 int subId = msg.arg1; 274 boolean enable = msg.arg2 != 0; 275 onRoamingDataEnabled(subId, enable); 276 break; 277 } 278 case EVENT_ALL_SUBSCRIPTIONS_LOADED: 279 onAllSubscriptionsLoaded(); 280 break; 281 case EVENT_SUBSCRIPTION_INFO_CHANGED: 282 onSubscriptionsChanged(); 283 break; 284 case EVENT_SUBSCRIPTION_GROUP_CHANGED: 285 ParcelUuid groupUuid = (ParcelUuid) msg.obj; 286 onSubscriptionGroupChanged(groupUuid); 287 break; 288 case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED: 289 onDefaultDataSettingChanged(); 290 break; 291 case EVENT_CARRIER_CONFIG_CHANGED: 292 int phoneId = msg.arg1; 293 int subId = msg.arg2; 294 onCarrierConfigChanged(phoneId, subId); 295 break; 296 case EVENT_MULTI_SIM_CONFIG_CHANGED: 297 int activeModems = (int) ((AsyncResult) msg.obj).result; 298 onMultiSimConfigChanged(activeModems); 299 break; 300 case EVENT_RADIO_STATE_CHANGED: 301 for (Phone phone : PhoneFactory.getPhones()) { 302 if (phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) { 303 if (DBG) log("Radio unavailable. Clearing sub info initialized flag."); 304 mSubInfoInitialized = false; 305 break; 306 } 307 } 308 break; 309 } 310 } 311 312 /** 313 * Make sure MOBILE_DATA of subscriptions in same group are synced. 314 * 315 * If user is enabling a non-default non-opportunistic subscription, make it default 316 * data subscription. 317 */ onUserDataEnabled(int subId, boolean enable)318 protected void onUserDataEnabled(int subId, boolean enable) { 319 if (DBG) log("onUserDataEnabled"); 320 // Make sure MOBILE_DATA of subscriptions in same group are synced. 321 setUserDataEnabledForGroup(subId, enable); 322 323 // If user is enabling a non-default non-opportunistic subscription, make it default. 324 if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId) 325 && enable && mSubController.isActiveSubId(subId)) { 326 android.provider.Settings.Global.putInt(mContext.getContentResolver(), 327 SETTING_USER_PREF_DATA_SUB, subId); 328 mSubController.setDefaultDataSubId(subId); 329 } 330 } 331 332 /** 333 * Make sure DATA_ROAMING of subscriptions in same group are synced. 334 */ onRoamingDataEnabled(int subId, boolean enable)335 private void onRoamingDataEnabled(int subId, boolean enable) { 336 if (DBG) log("onRoamingDataEnabled"); 337 setRoamingDataEnabledForGroup(subId, enable); 338 339 // Also inform SubscriptionController as it keeps another copy of user setting. 340 mSubController.setDataRoaming(enable ? 1 : 0, subId); 341 } 342 343 /** 344 * Upon initialization or radio available, update defaults and mobile data enabling. 345 * Should only be triggered once. 346 */ onAllSubscriptionsLoaded()347 private void onAllSubscriptionsLoaded() { 348 if (DBG) log("onAllSubscriptionsLoaded: mSubInfoInitialized=" + mSubInfoInitialized); 349 if (!mSubInfoInitialized) { 350 mSubInfoInitialized = true; 351 for (Phone phone : PhoneFactory.getPhones()) { 352 phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); 353 } 354 reEvaluateAll(); 355 } 356 } 357 358 /** 359 * Make sure default values are cleaned or updated. 360 * 361 * Make sure non-default non-opportunistic subscriptions has data off. 362 */ onSubscriptionsChanged()363 private void onSubscriptionsChanged() { 364 if (DBG) log("onSubscriptionsChanged"); 365 reEvaluateAll(); 366 } 367 368 /** 369 * This method is called when a phone object is removed (for example when going from multi-sim 370 * to single-sim). 371 * NOTE: This method does not post a message to self, instead it calls reEvaluateAll() directly. 372 * so it should only be called from the main thread. The reason is to update defaults asap 373 * after multi_sim_config property has been updated (see b/163582235). 374 */ onPhoneRemoved()375 public void onPhoneRemoved() { 376 if (DBG) log("onPhoneRemoved"); 377 if (Looper.myLooper() != this.getLooper()) { 378 throw new RuntimeException("This method must be called from the same looper as " 379 + "MultiSimSettingController."); 380 } 381 reEvaluateAll(); 382 } 383 384 /** 385 * Called when carrier config changes on any phone. 386 */ 387 @VisibleForTesting notifyCarrierConfigChanged(int phoneId, int subId)388 public void notifyCarrierConfigChanged(int phoneId, int subId) { 389 obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, phoneId, subId).sendToTarget(); 390 } 391 onCarrierConfigChanged(int phoneId, int subId)392 private void onCarrierConfigChanged(int phoneId, int subId) { 393 log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId); 394 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 395 loge("Carrier config change with invalid phoneId " + phoneId); 396 return; 397 } 398 399 // b/153860050 Occasionally we receive carrier config change broadcast without subId 400 // being specified in it. So here we do additional check to make sur we don't miss the 401 // subId. 402 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 403 int[] subIds = mSubController.getSubId(phoneId); 404 if (!ArrayUtils.isEmpty(subIds)) { 405 CarrierConfigManager cm = (CarrierConfigManager) mContext.getSystemService( 406 mContext.CARRIER_CONFIG_SERVICE); 407 if (cm != null && cm.getConfigForSubId(subIds[0]) != null) { 408 loge("onCarrierConfigChanged with invalid subId while subd " 409 + subIds[0] + " is active and its config is loaded"); 410 subId = subIds[0]; 411 } 412 } 413 } 414 415 mCarrierConfigLoadedSubIds[phoneId] = subId; 416 reEvaluateAll(); 417 } 418 isCarrierConfigLoadedForAllSub()419 private boolean isCarrierConfigLoadedForAllSub() { 420 int[] activeSubIds = mSubController.getActiveSubIdList(false); 421 for (int activeSubId : activeSubIds) { 422 boolean isLoaded = false; 423 for (int configLoadedSub : mCarrierConfigLoadedSubIds) { 424 if (configLoadedSub == activeSubId) { 425 isLoaded = true; 426 break; 427 } 428 } 429 if (!isLoaded) { 430 if (DBG) log("Carrier config subId " + activeSubId + " is not loaded."); 431 return false; 432 } 433 } 434 435 return true; 436 } 437 onMultiSimConfigChanged(int activeModems)438 private void onMultiSimConfigChanged(int activeModems) { 439 // Clear mCarrierConfigLoadedSubIds. Other actions will responds to active 440 // subscription change. 441 for (int phoneId = activeModems; phoneId < mCarrierConfigLoadedSubIds.length; phoneId++) { 442 mCarrierConfigLoadedSubIds[phoneId] = INVALID_SUBSCRIPTION_ID; 443 } 444 for (Phone phone : PhoneFactory.getPhones()) { 445 phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); 446 } 447 } 448 449 /** 450 * Wait for subInfo initialization (after boot up or radio unavailable) and carrier config load 451 * for all active subscriptions before re-evaluate multi SIM settings. 452 */ isReadyToReevaluate()453 private boolean isReadyToReevaluate() { 454 boolean carrierConfigsLoaded = isCarrierConfigLoadedForAllSub(); 455 if (DBG) { 456 log("isReadyToReevaluate: subInfoInitialized=" + mSubInfoInitialized 457 + ", carrierConfigsLoaded=" + carrierConfigsLoaded); 458 } 459 return mSubInfoInitialized && carrierConfigsLoaded; 460 } 461 reEvaluateAll()462 private void reEvaluateAll() { 463 if (!isReadyToReevaluate()) return; 464 updateDefaults(); 465 disableDataForNonDefaultNonOpportunisticSubscriptions(); 466 deactivateGroupedOpportunisticSubscriptionIfNeeded(); 467 } 468 469 /** 470 * Make sure non-default non-opportunistic subscriptions has data disabled. 471 */ onDefaultDataSettingChanged()472 private void onDefaultDataSettingChanged() { 473 if (DBG) log("onDefaultDataSettingChanged"); 474 disableDataForNonDefaultNonOpportunisticSubscriptions(); 475 } 476 477 /** 478 * When a subscription group is created or new subscriptions are added in the group, make 479 * sure the settings among them are synced. 480 * TODO: b/130258159 have a separate database table for grouped subscriptions so we don't 481 * manually sync each setting. 482 */ onSubscriptionGroupChanged(ParcelUuid groupUuid)483 private void onSubscriptionGroupChanged(ParcelUuid groupUuid) { 484 if (DBG) log("onSubscriptionGroupChanged"); 485 486 List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup( 487 groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag()); 488 if (infoList == null || infoList.isEmpty()) return; 489 490 // Get a reference subscription to copy settings from. 491 // TODO: the reference sub should be passed in from external caller. 492 int refSubId = infoList.get(0).getSubscriptionId(); 493 for (SubscriptionInfo info : infoList) { 494 int subId = info.getSubscriptionId(); 495 if (mSubController.isActiveSubId(subId) && !mSubController.isOpportunistic(subId)) { 496 refSubId = subId; 497 break; 498 } 499 } 500 if (DBG) log("refSubId is " + refSubId); 501 502 boolean enable = false; 503 try { 504 enable = GlobalSettingsHelper.getBoolean( 505 mContext, Settings.Global.MOBILE_DATA, refSubId); 506 onUserDataEnabled(refSubId, enable); 507 } catch (SettingNotFoundException exception) { 508 //pass invalid refSubId to fetch the single-sim setting 509 enable = GlobalSettingsHelper.getBoolean( 510 mContext, Settings.Global.MOBILE_DATA, INVALID_SUBSCRIPTION_ID, enable); 511 onUserDataEnabled(refSubId, enable); 512 } 513 514 enable = false; 515 try { 516 enable = GlobalSettingsHelper.getBoolean( 517 mContext, Settings.Global.DATA_ROAMING, refSubId); 518 onRoamingDataEnabled(refSubId, enable); 519 } catch (SettingNotFoundException exception) { 520 //pass invalid refSubId to fetch the single-sim setting 521 enable = GlobalSettingsHelper.getBoolean( 522 mContext, Settings.Global.DATA_ROAMING, INVALID_SUBSCRIPTION_ID, enable); 523 onRoamingDataEnabled(refSubId, enable); 524 } 525 526 // Sync settings in subscription database.. 527 mSubController.syncGroupedSetting(refSubId); 528 } 529 530 /** 531 * Automatically update default settings (data / voice / sms). 532 * 533 * Opportunistic subscriptions can't be default data / voice / sms subscription. 534 * 535 * 1) If the default subscription is still active, keep it unchanged. 536 * 2) Or if there's another active primary subscription that's in the same group, 537 * make it the new default value. 538 * 3) Or if there's only one active primary subscription, automatically set default 539 * data subscription on it. Because default data in Android Q is an internal value, 540 * not a user settable value anymore. 541 * 4) If non above is met, clear the default value to INVALID. 542 * 543 */ updateDefaults()544 protected void updateDefaults() { 545 if (DBG) log("updateDefaults"); 546 547 if (!isReadyToReevaluate()) return; 548 549 List<SubscriptionInfo> activeSubInfos = mSubController 550 .getActiveSubscriptionInfoList(mContext.getOpPackageName(), 551 mContext.getAttributionTag()); 552 553 if (ArrayUtils.isEmpty(activeSubInfos)) { 554 mPrimarySubList.clear(); 555 if (DBG) log("[updateDefaultValues] No active sub. Setting default to INVALID sub."); 556 mSubController.setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 557 mSubController.setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 558 mSubController.setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 559 return; 560 } 561 562 int change = updatePrimarySubListAndGetChangeType(activeSubInfos); 563 if (DBG) log("[updateDefaultValues] change: " + change); 564 if (change == PRIMARY_SUB_NO_CHANGE) return; 565 566 // If there's only one primary subscription active, we trigger PREFERRED_PICK_DIALOG 567 // dialog if and only if there were multiple primary SIM cards and one is removed. 568 // Otherwise, if user just inserted their first SIM, or there's one primary and one 569 // opportunistic subscription active (activeSubInfos.size() > 1), we automatically 570 // set the primary to be default SIM and return. 571 if (mPrimarySubList.size() == 1 && (change != PRIMARY_SUB_REMOVED 572 || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) 573 .getActiveModemCount() == 1)) { 574 int subId = mPrimarySubList.get(0); 575 if (DBG) log("[updateDefaultValues] to only primary sub " + subId); 576 mSubController.setDefaultDataSubId(subId); 577 mSubController.setDefaultVoiceSubId(subId); 578 mSubController.setDefaultSmsSubId(subId); 579 sendDefaultSubConfirmedNotification(subId); 580 return; 581 } 582 583 if (DBG) log("[updateDefaultValues] records: " + mPrimarySubList); 584 585 // Update default data subscription. 586 if (DBG) log("[updateDefaultValues] Update default data subscription"); 587 boolean dataSelected = updateDefaultValue(mPrimarySubList, 588 mSubController.getDefaultDataSubId(), 589 (newValue -> mSubController.setDefaultDataSubId(newValue))); 590 591 // Update default voice subscription. 592 if (DBG) log("[updateDefaultValues] Update default voice subscription"); 593 boolean voiceSelected = updateDefaultValue(mPrimarySubList, 594 mSubController.getDefaultVoiceSubId(), 595 (newValue -> mSubController.setDefaultVoiceSubId(newValue))); 596 597 // Update default sms subscription. 598 if (DBG) log("[updateDefaultValues] Update default sms subscription"); 599 boolean smsSelected = updateDefaultValue(mPrimarySubList, 600 mSubController.getDefaultSmsSubId(), 601 (newValue -> mSubController.setDefaultSmsSubId(newValue)), 602 mIsAskEverytimeSupportedForSms); 603 604 boolean autoFallbackEnabled = mContext.getResources().getBoolean( 605 com.android.internal.R.bool.config_voice_data_sms_auto_fallback); 606 607 // Based on config config_voice_data_sms_auto_fallback value choose voice/data/sms 608 // preference auto selection logic or display notification for end used to 609 // select voice/data/SMS preferences. 610 if (!autoFallbackEnabled) { 611 sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected); 612 } else { 613 updateUserPreferences(mPrimarySubList, dataSelected, voiceSelected, smsSelected); 614 } 615 } 616 617 @PrimarySubChangeType updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList)618 private int updatePrimarySubListAndGetChangeType(List<SubscriptionInfo> activeSubList) { 619 // Update mPrimarySubList. Opportunistic subscriptions can't be default 620 // data / voice / sms subscription. 621 List<Integer> prevPrimarySubList = mPrimarySubList; 622 mPrimarySubList = activeSubList.stream().filter(info -> !info.isOpportunistic()) 623 .map(info -> info.getSubscriptionId()) 624 .collect(Collectors.toList()); 625 626 if (mInitialHandling) { 627 mInitialHandling = false; 628 return PRIMARY_SUB_INITIALIZED; 629 } 630 if (mPrimarySubList.equals(prevPrimarySubList)) return PRIMARY_SUB_NO_CHANGE; 631 if (mPrimarySubList.size() > prevPrimarySubList.size()) return PRIMARY_SUB_ADDED; 632 633 if (mPrimarySubList.size() == prevPrimarySubList.size()) { 634 // We need to differentiate PRIMARY_SUB_SWAPPED and PRIMARY_SUB_SWAPPED_IN_GROUP: 635 // For SWAPPED_IN_GROUP, we never pop up dialog to ask data sub selection again. 636 for (int subId : mPrimarySubList) { 637 boolean swappedInSameGroup = false; 638 for (int prevSubId : prevPrimarySubList) { 639 if (areSubscriptionsInSameGroup(subId, prevSubId)) { 640 swappedInSameGroup = true; 641 break; 642 } 643 } 644 if (!swappedInSameGroup) return PRIMARY_SUB_SWAPPED; 645 } 646 return PRIMARY_SUB_SWAPPED_IN_GROUP; 647 } else /* mPrimarySubList.size() < prevPrimarySubList.size() */ { 648 // We need to differentiate whether the missing subscription is removed or marked as 649 // opportunistic. Usually only one subscription may change at a time, But to be safe, if 650 // any previous primary subscription becomes inactive, we consider it 651 for (int subId : prevPrimarySubList) { 652 if (mPrimarySubList.contains(subId)) continue; 653 if (!mSubController.isActiveSubId(subId)) { 654 for (int currentSubId : mPrimarySubList) { 655 if (areSubscriptionsInSameGroup(currentSubId, subId)) { 656 return PRIMARY_SUB_REMOVED_IN_GROUP; 657 } 658 } 659 return PRIMARY_SUB_REMOVED; 660 } 661 if (!mSubController.isOpportunistic(subId)) { 662 // Should never happen. 663 loge("[updatePrimarySubListAndGetChangeType]: missing active primary subId " 664 + subId); 665 } 666 } 667 return PRIMARY_SUB_MARKED_OPPT; 668 } 669 } 670 sendDefaultSubConfirmedNotification(int defaultSubId)671 private void sendDefaultSubConfirmedNotification(int defaultSubId) { 672 Intent intent = new Intent(); 673 intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED); 674 intent.setClassName("com.android.settings", 675 "com.android.settings.sim.SimSelectNotification"); 676 677 intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, 678 EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS); 679 intent.putExtra(EXTRA_SUBSCRIPTION_ID, defaultSubId); 680 681 mContext.sendBroadcast(intent); 682 } 683 sendSubChangeNotificationIfNeeded(int change, boolean dataSelected, boolean voiceSelected, boolean smsSelected)684 private void sendSubChangeNotificationIfNeeded(int change, boolean dataSelected, 685 boolean voiceSelected, boolean smsSelected) { 686 @TelephonyManager.DefaultSubscriptionSelectType 687 int simSelectDialogType = getSimSelectDialogType( 688 change, dataSelected, voiceSelected, smsSelected); 689 SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change); 690 691 if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE 692 || simCombinationParams.mWarningType != EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE) { 693 log("[sendSubChangeNotificationIfNeeded] showing dialog type " 694 + simSelectDialogType); 695 log("[sendSubChangeNotificationIfNeeded] showing sim warning " 696 + simCombinationParams.mWarningType); 697 Intent intent = new Intent(); 698 intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED); 699 intent.setClassName("com.android.settings", 700 "com.android.settings.sim.SimSelectNotification"); 701 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 702 703 intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, simSelectDialogType); 704 if (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL) { 705 intent.putExtra(EXTRA_SUBSCRIPTION_ID, mPrimarySubList.get(0)); 706 } 707 708 intent.putExtra(EXTRA_SIM_COMBINATION_WARNING_TYPE, simCombinationParams.mWarningType); 709 if (simCombinationParams.mWarningType == EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA) { 710 intent.putExtra(EXTRA_SIM_COMBINATION_NAMES, simCombinationParams.mSimNames); 711 } 712 mContext.sendBroadcast(intent); 713 } 714 } 715 getSimSelectDialogType(int change, boolean dataSelected, boolean voiceSelected, boolean smsSelected)716 private int getSimSelectDialogType(int change, boolean dataSelected, 717 boolean voiceSelected, boolean smsSelected) { 718 int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE; 719 720 // If a primary subscription is removed and only one is left active, ask user 721 // for preferred sub selection if any default setting is not set. 722 // If another primary subscription is added or default data is not selected, ask 723 // user to select default for data as it's most important. 724 if (mPrimarySubList.size() == 1 && change == PRIMARY_SUB_REMOVED 725 && (!dataSelected || !smsSelected || !voiceSelected)) { 726 dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL; 727 } else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change) 728 || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) { 729 // If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again. 730 dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA; 731 } 732 733 return dialogType; 734 } 735 736 private class SimCombinationWarningParams { 737 @TelephonyManager.SimCombinationWarningType 738 int mWarningType = EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE; 739 String mSimNames; 740 } 741 getSimCombinationWarningParams(int change)742 private SimCombinationWarningParams getSimCombinationWarningParams(int change) { 743 SimCombinationWarningParams params = new SimCombinationWarningParams(); 744 // If it's single SIM active, no SIM combination warning is needed. 745 if (mPrimarySubList.size() <= 1) return params; 746 // If it's no primary SIM change or it's not user visible change 747 // (initialized or swapped in a group), no SIM combination warning is needed. 748 if (!isUserVisibleChange(change)) return params; 749 750 List<String> simNames = new ArrayList<>(); 751 int cdmaPhoneCount = 0; 752 for (int subId : mPrimarySubList) { 753 Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 754 // If a dual CDMA SIM combination warning is needed. 755 if (phone != null && phone.isCdmaSubscriptionAppPresent()) { 756 cdmaPhoneCount++; 757 String simName = mSubController.getActiveSubscriptionInfo( 758 subId, mContext.getOpPackageName(), mContext.getAttributionTag()) 759 .getDisplayName().toString(); 760 if (TextUtils.isEmpty(simName)) { 761 // Fall back to carrier name. 762 simName = phone.getCarrierName(); 763 } 764 simNames.add(simName); 765 } 766 } 767 768 if (cdmaPhoneCount > 1) { 769 params.mWarningType = EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA; 770 params.mSimNames = String.join(" & ", simNames); 771 } 772 773 return params; 774 } 775 isUserVisibleChange(int change)776 private boolean isUserVisibleChange(int change) { 777 return (change == PRIMARY_SUB_ADDED || change == PRIMARY_SUB_REMOVED 778 || change == PRIMARY_SUB_SWAPPED); 779 } 780 disableDataForNonDefaultNonOpportunisticSubscriptions()781 protected void disableDataForNonDefaultNonOpportunisticSubscriptions() { 782 if (!isReadyToReevaluate()) return; 783 784 int defaultDataSub = mSubController.getDefaultDataSubId(); 785 786 for (Phone phone : PhoneFactory.getPhones()) { 787 if (phone.getSubId() != defaultDataSub 788 && SubscriptionManager.isValidSubscriptionId(phone.getSubId()) 789 && !mSubController.isOpportunistic(phone.getSubId()) 790 && phone.isUserDataEnabled() 791 && !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) { 792 log("setting data to false on " + phone.getSubId()); 793 phone.getDataEnabledSettings().setDataEnabled( 794 TelephonyManager.DATA_ENABLED_REASON_USER, false); 795 } 796 } 797 } 798 areSubscriptionsInSameGroup(int subId1, int subId2)799 private boolean areSubscriptionsInSameGroup(int subId1, int subId2) { 800 if (!SubscriptionManager.isUsableSubscriptionId(subId1) 801 || !SubscriptionManager.isUsableSubscriptionId(subId2)) return false; 802 if (subId1 == subId2) return true; 803 804 ParcelUuid groupUuid1 = mSubController.getGroupUuid(subId1); 805 ParcelUuid groupUuid2 = mSubController.getGroupUuid(subId2); 806 return groupUuid1 != null && groupUuid1.equals(groupUuid2); 807 } 808 809 /** 810 * Make sure MOBILE_DATA of subscriptions in the same group with the subId 811 * are synced. 812 */ setUserDataEnabledForGroup(int subId, boolean enable)813 protected void setUserDataEnabledForGroup(int subId, boolean enable) { 814 log("setUserDataEnabledForGroup subId " + subId + " enable " + enable); 815 List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup( 816 mSubController.getGroupUuid(subId), mContext.getOpPackageName(), 817 mContext.getAttributionTag()); 818 819 if (infoList == null) return; 820 821 for (SubscriptionInfo info : infoList) { 822 int currentSubId = info.getSubscriptionId(); 823 // TODO: simplify when setUserDataEnabled becomes singleton 824 if (mSubController.isActiveSubId(currentSubId)) { 825 // For active subscription, call setUserDataEnabled through DataEnabledSettings. 826 Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId)); 827 // If enable is true and it's not opportunistic subscription, we don't enable it, 828 // as there can't e two 829 if (phone != null) { 830 phone.getDataEnabledSettings().setUserDataEnabled(enable, false); 831 } 832 } else { 833 // For inactive subscription, directly write into global settings. 834 GlobalSettingsHelper.setBoolean( 835 mContext, Settings.Global.MOBILE_DATA, currentSubId, enable); 836 } 837 } 838 } 839 840 /** 841 * Make sure DATA_ROAMING of subscriptions in the same group with the subId 842 * are synced. 843 */ setRoamingDataEnabledForGroup(int subId, boolean enable)844 private void setRoamingDataEnabledForGroup(int subId, boolean enable) { 845 SubscriptionController subController = SubscriptionController.getInstance(); 846 List<SubscriptionInfo> infoList = subController.getSubscriptionsInGroup( 847 mSubController.getGroupUuid(subId), mContext.getOpPackageName(), 848 mContext.getAttributionTag()); 849 850 if (infoList == null) return; 851 852 for (SubscriptionInfo info : infoList) { 853 // For inactive subscription, directly write into global settings. 854 GlobalSettingsHelper.setBoolean( 855 mContext, Settings.Global.DATA_ROAMING, info.getSubscriptionId(), enable); 856 } 857 } 858 859 private interface UpdateDefaultAction { update(int newValue)860 void update(int newValue); 861 } 862 863 // Returns whether the new default value is valid. updateDefaultValue(List<Integer> primarySubList, int oldValue, UpdateDefaultAction action)864 private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue, 865 UpdateDefaultAction action) { 866 return updateDefaultValue(primarySubList, oldValue, action, true); 867 } 868 updateDefaultValue(List<Integer> primarySubList, int oldValue, UpdateDefaultAction action, boolean allowInvalidSubId)869 private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue, 870 UpdateDefaultAction action, boolean allowInvalidSubId) { 871 int newValue = INVALID_SUBSCRIPTION_ID; 872 873 if (primarySubList.size() > 0) { 874 for (int subId : primarySubList) { 875 if (DBG) log("[updateDefaultValue] Record.id: " + subId); 876 // 1) If the old subId is still active, or there's another active primary 877 // subscription that is in the same group, that should become the new default 878 // subscription. 879 // 2) If the old subId is INVALID_SUBSCRIPTION_ID and allowInvalidSubId is false, 880 // first active subscription is used for new default always. 881 if (areSubscriptionsInSameGroup(subId, oldValue) 882 || (!allowInvalidSubId && oldValue == INVALID_SUBSCRIPTION_ID)) { 883 newValue = subId; 884 log("[updateDefaultValue] updates to subId=" + newValue); 885 break; 886 } 887 } 888 } 889 890 if (oldValue != newValue) { 891 if (DBG) log("[updateDefaultValue: subId] from " + oldValue + " to " + newValue); 892 action.update(newValue); 893 } 894 895 return SubscriptionManager.isValidSubscriptionId(newValue); 896 } 897 898 // When a primary and its grouped opportunistic subscriptions were active, and the primary 899 // subscription gets deactivated or removed, we need to automatically disable the grouped 900 // opportunistic subscription, which will be marked isGroupDisabled as true by SubController. deactivateGroupedOpportunisticSubscriptionIfNeeded()901 private void deactivateGroupedOpportunisticSubscriptionIfNeeded() { 902 if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return; 903 904 List<SubscriptionInfo> opptSubList = mSubController.getOpportunisticSubscriptions( 905 mContext.getOpPackageName(), mContext.getAttributionTag()); 906 907 if (ArrayUtils.isEmpty(opptSubList)) return; 908 909 for (SubscriptionInfo info : opptSubList) { 910 if (info.isGroupDisabled() && mSubController.isActiveSubId(info.getSubscriptionId())) { 911 log("[deactivateGroupedOpptSubIfNeeded] " 912 + "Deactivating grouped opportunistic subscription " 913 + info.getSubscriptionId()); 914 deactivateSubscription(info); 915 } 916 } 917 } 918 deactivateSubscription(SubscriptionInfo info)919 private void deactivateSubscription(SubscriptionInfo info) { 920 // TODO: b/133379187 have a way to deactivate pSIM. 921 if (info.isEmbedded()) { 922 log("[deactivateSubscription] eSIM profile " + info.getSubscriptionId()); 923 EuiccManager euiccManager = (EuiccManager) 924 mContext.getSystemService(Context.EUICC_SERVICE); 925 euiccManager.switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 926 PendingIntent.getService( 927 mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)); 928 } 929 } 930 931 // Voice/Data/SMS preferences would be auto selected without any user 932 // confirmation in following scenarios, 933 // 1. When device powered-up with only one SIM Inserted or while two SIM cards 934 // present if one SIM is removed(or turned OFF) the reaiming SIM would be 935 // selected as preferred voice/data/sms SIM. 936 // 2. When device powered-up with two SIM cards or if two SIM cards 937 // present on device with new SIM insert(or SIM turn ON) the first inserted SIM 938 // would be selected as preferred voice/data/sms SIM. updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, boolean voiceSelected, boolean smsSelected)939 private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected, 940 boolean voiceSelected, boolean smsSelected) { 941 // In Single SIM case or if there are no activated subs available, no need to update. EXIT. 942 if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return; 943 944 if (!isRadioAvailableOnAllSubs()) { 945 log("Radio is in Invalid state, Ignore Updating User Preference!!!"); 946 return; 947 } 948 final int defaultDataSubId = mSubController.getDefaultDataSubId(); 949 950 if (DBG) log("updateUserPreferences: dds = " + defaultDataSubId + " voice = " 951 + mSubController.getDefaultVoiceSubId() + 952 " sms = " + mSubController.getDefaultSmsSubId()); 953 954 int autoDefaultSubId = primarySubList.get(0); 955 956 if ((primarySubList.size() == 1) && !smsSelected) { 957 mSubController.setDefaultSmsSubId(autoDefaultSubId); 958 } 959 960 if ((primarySubList.size() == 1) && !voiceSelected) { 961 mSubController.setDefaultVoiceSubId(autoDefaultSubId); 962 } 963 964 int userPrefDataSubId = getUserPrefDataSubIdFromDB(); 965 966 if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId 967 + " next active subId " + autoDefaultSubId); 968 969 // If earlier user selected DDS is now available, set that as DDS subId. 970 if (primarySubList.contains(userPrefDataSubId) && 971 SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) && 972 (defaultDataSubId != userPrefDataSubId)) { 973 mSubController.setDefaultDataSubId(userPrefDataSubId); 974 } else if (!dataSelected) { 975 mSubController.setDefaultDataSubId(autoDefaultSubId); 976 } 977 978 979 if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() + 980 " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " + 981 mSubController.getDefaultSmsSubId()); 982 } 983 getUserPrefDataSubIdFromDB()984 private int getUserPrefDataSubIdFromDB() { 985 return android.provider.Settings.Global.getInt(mContext.getContentResolver(), 986 SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 987 } 988 isRadioAvailableOnAllSubs()989 private boolean isRadioAvailableOnAllSubs() { 990 for (Phone phone : PhoneFactory.getPhones()) { 991 if ((phone.mCi != null && 992 phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) || 993 phone.isShuttingDown()) { 994 return false; 995 } 996 } 997 return true; 998 } 999 log(String msg)1000 private void log(String msg) { 1001 Log.d(LOG_TAG, msg); 1002 } 1003 loge(String msg)1004 private void loge(String msg) { 1005 Log.e(LOG_TAG, msg); 1006 } 1007 } 1008