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