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