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