• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2014 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.content.pm.PackageManager.PERMISSION_GRANTED;
20 import static android.telephony.TelephonyManager.MULTISIM_ALLOWED;
21 import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION;
22 import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
23 
24 import android.Manifest;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.app.AppOpsManager;
28 import android.app.PendingIntent;
29 import android.compat.annotation.UnsupportedAppUsage;
30 import android.content.ContentResolver;
31 import android.content.ContentValues;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.database.Cursor;
35 import android.graphics.Bitmap;
36 import android.graphics.BitmapFactory;
37 import android.net.Uri;
38 import android.os.Binder;
39 import android.os.Handler;
40 import android.os.ParcelUuid;
41 import android.os.PersistableBundle;
42 import android.os.RegistrantList;
43 import android.os.RemoteException;
44 import android.os.TelephonyServiceManager.ServiceRegisterer;
45 import android.os.UserHandle;
46 import android.provider.Settings;
47 import android.telecom.PhoneAccountHandle;
48 import android.telecom.TelecomManager;
49 import android.telephony.AnomalyReporter;
50 import android.telephony.CarrierConfigManager;
51 import android.telephony.RadioAccessFamily;
52 import android.telephony.SubscriptionInfo;
53 import android.telephony.SubscriptionManager;
54 import android.telephony.SubscriptionManager.SimDisplayNameSource;
55 import android.telephony.TelephonyFrameworkInitializer;
56 import android.telephony.TelephonyManager;
57 import android.telephony.TelephonyRegistryManager;
58 import android.telephony.UiccAccessRule;
59 import android.telephony.UiccSlotInfo;
60 import android.telephony.euicc.EuiccManager;
61 import android.text.TextUtils;
62 import android.util.EventLog;
63 import android.util.LocalLog;
64 import android.util.Log;
65 
66 import com.android.internal.annotations.VisibleForTesting;
67 import com.android.internal.telephony.IccCardConstants.State;
68 import com.android.internal.telephony.dataconnection.DataEnabledOverride;
69 import com.android.internal.telephony.metrics.TelephonyMetrics;
70 import com.android.internal.telephony.uicc.IccUtils;
71 import com.android.internal.telephony.uicc.UiccCard;
72 import com.android.internal.telephony.uicc.UiccController;
73 import com.android.internal.telephony.uicc.UiccProfile;
74 import com.android.internal.telephony.uicc.UiccSlot;
75 import com.android.internal.telephony.util.ArrayUtils;
76 import com.android.internal.telephony.util.TelephonyUtils;
77 import com.android.telephony.Rlog;
78 
79 import java.io.FileDescriptor;
80 import java.io.PrintWriter;
81 import java.util.ArrayList;
82 import java.util.Arrays;
83 import java.util.Collections;
84 import java.util.Comparator;
85 import java.util.HashSet;
86 import java.util.List;
87 import java.util.Map;
88 import java.util.Map.Entry;
89 import java.util.Objects;
90 import java.util.Set;
91 import java.util.UUID;
92 import java.util.concurrent.ConcurrentHashMap;
93 import java.util.concurrent.atomic.AtomicBoolean;
94 import java.util.stream.Collectors;
95 
96 /**
97  * Implementation of the ISub interface.
98  *
99  * Any setters which take subId, slotIndex or phoneId as a parameter will throw an exception if the
100  * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID.
101  *
102  * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling
103  * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()).
104  *
105  * Finally, any getters which perform the mapping between subscriptions, slots and phones will
106  * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters
107  * will fail and return the appropriate error value. Ie calling
108  * getSlotIndex(INVALID_SUBSCRIPTION_ID) will return INVALID_SIM_SLOT_INDEX and calling
109  * getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID) will return null.
110  *
111  */
112 public class SubscriptionController extends ISub.Stub {
113     private static final String LOG_TAG = "SubscriptionController";
114     private static final boolean DBG = true;
115     private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
116     private static final boolean DBG_CACHE = false;
117     private static final int DEPRECATED_SETTING = -1;
118     private static final ParcelUuid INVALID_GROUP_UUID =
119             ParcelUuid.fromString(CarrierConfigManager.REMOVE_GROUP_UUID_STRING);
120     private final LocalLog mLocalLog = new LocalLog(200);
121     private static final int SUB_ID_FOUND = 1;
122     private static final int NO_ENTRY_FOR_SLOT_INDEX = -1;
123     private static final int SUB_ID_NOT_IN_SLOT = -2;
124 
125     // Lock that both mCacheActiveSubInfoList and mCacheOpportunisticSubInfoList use.
126     private Object mSubInfoListLock = new Object();
127 
128     /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */
129     private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>();
130 
131     /* Similar to mCacheActiveSubInfoList but only caching opportunistic subscriptions. */
132     private List<SubscriptionInfo> mCacheOpportunisticSubInfoList = new ArrayList<>();
133     private AtomicBoolean mOpptSubInfoListChangedDirtyBit = new AtomicBoolean();
134 
135     private static final Comparator<SubscriptionInfo> SUBSCRIPTION_INFO_COMPARATOR =
136             (arg0, arg1) -> {
137                 // Primary sort key on SimSlotIndex
138                 int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
139                 if (flag == 0) {
140                     // Secondary sort on SubscriptionId
141                     return arg0.getSubscriptionId() - arg1.getSubscriptionId();
142                 }
143                 return flag;
144             };
145 
146     @UnsupportedAppUsage
147     protected final Object mLock = new Object();
148 
149     /** The singleton instance. */
150     protected static SubscriptionController sInstance = null;
151     @UnsupportedAppUsage
152     protected Context mContext;
153     protected TelephonyManager mTelephonyManager;
154     protected UiccController mUiccController;
155 
156     private AppOpsManager mAppOps;
157 
158     // Allows test mocks to avoid SELinux failures on invalidate calls.
159     private static boolean sCachingEnabled = true;
160 
161     // Each slot can have multiple subs.
162     private static class WatchedSlotIndexToSubIds {
163         private Map<Integer, ArrayList<Integer>> mSlotIndexToSubIds = new ConcurrentHashMap<>();
164 
WatchedSlotIndexToSubIds()165         WatchedSlotIndexToSubIds() {
166         }
167 
clear()168         public void clear() {
169             mSlotIndexToSubIds.clear();
170             invalidateDefaultSubIdCaches();
171             invalidateSlotIndexCaches();
172         }
173 
entrySet()174         public Set<Entry<Integer, ArrayList<Integer>>> entrySet() {
175             return mSlotIndexToSubIds.entrySet();
176         }
177 
178         // Force all updates to data structure through wrapper.
getCopy(int slotIndex)179         public ArrayList<Integer> getCopy(int slotIndex) {
180             ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
181             if (subIdList == null) {
182                 return null;
183             }
184 
185             return new ArrayList<Integer>(subIdList);
186         }
187 
put(int slotIndex, ArrayList<Integer> value)188         public void put(int slotIndex, ArrayList<Integer> value) {
189             mSlotIndexToSubIds.put(slotIndex, value);
190             invalidateDefaultSubIdCaches();
191             invalidateSlotIndexCaches();
192         }
193 
remove(int slotIndex)194         public void remove(int slotIndex) {
195             mSlotIndexToSubIds.remove(slotIndex);
196             invalidateDefaultSubIdCaches();
197             invalidateSlotIndexCaches();
198         }
199 
size()200         public int size() {
201             return mSlotIndexToSubIds.size();
202         }
203 
204         @VisibleForTesting
getMap()205         public Map<Integer, ArrayList<Integer>> getMap() {
206             return mSlotIndexToSubIds;
207         }
208 
removeFromSubIdList(int slotIndex, int subId)209         public int removeFromSubIdList(int slotIndex, int subId) {
210             ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
211             if (subIdList == null) {
212                 return NO_ENTRY_FOR_SLOT_INDEX;
213             } else {
214                 if (subIdList.contains(subId)) {
215                     subIdList.remove(new Integer(subId));
216                     if (subIdList.isEmpty()) {
217                         mSlotIndexToSubIds.remove(slotIndex);
218                     }
219                     invalidateDefaultSubIdCaches();
220                     invalidateSlotIndexCaches();
221                     return SUB_ID_FOUND;
222                 } else {
223                     return SUB_ID_NOT_IN_SLOT;
224                 }
225             }
226         }
227 
addToSubIdList(int slotIndex, Integer value)228         public void addToSubIdList(int slotIndex, Integer value) {
229             ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
230             if (subIdList == null) {
231                 subIdList = new ArrayList<Integer>();
232                 subIdList.add(value);
233                 mSlotIndexToSubIds.put(slotIndex, subIdList);
234             } else {
235                 subIdList.add(value);
236             }
237             invalidateDefaultSubIdCaches();
238             invalidateSlotIndexCaches();
239         }
240 
clearSubIdList(int slotIndex)241         public void clearSubIdList(int slotIndex) {
242             ArrayList<Integer> subIdList = mSlotIndexToSubIds.get(slotIndex);
243             if (subIdList != null) {
244                 subIdList.clear();
245                 invalidateDefaultSubIdCaches();
246                 invalidateSlotIndexCaches();
247             }
248         }
249     }
250 
251     public static class WatchedInt {
252         private int mValue;
253 
WatchedInt(int initialValue)254         public WatchedInt(int initialValue) {
255             mValue = initialValue;
256         }
257 
get()258         public int get() {
259             return mValue;
260         }
261 
set(int newValue)262         public void set(int newValue) {
263             mValue = newValue;
264         }
265     }
266 
267     private static WatchedSlotIndexToSubIds sSlotIndexToSubIds = new WatchedSlotIndexToSubIds();
268 
269     protected static WatchedInt sDefaultFallbackSubId =
270             new WatchedInt(SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
271         @Override
272         public void set(int newValue) {
273             super.set(newValue);
274             invalidateDefaultSubIdCaches();
275             invalidateSlotIndexCaches();
276         }
277     };
278 
279     @UnsupportedAppUsage
280     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
281 
282     @UnsupportedAppUsage
283     private int[] colorArr;
284     private long mLastISubServiceRegTime;
285     private RegistrantList mUiccAppsEnableChangeRegList = new RegistrantList();
286 
287     // The properties that should be shared and synced across grouped subscriptions.
288     private static final Set<String> GROUP_SHARING_PROPERTIES = new HashSet<>(Arrays.asList(
289             SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
290             SubscriptionManager.VT_IMS_ENABLED,
291             SubscriptionManager.WFC_IMS_ENABLED,
292             SubscriptionManager.WFC_IMS_MODE,
293             SubscriptionManager.WFC_IMS_ROAMING_MODE,
294             SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
295             SubscriptionManager.DATA_ROAMING,
296             SubscriptionManager.DISPLAY_NAME,
297             SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES,
298             SubscriptionManager.UICC_APPLICATIONS_ENABLED,
299             SubscriptionManager.IMS_RCS_UCE_ENABLED));
300 
init(Context c)301     public static SubscriptionController init(Context c) {
302         synchronized (SubscriptionController.class) {
303             if (sInstance == null) {
304                 sInstance = new SubscriptionController(c);
305             } else {
306                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
307             }
308             return sInstance;
309         }
310     }
311 
312     @UnsupportedAppUsage
getInstance()313     public static SubscriptionController getInstance() {
314         if (sInstance == null) {
315            Log.wtf(LOG_TAG, "getInstance null");
316         }
317 
318         return sInstance;
319     }
320 
SubscriptionController(Context c)321     protected SubscriptionController(Context c) {
322         internalInit(c);
323         migrateImsSettings();
324     }
325 
internalInit(Context c)326     protected void internalInit(Context c) {
327         mContext = c;
328         mTelephonyManager = TelephonyManager.from(mContext);
329 
330         try {
331             mUiccController = UiccController.getInstance();
332         } catch(RuntimeException ex) {
333             throw new RuntimeException(
334                     "UiccController has to be initialised before SubscriptionController init");
335         }
336 
337         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
338 
339         ServiceRegisterer subscriptionServiceRegisterer = TelephonyFrameworkInitializer
340                 .getTelephonyServiceManager()
341                 .getSubscriptionServiceRegisterer();
342         if (subscriptionServiceRegisterer.get() == null) {
343             subscriptionServiceRegisterer.register(this);
344             mLastISubServiceRegTime = System.currentTimeMillis();
345         }
346 
347         // clear SLOT_INDEX for all subs
348         clearSlotIndexForSubInfoRecords();
349 
350         // Cache Setting values
351         cacheSettingValues();
352 
353         // Initial invalidate activates caching.
354         invalidateDefaultSubIdCaches();
355         invalidateDefaultDataSubIdCaches();
356         invalidateDefaultSmsSubIdCaches();
357         invalidateActiveDataSubIdCaches();
358         invalidateSlotIndexCaches();
359 
360         if (DBG) logdl("[SubscriptionController] init by Context");
361     }
362 
363     /**
364      * Should only be triggered once.
365      */
notifySubInfoReady()366     public void notifySubInfoReady() {
367         // broadcast default subId.
368         sendDefaultChangedBroadcast(SubscriptionManager.getDefaultSubscriptionId());
369     }
370 
371     @UnsupportedAppUsage
isSubInfoReady()372     private boolean isSubInfoReady() {
373         return SubscriptionInfoUpdater.isSubInfoInitialized();
374     }
375 
376     /**
377      * This function marks SIM_SLOT_INDEX as INVALID for all subscriptions in the database. This
378      * should be done as part of initialization.
379      *
380      * TODO: SIM_SLOT_INDEX is based on current state and should not even be persisted in the
381      * database.
382      */
clearSlotIndexForSubInfoRecords()383     private void clearSlotIndexForSubInfoRecords() {
384         if (mContext == null) {
385             logel("[clearSlotIndexForSubInfoRecords] TelephonyManager or mContext is null");
386             return;
387         }
388 
389         // Update all subscriptions in simInfo db with invalid slot index
390         ContentValues value = new ContentValues(1);
391         value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
392         mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, null, null);
393     }
394 
395     /**
396      * Cache the Settings values by reading these values from Setting from disk to prevent disk I/O
397      * access during the API calling. This is based on an assumption that the Settings system will
398      * itself cache this value after the first read and thus only the first read after boot will
399      * access the disk.
400      */
cacheSettingValues()401     private void cacheSettingValues() {
402         Settings.Global.getInt(mContext.getContentResolver(),
403                 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
404                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
405 
406         Settings.Global.getInt(mContext.getContentResolver(),
407                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
408                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
409 
410         Settings.Global.getInt(mContext.getContentResolver(),
411                 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
412                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
413     }
414 
415     @UnsupportedAppUsage
enforceModifyPhoneState(String message)416     protected void enforceModifyPhoneState(String message) {
417         mContext.enforceCallingOrSelfPermission(
418                 android.Manifest.permission.MODIFY_PHONE_STATE, message);
419     }
420 
enforceReadPrivilegedPhoneState(String message)421     private void enforceReadPrivilegedPhoneState(String message) {
422         mContext.enforceCallingOrSelfPermission(
423                 Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
424     }
425 
426     /**
427      * Returns whether the {@code callingPackage} has access to subscriber identifiers on the
428      * specified {@code subId} using the provided {@code message} in any resulting
429      * SecurityException.
430      */
hasSubscriberIdentifierAccess(int subId, String callingPackage, String callingFeatureId, String message)431     private boolean hasSubscriberIdentifierAccess(int subId, String callingPackage,
432             String callingFeatureId, String message) {
433         try {
434             return TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mContext, subId,
435                     callingPackage, callingFeatureId, message);
436         } catch (SecurityException e) {
437             // A SecurityException indicates that the calling package is targeting at least the
438             // minimum level that enforces identifier access restrictions and the new access
439             // requirements are not met.
440             return false;
441         }
442     }
443 
444     /**
445      * Returns whether the {@code callingPackage} has access to the phone number on the specified
446      * {@code subId} using the provided {@code message} in any resulting SecurityException.
447      */
hasPhoneNumberAccess(int subId, String callingPackage, String callingFeatureId, String message)448     private boolean hasPhoneNumberAccess(int subId, String callingPackage, String callingFeatureId,
449             String message) {
450         try {
451             return TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(mContext, subId,
452                     callingPackage, callingFeatureId, message);
453         } catch (SecurityException e) {
454             return false;
455         }
456     }
457 
458     /**
459      * Broadcast when SubscriptionInfo has changed
460      * FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener
461      */
broadcastSimInfoContentChanged()462      private void broadcastSimInfoContentChanged() {
463         Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
464         mContext.sendBroadcast(intent);
465         intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
466         mContext.sendBroadcast(intent);
467      }
468 
469     /**
470      * Notify the changed of subscription info.
471      */
472     @UnsupportedAppUsage
notifySubscriptionInfoChanged()473     public void notifySubscriptionInfoChanged() {
474         TelephonyRegistryManager trm =
475                 (TelephonyRegistryManager)
476                         mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
477         if (DBG) logd("notifySubscriptionInfoChanged:");
478         trm.notifySubscriptionInfoChanged();
479 
480         // FIXME: Remove if listener technique accepted.
481         broadcastSimInfoContentChanged();
482 
483         MultiSimSettingController.getInstance().notifySubscriptionInfoChanged();
484         TelephonyMetrics metrics = TelephonyMetrics.getInstance();
485         List<SubscriptionInfo> subInfos;
486         synchronized (mSubInfoListLock) {
487             subInfos = new ArrayList<>(mCacheActiveSubInfoList);
488         }
489 
490         if (mOpptSubInfoListChangedDirtyBit.getAndSet(false)) {
491             notifyOpportunisticSubscriptionInfoChanged();
492         }
493         metrics.updateActiveSubscriptionInfoList(subInfos);
494     }
495 
496     /**
497      * New SubInfoRecord instance and fill in detail info
498      * @param cursor
499      * @return the query result of desired SubInfoRecord
500      */
501     @UnsupportedAppUsage
getSubInfoRecord(Cursor cursor)502     private SubscriptionInfo getSubInfoRecord(Cursor cursor) {
503         int id = cursor.getInt(cursor.getColumnIndexOrThrow(
504                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
505         String iccId = cursor.getString(cursor.getColumnIndexOrThrow(
506                 SubscriptionManager.ICC_ID));
507         int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(
508                 SubscriptionManager.SIM_SLOT_INDEX));
509         String displayName = cursor.getString(cursor.getColumnIndexOrThrow(
510                 SubscriptionManager.DISPLAY_NAME));
511         String carrierName = cursor.getString(cursor.getColumnIndexOrThrow(
512                 SubscriptionManager.CARRIER_NAME));
513         int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow(
514                 SubscriptionManager.NAME_SOURCE));
515         int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow(
516                 SubscriptionManager.HUE));
517         String number = cursor.getString(cursor.getColumnIndexOrThrow(
518                 SubscriptionManager.NUMBER));
519         int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(
520                 SubscriptionManager.DATA_ROAMING));
521         // Get the blank bitmap for this SubInfoRecord
522         Bitmap iconBitmap = BitmapFactory.decodeResource(mContext.getResources(),
523                 com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
524         String mcc = cursor.getString(cursor.getColumnIndexOrThrow(
525                 SubscriptionManager.MCC_STRING));
526         String mnc = cursor.getString(cursor.getColumnIndexOrThrow(
527                 SubscriptionManager.MNC_STRING));
528         String ehplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow(
529                 SubscriptionManager.EHPLMNS));
530         String hplmnsRaw = cursor.getString(cursor.getColumnIndexOrThrow(
531                 SubscriptionManager.HPLMNS));
532         String[] ehplmns = ehplmnsRaw == null ? null : ehplmnsRaw.split(",");
533         String[] hplmns = hplmnsRaw == null ? null : hplmnsRaw.split(",");
534 
535         // cardId is the private ICCID/EID string, also known as the card string
536         String cardId = cursor.getString(cursor.getColumnIndexOrThrow(
537                 SubscriptionManager.CARD_ID));
538         String countryIso = cursor.getString(cursor.getColumnIndexOrThrow(
539                 SubscriptionManager.ISO_COUNTRY_CODE));
540         // publicCardId is the publicly exposed int card ID
541         int publicCardId = mUiccController.convertToPublicCardId(cardId);
542         boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow(
543                 SubscriptionManager.IS_EMBEDDED)) == 1;
544         int carrierId = cursor.getInt(cursor.getColumnIndexOrThrow(
545                 SubscriptionManager.CARRIER_ID));
546         UiccAccessRule[] accessRules;
547         if (isEmbedded) {
548             accessRules = UiccAccessRule.decodeRules(cursor.getBlob(
549                     cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES)));
550         } else {
551             accessRules = null;
552         }
553         UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRules(cursor.getBlob(
554             cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS)));
555         boolean isOpportunistic = cursor.getInt(cursor.getColumnIndexOrThrow(
556                 SubscriptionManager.IS_OPPORTUNISTIC)) == 1;
557         String groupUUID = cursor.getString(cursor.getColumnIndexOrThrow(
558                 SubscriptionManager.GROUP_UUID));
559         int profileClass = cursor.getInt(cursor.getColumnIndexOrThrow(
560                 SubscriptionManager.PROFILE_CLASS));
561         int subType = cursor.getInt(cursor.getColumnIndexOrThrow(
562                 SubscriptionManager.SUBSCRIPTION_TYPE));
563         String groupOwner = getOptionalStringFromCursor(cursor, SubscriptionManager.GROUP_OWNER,
564                 /*defaultVal*/ null);
565         boolean areUiccApplicationsEnabled = cursor.getInt(cursor.getColumnIndexOrThrow(
566                 SubscriptionManager.UICC_APPLICATIONS_ENABLED)) == 1;
567 
568         if (VDBG) {
569             String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId);
570             String cardIdToPrint = SubscriptionInfo.givePrintableIccid(cardId);
571             logd("[getSubInfoRecord] id:" + id + " iccid:" + iccIdToPrint + " simSlotIndex:"
572                     + simSlotIndex + " carrierid:" + carrierId + " displayName:" + displayName
573                     + " nameSource:" + nameSource + " iconTint:" + iconTint
574                     + " dataRoaming:" + dataRoaming + " mcc:" + mcc + " mnc:" + mnc
575                     + " countIso:" + countryIso + " isEmbedded:"
576                     + isEmbedded + " accessRules:" + Arrays.toString(accessRules)
577                     + " carrierConfigAccessRules: " + Arrays.toString(carrierConfigAccessRules)
578                     + " cardId:" + cardIdToPrint + " publicCardId:" + publicCardId
579                     + " isOpportunistic:" + isOpportunistic + " groupUUID:" + groupUUID
580                     + " profileClass:" + profileClass + " subscriptionType: " + subType
581                     + " carrierConfigAccessRules:" + carrierConfigAccessRules
582                     + " areUiccApplicationsEnabled: " + areUiccApplicationsEnabled);
583         }
584 
585         // If line1number has been set to a different number, use it instead.
586         String line1Number = mTelephonyManager.getLine1Number(id);
587         if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
588             number = line1Number;
589         }
590         SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
591                 carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
592                 countryIso, isEmbedded, accessRules, cardId, publicCardId, isOpportunistic,
593                 groupUUID, false /* isGroupDisabled */, carrierId, profileClass, subType,
594                 groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled);
595         info.setAssociatedPlmns(ehplmns, hplmns);
596         return info;
597     }
598 
getOptionalStringFromCursor(Cursor cursor, String column, String defaultVal)599     private String getOptionalStringFromCursor(Cursor cursor, String column, String defaultVal) {
600         // Return defaultVal if the column doesn't exist.
601         int columnIndex = cursor.getColumnIndex(column);
602         return (columnIndex == -1) ? defaultVal : cursor.getString(columnIndex);
603     }
604 
605     /**
606      * Get a subscription that matches IccId.
607      * @return null if there isn't a match, or subscription info if there is one.
608      */
getSubInfoForIccId(String iccId)609     public SubscriptionInfo getSubInfoForIccId(String iccId) {
610         List<SubscriptionInfo> info = getSubInfo(
611                 SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null);
612         if (info == null || info.size() == 0) return null;
613         // Should be at most one subscription with the iccid.
614         return info.get(0);
615     }
616 
617     /**
618      * Query SubInfoRecord(s) from subinfo database
619      * @param selection A filter declaring which rows to return
620      * @param queryKey query key content
621      * @return Array list of queried result from database
622      */
623     @UnsupportedAppUsage
getSubInfo(String selection, Object queryKey)624     public List<SubscriptionInfo> getSubInfo(String selection, Object queryKey) {
625         if (VDBG) logd("selection:" + selection + ", querykey: " + queryKey);
626         String[] selectionArgs = null;
627         if (queryKey != null) {
628             selectionArgs = new String[] {queryKey.toString()};
629         }
630         ArrayList<SubscriptionInfo> subList = null;
631         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
632                 null, selection, selectionArgs, null);
633         try {
634             if (cursor != null) {
635                 while (cursor.moveToNext()) {
636                     SubscriptionInfo subInfo = getSubInfoRecord(cursor);
637                     if (subInfo != null) {
638                         if (subList == null) {
639                             subList = new ArrayList<SubscriptionInfo>();
640                         }
641                         subList.add(subInfo);
642                     }
643                 }
644             } else {
645                 if (DBG) logd("Query fail");
646             }
647         } finally {
648             if (cursor != null) {
649                 cursor.close();
650             }
651         }
652 
653         return subList;
654     }
655 
656     /**
657      * Find unused color to be set for new SubInfoRecord
658      * @param callingPackage The package making the IPC.
659      * @param callingFeatureId The feature in the package
660      * @return RGB integer value of color
661      */
getUnusedColor(String callingPackage, String callingFeatureId)662     private int getUnusedColor(String callingPackage, String callingFeatureId) {
663         List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage,
664                 callingFeatureId);
665         colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
666         int colorIdx = 0;
667 
668         if (availableSubInfos != null) {
669             for (int i = 0; i < colorArr.length; i++) {
670                 int j;
671                 for (j = 0; j < availableSubInfos.size(); j++) {
672                     if (colorArr[i] == availableSubInfos.get(j).getIconTint()) {
673                         break;
674                     }
675                 }
676                 if (j == availableSubInfos.size()) {
677                     return colorArr[i];
678                 }
679             }
680             colorIdx = availableSubInfos.size() % colorArr.length;
681         }
682         return colorArr[colorIdx];
683     }
684 
685     @Deprecated
686     @UnsupportedAppUsage
getActiveSubscriptionInfo(int subId, String callingPackage)687     public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
688         return getActiveSubscriptionInfo(subId, callingPackage, null);
689     }
690 
691     /**
692      * Get the active SubscriptionInfo with the subId key
693      * @param subId The unique SubscriptionInfo key in database
694      * @param callingPackage The package making the IPC.
695      * @param callingFeatureId The feature in the package
696      * @return SubscriptionInfo, maybe null if its not active
697      */
698     @Override
getActiveSubscriptionInfo(int subId, String callingPackage, String callingFeatureId)699     public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage,
700             String callingFeatureId) {
701         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
702                 callingFeatureId, "getActiveSubscriptionInfo")) {
703             return null;
704         }
705 
706         // Now that all security checks passes, perform the operation as ourselves.
707         final long identity = Binder.clearCallingIdentity();
708         List<SubscriptionInfo> subList;
709         try {
710             subList = getActiveSubscriptionInfoList(
711                     mContext.getOpPackageName(), mContext.getAttributionTag());
712         } finally {
713             Binder.restoreCallingIdentity(identity);
714         }
715         if (subList != null) {
716             for (SubscriptionInfo si : subList) {
717                 if (si.getSubscriptionId() == subId) {
718                     if (VDBG) {
719                         logd("[getActiveSubscriptionInfo]+ subId=" + subId + " subInfo=" + si);
720                     }
721                     return conditionallyRemoveIdentifiers(si, callingPackage, callingFeatureId,
722                             "getActiveSubscriptionInfo");
723                 }
724             }
725         }
726         if (DBG) {
727             logd("[getActiveSubscriptionInfo]- subId=" + subId
728                     + " subList=" + subList + " subInfo=null");
729         }
730 
731         return null;
732     }
733 
734     /**
735      * Get a single subscription info record for a given subscription.
736      *
737      * @param subId the subId to query.
738      *
739      * @hide
740      */
getSubscriptionInfo(int subId)741     public SubscriptionInfo getSubscriptionInfo(int subId) {
742         synchronized (mSubInfoListLock) {
743             // check cache for active subscriptions first, before querying db
744             for (SubscriptionInfo subInfo : mCacheActiveSubInfoList) {
745                 if (subInfo.getSubscriptionId() == subId) {
746                     return subInfo;
747                 }
748             }
749             // check cache for opportunistic subscriptions too, before querying db
750             for (SubscriptionInfo subInfo : mCacheOpportunisticSubInfoList) {
751                 if (subInfo.getSubscriptionId() == subId) {
752                     return subInfo;
753                 }
754             }
755         }
756 
757         List<SubscriptionInfo> subInfoList = getSubInfo(
758                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
759         if (subInfoList == null || subInfoList.isEmpty()) return null;
760         return subInfoList.get(0);
761     }
762 
763     /**
764      * Get the active SubscriptionInfo associated with the iccId
765      * @param iccId the IccId of SIM card
766      * @param callingPackage The package making the IPC.
767      * @param callingFeatureId The feature in the package
768      * @return SubscriptionInfo, maybe null if its not active
769      */
770     @Override
getActiveSubscriptionInfoForIccId(String iccId, String callingPackage, String callingFeatureId)771     public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage,
772             String callingFeatureId) {
773         enforceReadPrivilegedPhoneState("getActiveSubscriptionInfoForIccId");
774         return getActiveSubscriptionInfoForIccIdInternal(iccId);
775     }
776 
777     /**
778      * Get the active SubscriptionInfo associated with the given iccId. The caller *must* perform
779      * permission checks when using this method.
780      */
getActiveSubscriptionInfoForIccIdInternal(String iccId)781     private SubscriptionInfo getActiveSubscriptionInfoForIccIdInternal(String iccId) {
782         if (iccId == null) {
783             return null;
784         }
785 
786         final long identity = Binder.clearCallingIdentity();
787         try {
788             List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
789                     mContext.getOpPackageName(), mContext.getAttributionTag());
790             if (subList != null) {
791                 for (SubscriptionInfo si : subList) {
792                     if (iccId.equals(si.getIccId())) {
793                         if (DBG)
794                             logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
795                         return si;
796                     }
797                 }
798             }
799             if (DBG) {
800                 logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
801                         + " subList=" + subList + " subInfo=null");
802             }
803         } finally {
804             Binder.restoreCallingIdentity(identity);
805         }
806 
807         return null;
808     }
809 
810     /**
811      * Get the active SubscriptionInfo associated with the slotIndex.
812      * This API does not return details on Remote-SIM subscriptions.
813      * @param slotIndex the slot which the subscription is inserted
814      * @param callingPackage The package making the IPC.
815      * @param callingFeatureId The feature in the package
816      * @return SubscriptionInfo, null for Remote-SIMs or non-active slotIndex.
817      */
818     @Override
getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, String callingPackage, String callingFeatureId)819     public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
820             String callingPackage, String callingFeatureId) {
821         Phone phone = PhoneFactory.getPhone(slotIndex);
822         if (phone == null) {
823             if (DBG) {
824                 loge("[getActiveSubscriptionInfoForSimSlotIndex] no phone, slotIndex=" + slotIndex);
825             }
826             return null;
827         }
828         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
829                 mContext, phone.getSubId(), callingPackage, callingFeatureId,
830                 "getActiveSubscriptionInfoForSimSlotIndex")) {
831             return null;
832         }
833 
834         // Now that all security checks passes, perform the operation as ourselves.
835         final long identity = Binder.clearCallingIdentity();
836         List<SubscriptionInfo> subList;
837         try {
838             subList = getActiveSubscriptionInfoList(
839                     mContext.getOpPackageName(), mContext.getAttributionTag());
840         } finally {
841             Binder.restoreCallingIdentity(identity);
842         }
843         if (subList != null) {
844             for (SubscriptionInfo si : subList) {
845                 if (si.getSimSlotIndex() == slotIndex) {
846                     if (DBG) {
847                         logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex="
848                                 + slotIndex + " subId=" + si);
849                     }
850                     return conditionallyRemoveIdentifiers(si, callingPackage, callingFeatureId,
851                             "getActiveSubscriptionInfoForSimSlotIndex");
852                 }
853             }
854             if (DBG) {
855                 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex
856                         + " subId=null");
857             }
858         } else {
859             if (DBG) {
860                 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
861             }
862         }
863 
864 
865         return null;
866     }
867 
868     /**
869      * @param callingPackage The package making the IPC.
870      * @param callingFeatureId The feature in the package
871      * @return List of all SubscriptionInfo records in database,
872      * include those that were inserted before, maybe empty but not null.
873      * @hide
874      */
875     @Override
getAllSubInfoList(String callingPackage, String callingFeatureId)876     public List<SubscriptionInfo> getAllSubInfoList(String callingPackage,
877             String callingFeatureId) {
878         if (VDBG) logd("[getAllSubInfoList]+");
879 
880         // This API isn't public, so no need to provide a valid subscription ID - we're not worried
881         // about carrier-privileged callers not having access.
882         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
883                 mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
884                 callingFeatureId, "getAllSubInfoList")) {
885             return null;
886         }
887 
888         // Now that all security checks passes, perform the operation as ourselves.
889         final long identity = Binder.clearCallingIdentity();
890         List<SubscriptionInfo> subList;
891         try {
892             subList = getSubInfo(null, null);
893         } finally {
894             Binder.restoreCallingIdentity(identity);
895         }
896         if (subList != null) {
897             if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
898             subList.stream().map(
899                     subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
900                             callingPackage, callingFeatureId, "getAllSubInfoList"))
901                     .collect(Collectors.toList());
902         } else {
903             if (VDBG) logd("[getAllSubInfoList]- no info return");
904         }
905         return subList;
906     }
907 
makeCacheListCopyWithLock(List<SubscriptionInfo> cacheSubList)908     private List<SubscriptionInfo> makeCacheListCopyWithLock(List<SubscriptionInfo> cacheSubList) {
909         synchronized (mSubInfoListLock) {
910             return new ArrayList<>(cacheSubList);
911         }
912     }
913 
914     @Deprecated
915     @UnsupportedAppUsage
getActiveSubscriptionInfoList(String callingPackage)916     public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
917         return getSubscriptionInfoListFromCacheHelper(callingPackage, null,
918                 makeCacheListCopyWithLock(mCacheActiveSubInfoList));
919     }
920 
921     /**
922      * Get the SubInfoRecord(s) of the currently active SIM(s) - which include both local
923      * and remote SIMs.
924      * @param callingPackage The package making the IPC.
925      * @param callingFeatureId The feature in the package
926      * @return Array list of currently inserted SubInfoRecord(s)
927      */
928     @Override
getActiveSubscriptionInfoList(String callingPackage, String callingFeatureId)929     public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage,
930             String callingFeatureId) {
931         return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId,
932                 makeCacheListCopyWithLock(mCacheActiveSubInfoList));
933     }
934 
935     /**
936      * Refresh the cache of SubInfoRecord(s) of the currently available SIM(s) - including
937      * local & remote SIMs.
938      */
939     @VisibleForTesting  // For mockito to mock this method
refreshCachedActiveSubscriptionInfoList()940     public void refreshCachedActiveSubscriptionInfoList() {
941         boolean opptSubListChanged;
942 
943         List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
944                 SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
945                 + SubscriptionManager.SUBSCRIPTION_TYPE + "="
946                 + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM,
947                 null);
948 
949         synchronized (mSubInfoListLock) {
950             if (activeSubscriptionInfoList != null) {
951                 // Log when active sub info changes.
952                 if (mCacheActiveSubInfoList.size() != activeSubscriptionInfoList.size()
953                         || !mCacheActiveSubInfoList.containsAll(activeSubscriptionInfoList)) {
954                     logdl("Active subscription info list changed. " + activeSubscriptionInfoList);
955                 }
956 
957                 mCacheActiveSubInfoList.clear();
958                 activeSubscriptionInfoList.sort(SUBSCRIPTION_INFO_COMPARATOR);
959                 mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList);
960             } else {
961                 logd("activeSubscriptionInfoList is null.");
962                 mCacheActiveSubInfoList.clear();
963             }
964             if (DBG_CACHE) {
965                 if (!mCacheActiveSubInfoList.isEmpty()) {
966                     for (SubscriptionInfo si : mCacheActiveSubInfoList) {
967                         logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached info="
968                                 + si);
969                     }
970                 } else {
971                     logdl("[refreshCachedActiveSubscriptionInfoList]- no info return");
972                 }
973             }
974         }
975 
976         // Refresh cached opportunistic sub list and detect whether it's changed.
977         refreshCachedOpportunisticSubscriptionInfoList();
978     }
979 
980     @Deprecated
981     @UnsupportedAppUsage
getActiveSubInfoCount(String callingPackage)982     public int getActiveSubInfoCount(String callingPackage) {
983         return getActiveSubInfoCount(callingPackage, null);
984     }
985 
986     /**
987      * Get the SUB count of active SUB(s)
988      * @param callingPackage The package making the IPC.
989      * @param callingFeatureId The feature in the package.
990      * @return active SIM count
991      */
992     @Override
getActiveSubInfoCount(String callingPackage, String callingFeatureId)993     public int getActiveSubInfoCount(String callingPackage, String callingFeatureId) {
994         // Let getActiveSubscriptionInfoList perform permission checks / filtering.
995         List<SubscriptionInfo> records = getActiveSubscriptionInfoList(callingPackage,
996                 callingFeatureId);
997         if (records == null) {
998             if (VDBG) logd("[getActiveSubInfoCount] records null");
999             return 0;
1000         }
1001         if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
1002         return records.size();
1003     }
1004 
1005     /**
1006      * Get the SUB count of all SUB(s) in SubscriptoinInfo database
1007      * @param callingPackage The package making the IPC.
1008      * @param callingFeatureId The feature in the package
1009      * @return all SIM count in database, include what was inserted before
1010      */
1011     @Override
getAllSubInfoCount(String callingPackage, String callingFeatureId)1012     public int getAllSubInfoCount(String callingPackage, String callingFeatureId) {
1013         if (DBG) logd("[getAllSubInfoCount]+");
1014 
1015         // This API isn't public, so no need to provide a valid subscription ID - we're not worried
1016         // about carrier-privileged callers not having access.
1017         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
1018                 mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
1019                 callingFeatureId, "getAllSubInfoCount")) {
1020             return 0;
1021         }
1022 
1023         // Now that all security checks passes, perform the operation as ourselves.
1024         final long identity = Binder.clearCallingIdentity();
1025         try {
1026             Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
1027                     null, null, null, null);
1028             try {
1029                 if (cursor != null) {
1030                     int count = cursor.getCount();
1031                     if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
1032                     return count;
1033                 }
1034             } finally {
1035                 if (cursor != null) {
1036                     cursor.close();
1037                 }
1038             }
1039             if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
1040 
1041             return 0;
1042         } finally {
1043             Binder.restoreCallingIdentity(identity);
1044         }
1045     }
1046 
1047     /**
1048      * @return the maximum number of local subscriptions this device will support at any one time.
1049      */
1050     @Override
getActiveSubInfoCountMax()1051     public int getActiveSubInfoCountMax() {
1052         // FIXME: This valid now but change to use TelephonyDevController in the future
1053         return mTelephonyManager.getSimCount();
1054     }
1055 
1056     @Override
getAvailableSubscriptionInfoList(String callingPackage, String callingFeatureId)1057     public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage,
1058             String callingFeatureId) {
1059         try {
1060             enforceReadPrivilegedPhoneState("getAvailableSubscriptionInfoList");
1061         } catch (SecurityException e) {
1062             try {
1063                 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE, null);
1064                 // If caller doesn't have READ_PRIVILEGED_PHONE_STATE permission but only
1065                 // has READ_PHONE_STATE permission, log this event.
1066                 EventLog.writeEvent(0x534e4554, "185235454", Binder.getCallingUid());
1067             } catch (SecurityException ex) {
1068                 // Ignore
1069             }
1070             throw new SecurityException("Need READ_PRIVILEGED_PHONE_STATE to call "
1071                     + " getAvailableSubscriptionInfoList");
1072         }
1073 
1074         // Now that all security checks pass, perform the operation as ourselves.
1075         final long identity = Binder.clearCallingIdentity();
1076         try {
1077             String selection = SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
1078                     + SubscriptionManager.SUBSCRIPTION_TYPE + "="
1079                     + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
1080 
1081             EuiccManager euiccManager =
1082                     (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
1083             if (euiccManager.isEnabled()) {
1084                 selection += " OR " + SubscriptionManager.IS_EMBEDDED + "=1";
1085             }
1086 
1087             // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if
1088             // they are in inactive slot or programmatically disabled, they are still considered
1089             // available. In this case we get their iccid from slot info and include their
1090             // subscriptionInfos.
1091             List<String> iccIds = getIccIdsOfInsertedPhysicalSims();
1092 
1093             if (!iccIds.isEmpty()) {
1094                 selection += " OR ("  + getSelectionForIccIdList(iccIds.toArray(new String[0]))
1095                         + ")";
1096             }
1097 
1098             List<SubscriptionInfo> subList = getSubInfo(selection, null /* queryKey */);
1099 
1100             if (subList != null) {
1101                 subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
1102 
1103                 if (VDBG) logdl("[getAvailableSubInfoList]- " + subList.size() + " infos return");
1104             } else {
1105                 if (DBG) logdl("[getAvailableSubInfoList]- no info return");
1106             }
1107 
1108             return subList;
1109         } finally {
1110             Binder.restoreCallingIdentity(identity);
1111         }
1112     }
1113 
getIccIdsOfInsertedPhysicalSims()1114     private List<String> getIccIdsOfInsertedPhysicalSims() {
1115         List<String> ret = new ArrayList<>();
1116         UiccSlot[] uiccSlots = UiccController.getInstance().getUiccSlots();
1117         if (uiccSlots == null) return ret;
1118 
1119         for (UiccSlot uiccSlot : uiccSlots) {
1120             if (uiccSlot != null && uiccSlot.getCardState() != null
1121                     && uiccSlot.getCardState().isCardPresent()
1122                     && !uiccSlot.isEuicc()
1123                     && !TextUtils.isEmpty(uiccSlot.getIccId())) {
1124                 ret.add(IccUtils.stripTrailingFs(uiccSlot.getIccId()));
1125             }
1126         }
1127 
1128         return ret;
1129     }
1130 
1131     @Override
getAccessibleSubscriptionInfoList(String callingPackage)1132     public List<SubscriptionInfo> getAccessibleSubscriptionInfoList(String callingPackage) {
1133         EuiccManager euiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
1134         if (!euiccManager.isEnabled()) {
1135             if (DBG) {
1136                 logdl("[getAccessibleSubInfoList] Embedded subscriptions are disabled");
1137             }
1138             return null;
1139         }
1140 
1141         // Verify that the given package belongs to the calling UID.
1142         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
1143 
1144         // Perform the operation as ourselves. If the caller cannot read phone state, they may still
1145         // have carrier privileges per the subscription metadata, so we always need to make the
1146         // query and then filter the results.
1147         final long identity = Binder.clearCallingIdentity();
1148         List<SubscriptionInfo> subList;
1149         try {
1150             subList = getSubInfo(SubscriptionManager.IS_EMBEDDED + "=1", null);
1151         } finally {
1152             Binder.restoreCallingIdentity(identity);
1153         }
1154 
1155         if (subList == null) {
1156             if (DBG) logdl("[getAccessibleSubInfoList] No info returned");
1157             return null;
1158         }
1159 
1160         // Filter the list to only include subscriptions which the (restored) caller can manage.
1161         List<SubscriptionInfo> filteredList = subList.stream()
1162                 .filter(subscriptionInfo ->
1163                         subscriptionInfo.canManageSubscription(mContext, callingPackage))
1164                 .sorted(SUBSCRIPTION_INFO_COMPARATOR)
1165                 .collect(Collectors.toList());
1166         if (VDBG) {
1167             logdl("[getAccessibleSubInfoList] " + filteredList.size() + " infos returned");
1168         }
1169         return filteredList;
1170     }
1171 
1172     /**
1173      * Return the list of subscriptions in the database which are either:
1174      * <ul>
1175      * <li>Embedded (but see note about {@code includeNonRemovableSubscriptions}, or
1176      * <li>In the given list of current embedded ICCIDs (which may not yet be in the database, or
1177      *     which may not currently be marked as embedded).
1178      * </ul>
1179      *
1180      * <p>NOTE: This is not accessible to external processes, so it does not need a permission
1181      * check. It is only intended for use by {@link SubscriptionInfoUpdater}.
1182      *
1183      * @param embeddedIccids all ICCIDs of available embedded subscriptions. This is used to surface
1184      *     entries for profiles which had been previously deleted.
1185      * @param isEuiccRemovable whether the current ICCID is removable. Non-removable subscriptions
1186      *     will only be returned if the current ICCID is not removable; otherwise, they are left
1187      *     alone (not returned here unless in the embeddedIccids list) under the assumption that
1188      *     they will still be accessible when the eUICC containing them is activated.
1189      */
1190     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getSubscriptionInfoListForEmbeddedSubscriptionUpdate( String[] embeddedIccids, boolean isEuiccRemovable)1191     public List<SubscriptionInfo> getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
1192             String[] embeddedIccids, boolean isEuiccRemovable) {
1193         StringBuilder whereClause = new StringBuilder();
1194         whereClause.append("(").append(SubscriptionManager.IS_EMBEDDED).append("=1");
1195         if (isEuiccRemovable) {
1196             // Current eUICC is removable, so don't return non-removable subscriptions (which would
1197             // be deleted), as these are expected to still be present on a different, non-removable
1198             // eUICC.
1199             whereClause.append(" AND ").append(SubscriptionManager.IS_REMOVABLE).append("=1");
1200         }
1201         // Else, return both removable and non-removable subscriptions. This is expected to delete
1202         // all removable subscriptions, which is desired as they may not be accessible.
1203 
1204         whereClause.append(") OR ").append(SubscriptionManager.ICC_ID).append(" IN (");
1205         // ICCIDs are validated to contain only numbers when passed in, and come from a trusted
1206         // app, so no need to escape.
1207         for (int i = 0; i < embeddedIccids.length; i++) {
1208             if (i > 0) {
1209                 whereClause.append(",");
1210             }
1211             whereClause.append("\"").append(embeddedIccids[i]).append("\"");
1212         }
1213         whereClause.append(")");
1214 
1215         List<SubscriptionInfo> list = getSubInfo(whereClause.toString(), null);
1216         if (list == null) {
1217             return Collections.emptyList();
1218         }
1219         return list;
1220     }
1221 
1222     @Override
requestEmbeddedSubscriptionInfoListRefresh(int cardId)1223     public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
1224         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS,
1225                 "requestEmbeddedSubscriptionInfoListRefresh");
1226         long token = Binder.clearCallingIdentity();
1227         try {
1228             PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(cardId, null /* callback */);
1229         } finally {
1230             Binder.restoreCallingIdentity(token);
1231         }
1232     }
1233 
1234     /**
1235      * Asynchronously refresh the embedded subscription info list for the embedded card has the
1236      * given card id {@code cardId}.
1237      *
1238      * @param callback Optional callback to execute after the refresh completes. Must terminate
1239      *     quickly as it will be called from SubscriptionInfoUpdater's handler thread.
1240      */
1241     // No permission check needed as this is not exposed via AIDL.
requestEmbeddedSubscriptionInfoListRefresh( int cardId, @Nullable Runnable callback)1242     public void requestEmbeddedSubscriptionInfoListRefresh(
1243             int cardId, @Nullable Runnable callback) {
1244         PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback);
1245     }
1246 
1247     /**
1248      * Asynchronously refresh the embedded subscription info list for the embedded card has the
1249      * default card id return by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
1250      *
1251      * @param callback Optional callback to execute after the refresh completes. Must terminate
1252      *     quickly as it will be called from SubscriptionInfoUpdater's handler thread.
1253      */
1254     // No permission check needed as this is not exposed via AIDL.
requestEmbeddedSubscriptionInfoListRefresh(@ullable Runnable callback)1255     public void requestEmbeddedSubscriptionInfoListRefresh(@Nullable Runnable callback) {
1256         PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(
1257                 mTelephonyManager.getCardIdForDefaultEuicc(), callback);
1258     }
1259 
1260     /**
1261      * Add a new SubInfoRecord to subinfo database if needed
1262      * @param iccId the IccId of the SIM card
1263      * @param slotIndex the slot which the SIM is inserted
1264      * @return 0 if success, < 0 on error.
1265      */
1266     @Override
addSubInfoRecord(String iccId, int slotIndex)1267     public int addSubInfoRecord(String iccId, int slotIndex) {
1268         return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
1269     }
1270 
1271     /**
1272      * Add a new subscription info record, if needed.
1273      * @param uniqueId This is the unique identifier for the subscription within the specific
1274      *                 subscription type.
1275      * @param displayName human-readable name of the device the subscription corresponds to.
1276      * @param slotIndex value for {@link SubscriptionManager#SIM_SLOT_INDEX}
1277      * @param subscriptionType the type of subscription to be added.
1278      * @return 0 if success, < 0 on error.
1279      */
1280     @Override
addSubInfo(String uniqueId, String displayName, int slotIndex, int subscriptionType)1281     public int addSubInfo(String uniqueId, String displayName, int slotIndex,
1282             int subscriptionType) {
1283         if (DBG) {
1284             String iccIdStr = uniqueId;
1285             if (!isSubscriptionForRemoteSim(subscriptionType)) {
1286                 iccIdStr = SubscriptionInfo.givePrintableIccid(uniqueId);
1287             }
1288             logdl("[addSubInfoRecord]+ iccid: " + iccIdStr
1289                     + ", slotIndex: " + slotIndex
1290                     + ", subscriptionType: " + subscriptionType);
1291         }
1292 
1293         enforceModifyPhoneState("addSubInfo");
1294 
1295         // Now that all security checks passes, perform the operation as ourselves.
1296         final long identity = Binder.clearCallingIdentity();
1297         try {
1298             if (uniqueId == null) {
1299                 if (DBG) logdl("[addSubInfo]- null iccId");
1300                 return -1;
1301             }
1302 
1303             ContentResolver resolver = mContext.getContentResolver();
1304             String selection = SubscriptionManager.ICC_ID + "=?";
1305             String[] args;
1306             if (isSubscriptionForRemoteSim(subscriptionType)) {
1307                 selection += " AND " + SubscriptionManager.SUBSCRIPTION_TYPE + "=?";
1308                 args = new String[]{uniqueId, Integer.toString(subscriptionType)};
1309             } else {
1310                 selection += " OR " + SubscriptionManager.ICC_ID + "=?";
1311                 args = new String[]{uniqueId, IccUtils.getDecimalSubstring(uniqueId)};
1312             }
1313             Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
1314                     new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
1315                             SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE,
1316                             SubscriptionManager.ICC_ID, SubscriptionManager.CARD_ID},
1317                     selection, args, null);
1318 
1319             boolean setDisplayName = false;
1320             try {
1321                 boolean recordsDoNotExist = (cursor == null || !cursor.moveToFirst());
1322                 if (isSubscriptionForRemoteSim(subscriptionType)) {
1323                     if (recordsDoNotExist) {
1324                         // create a Subscription record
1325                         slotIndex = SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB;
1326                         Uri uri = insertEmptySubInfoRecord(uniqueId, displayName,
1327                                 slotIndex, subscriptionType);
1328                         if (DBG) logd("[addSubInfoRecord] New record created: " + uri);
1329                     } else {
1330                         if (DBG) logdl("[addSubInfoRecord] Record already exists");
1331                     }
1332                 } else {  // Handle Local SIM devices
1333                     if (recordsDoNotExist) {
1334                         setDisplayName = true;
1335                         Uri uri = insertEmptySubInfoRecord(uniqueId, slotIndex);
1336                         if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
1337                     } else { // there are matching records in the database for the given ICC_ID
1338                         int subId = cursor.getInt(0);
1339                         int oldSimInfoId = cursor.getInt(1);
1340                         int nameSource = cursor.getInt(2);
1341                         String oldIccId = cursor.getString(3);
1342                         String oldCardId = cursor.getString(4);
1343                         ContentValues value = new ContentValues();
1344 
1345                         if (slotIndex != oldSimInfoId) {
1346                             value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
1347                         }
1348 
1349                         if (oldIccId != null && oldIccId.length() < uniqueId.length()
1350                                 && (oldIccId.equals(IccUtils.getDecimalSubstring(uniqueId)))) {
1351                             value.put(SubscriptionManager.ICC_ID, uniqueId);
1352                         }
1353 
1354                         UiccCard card = mUiccController.getUiccCardForPhone(slotIndex);
1355                         if (card != null) {
1356                             String cardId = card.getCardId();
1357                             if (cardId != null && cardId != oldCardId) {
1358                                 value.put(SubscriptionManager.CARD_ID, cardId);
1359                             }
1360                         }
1361 
1362                         if (value.size() > 0) {
1363                             resolver.update(SubscriptionManager.getUriForSubscriptionId(subId),
1364                                     value, null, null);
1365                         }
1366 
1367                         if (DBG) logdl("[addSubInfoRecord] Record already exists");
1368                     }
1369                 }
1370             } finally {
1371                 if (cursor != null) {
1372                     cursor.close();
1373                 }
1374             }
1375 
1376             selection = SubscriptionManager.SIM_SLOT_INDEX + "=?";
1377             args = new String[] {String.valueOf(slotIndex)};
1378             if (isSubscriptionForRemoteSim(subscriptionType)) {
1379                 selection = SubscriptionManager.ICC_ID + "=? AND "
1380                         + SubscriptionManager.SUBSCRIPTION_TYPE + "=?";
1381                 args = new String[]{uniqueId, Integer.toString(subscriptionType)};
1382             }
1383             cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
1384                     selection, args, null);
1385             try {
1386                 if (cursor != null && cursor.moveToFirst()) {
1387                     do {
1388                         int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
1389                                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
1390                         // If sSlotIndexToSubIds already has the same subId for a slotIndex/phoneId,
1391                         // do not add it.
1392                         if (addToSubIdList(slotIndex, subId, subscriptionType)) {
1393                             // TODO While two subs active, if user deactivats first
1394                             // one, need to update the default subId with second one.
1395 
1396                             // FIXME: Currently we assume phoneId == slotIndex which in the future
1397                             // may not be true, for instance with multiple subs per slot.
1398                             // But is true at the moment.
1399                             int subIdCountMax = getActiveSubInfoCountMax();
1400                             int defaultSubId = getDefaultSubId();
1401                             if (DBG) {
1402                                 logdl("[addSubInfoRecord]"
1403                                         + " sSlotIndexToSubIds.size=" + sSlotIndexToSubIds.size()
1404                                         + " slotIndex=" + slotIndex + " subId=" + subId
1405                                         + " defaultSubId=" + defaultSubId
1406                                         + " simCount=" + subIdCountMax);
1407                             }
1408 
1409                             // Set the default sub if not set or if single sim device
1410                             if (!isSubscriptionForRemoteSim(subscriptionType)) {
1411                                 if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
1412                                         || subIdCountMax == 1) {
1413                                     logdl("setting default fallback subid to " + subId);
1414                                     setDefaultFallbackSubId(subId, subscriptionType);
1415                                 }
1416                                 // If single sim device, set this subscription as the default for
1417                                 // everything
1418                                 if (subIdCountMax == 1) {
1419                                     if (DBG) {
1420                                         logdl("[addSubInfoRecord] one sim set defaults to subId="
1421                                                 + subId);
1422                                     }
1423                                     setDefaultDataSubId(subId);
1424                                     setDefaultSmsSubId(subId);
1425                                     setDefaultVoiceSubId(subId);
1426                                 }
1427                             } else {
1428                                 updateDefaultSubIdsIfNeeded(subId, subscriptionType);
1429                             }
1430                         } else {
1431                             if (DBG) {
1432                                 logdl("[addSubInfoRecord] current SubId is already known, "
1433                                         + "IGNORE");
1434                             }
1435                         }
1436                         if (DBG) {
1437                             logdl("[addSubInfoRecord] hashmap(" + slotIndex + "," + subId + ")");
1438                         }
1439                     } while (cursor.moveToNext());
1440                 }
1441             } finally {
1442                 if (cursor != null) {
1443                     cursor.close();
1444                 }
1445             }
1446 
1447             // Refresh the Cache of Active Subscription Info List. This should be done after
1448             // updating sSlotIndexToSubIds which is done through addToSubIdList() above.
1449             refreshCachedActiveSubscriptionInfoList();
1450 
1451             if (isSubscriptionForRemoteSim(subscriptionType)) {
1452                 notifySubscriptionInfoChanged();
1453             } else {  // Handle Local SIM devices
1454                 // Set Display name after sub id is set above so as to get valid simCarrierName
1455                 int subId = getSubIdUsingPhoneId(slotIndex);
1456                 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
1457                     if (DBG) {
1458                         logdl("[addSubInfoRecord]- getSubId failed invalid subId = " + subId);
1459                     }
1460                     return -1;
1461                 }
1462                 if (setDisplayName) {
1463                     String simCarrierName = mTelephonyManager.getSimOperatorName(subId);
1464                     String nameToSet;
1465 
1466                     if (!TextUtils.isEmpty(simCarrierName)) {
1467                         nameToSet = simCarrierName;
1468                     } else {
1469                         nameToSet = "CARD " + Integer.toString(slotIndex + 1);
1470                     }
1471 
1472                     ContentValues value = new ContentValues();
1473                     value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
1474                     resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value,
1475                             null, null);
1476 
1477                     // Refresh the Cache of Active Subscription Info List
1478                     refreshCachedActiveSubscriptionInfoList();
1479 
1480                     if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
1481                 }
1482 
1483                 if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubIds.size());
1484             }
1485 
1486         } finally {
1487             Binder.restoreCallingIdentity(identity);
1488         }
1489         return 0;
1490     }
1491 
updateDefaultSubIdsIfNeeded(int newDefault, int subscriptionType)1492     private void updateDefaultSubIdsIfNeeded(int newDefault, int subscriptionType) {
1493         if (DBG) {
1494             logdl("[updateDefaultSubIdsIfNeeded] newDefault=" + newDefault
1495                     + ", subscriptionType=" + subscriptionType);
1496         }
1497         // Set the default ot new value only if the current default is invalid.
1498         if (!isActiveSubscriptionId(getDefaultSubId())) {
1499             // current default is not valid anylonger. set a new default
1500             if (DBG) {
1501                 logdl("[updateDefaultSubIdsIfNeeded] set sDefaultFallbackSubId=" + newDefault);
1502             }
1503             setDefaultFallbackSubId(newDefault, subscriptionType);
1504         }
1505 
1506         int value = getDefaultSmsSubId();
1507         if (!isActiveSubscriptionId(value)) {
1508             // current default is not valid. set it to the given newDefault value
1509             setDefaultSmsSubId(newDefault);
1510         }
1511         value = getDefaultDataSubId();
1512         if (!isActiveSubscriptionId(value)) {
1513             setDefaultDataSubId(newDefault);
1514         }
1515         value = getDefaultVoiceSubId();
1516         if (!isActiveSubscriptionId(value)) {
1517             setDefaultVoiceSubId(newDefault);
1518         }
1519     }
1520 
1521     /**
1522      * This method returns true if the given subId is among the list of currently active
1523      * subscriptions.
1524      */
isActiveSubscriptionId(int subId)1525     private boolean isActiveSubscriptionId(int subId) {
1526         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
1527         ArrayList<Integer> subIdList = getActiveSubIdArrayList();
1528         if (subIdList.isEmpty()) return false;
1529         return subIdList.contains(new Integer(subId));
1530     }
1531 
1532     /*
1533      * Delete subscription info record for the given device.
1534      * @param uniqueId This is the unique identifier for the subscription within the specific
1535      *                 subscription type.
1536      * @param subscriptionType the type of subscription to be removed
1537      * @return 0 if success, < 0 on error.
1538      */
1539     @Override
removeSubInfo(String uniqueId, int subscriptionType)1540     public int removeSubInfo(String uniqueId, int subscriptionType) {
1541         enforceModifyPhoneState("removeSubInfo");
1542         if (DBG) {
1543             logd("[removeSubInfo] uniqueId: " + uniqueId
1544                     + ", subscriptionType: " + subscriptionType);
1545         }
1546 
1547         // validate the given info - does it exist in the active subscription list
1548         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1549         int slotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
1550         synchronized (mSubInfoListLock) {
1551             for (SubscriptionInfo info : mCacheActiveSubInfoList) {
1552                 if ((info.getSubscriptionType() == subscriptionType)
1553                         && info.getIccId().equalsIgnoreCase(uniqueId)) {
1554                     subId = info.getSubscriptionId();
1555                     slotIndex = info.getSimSlotIndex();
1556                     break;
1557                 }
1558             }
1559         }
1560         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1561             if (DBG) {
1562                 logd("Invalid subscription details: subscriptionType = " + subscriptionType
1563                         + ", uniqueId = " + uniqueId);
1564             }
1565             return -1;
1566         }
1567 
1568         if (DBG) logd("removing the subid : " + subId);
1569 
1570         // Now that all security checks passes, perform the operation as ourselves.
1571         int result = 0;
1572         final long identity = Binder.clearCallingIdentity();
1573         try {
1574             ContentResolver resolver = mContext.getContentResolver();
1575             result = resolver.delete(SubscriptionManager.CONTENT_URI,
1576                     SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=? AND "
1577                             + SubscriptionManager.SUBSCRIPTION_TYPE + "=?",
1578                     new String[]{Integer.toString(subId), Integer.toString(subscriptionType)});
1579             if (result != 1) {
1580                 if (DBG) {
1581                     logd("found NO subscription to remove with subscriptionType = "
1582                             + subscriptionType + ", uniqueId = " + uniqueId);
1583                 }
1584                 return -1;
1585             }
1586             refreshCachedActiveSubscriptionInfoList();
1587             result = sSlotIndexToSubIds.removeFromSubIdList(slotIndex, subId);
1588             if (result == NO_ENTRY_FOR_SLOT_INDEX) {
1589                 loge("sSlotIndexToSubIds has no entry for slotIndex = " + slotIndex);
1590             } else if (result == SUB_ID_NOT_IN_SLOT) {
1591                 loge("sSlotIndexToSubIds has no subid: " + subId + ", in index: " + slotIndex);
1592             }
1593 
1594             // Since a subscription is removed, if this one is set as default for any setting,
1595             // set some other subid as the default.
1596             int newDefault = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1597             SubscriptionInfo info = null;
1598             final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
1599                     mContext.getOpPackageName(), mContext.getAttributionTag());
1600             if (!records.isEmpty()) {
1601                 // yes, we have more subscriptions. pick the first one.
1602                 // FIXME do we need a policy to figure out which one is to be next default
1603                 info = records.get(0);
1604             }
1605             updateDefaultSubIdsIfNeeded(info.getSubscriptionId(), info.getSubscriptionType());
1606 
1607             notifySubscriptionInfoChanged();
1608         } finally {
1609             Binder.restoreCallingIdentity(identity);
1610         }
1611         return result;
1612     }
1613 
1614     /**
1615      * Clear an subscriptionInfo to subinfo database if needed by updating slot index to invalid.
1616      * @param slotIndex the slot which the SIM is removed
1617      */
clearSubInfoRecord(int slotIndex)1618     public void clearSubInfoRecord(int slotIndex) {
1619         if (DBG) logdl("[clearSubInfoRecord]+ iccId:" + " slotIndex:" + slotIndex);
1620 
1621         // update simInfo db with invalid slot index
1622         ContentResolver resolver = mContext.getContentResolver();
1623         ContentValues value = new ContentValues(1);
1624         value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
1625         String where = "(" + SubscriptionManager.SIM_SLOT_INDEX + "=" + slotIndex + ")";
1626         resolver.update(SubscriptionManager.CONTENT_URI, value, where, null);
1627 
1628         // Refresh the Cache of Active Subscription Info List
1629         refreshCachedActiveSubscriptionInfoList();
1630 
1631         sSlotIndexToSubIds.remove(slotIndex);
1632     }
1633 
1634     /**
1635      * Insert an empty SubInfo record into the database.
1636      *
1637      * <p>NOTE: This is not accessible to external processes, so it does not need a permission
1638      * check. It is only intended for use by {@link SubscriptionInfoUpdater}.
1639      *
1640      * <p>Precondition: No record exists with this iccId.
1641      */
1642     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
insertEmptySubInfoRecord(String iccId, int slotIndex)1643     public Uri insertEmptySubInfoRecord(String iccId, int slotIndex) {
1644         return insertEmptySubInfoRecord(iccId, null, slotIndex,
1645                 SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
1646     }
1647 
insertEmptySubInfoRecord(String uniqueId, String displayName, int slotIndex, int subscriptionType)1648     Uri insertEmptySubInfoRecord(String uniqueId, String displayName, int slotIndex,
1649             int subscriptionType) {
1650         ContentResolver resolver = mContext.getContentResolver();
1651         ContentValues value = new ContentValues();
1652         value.put(SubscriptionManager.ICC_ID, uniqueId);
1653         int color = getUnusedColor(mContext.getOpPackageName(), mContext.getAttributionTag());
1654         // default SIM color differs between slots
1655         value.put(SubscriptionManager.HUE, color);
1656         value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
1657         value.put(SubscriptionManager.CARRIER_NAME, "");
1658         value.put(SubscriptionManager.CARD_ID, uniqueId);
1659         value.put(SubscriptionManager.SUBSCRIPTION_TYPE, subscriptionType);
1660         if (!TextUtils.isEmpty(displayName)) {
1661             value.put(SubscriptionManager.DISPLAY_NAME, displayName);
1662         }
1663         if (!isSubscriptionForRemoteSim(subscriptionType)) {
1664             UiccCard card = mUiccController.getUiccCardForPhone(slotIndex);
1665             if (card != null) {
1666                 String cardId = card.getCardId();
1667                 if (cardId != null) {
1668                     value.put(SubscriptionManager.CARD_ID, cardId);
1669                 }
1670             }
1671         }
1672 
1673         Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
1674 
1675         // Refresh the Cache of Active Subscription Info List
1676         refreshCachedActiveSubscriptionInfoList();
1677 
1678         return uri;
1679     }
1680 
1681     /**
1682      * Generate and set carrier text based on input parameters
1683      * @param showPlmn flag to indicate if plmn should be included in carrier text
1684      * @param plmn plmn to be included in carrier text
1685      * @param showSpn flag to indicate if spn should be included in carrier text
1686      * @param spn spn to be included in carrier text
1687      * @return true if carrier text is set, false otherwise
1688      */
1689     @UnsupportedAppUsage
setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn, String spn)1690     public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,
1691                               String spn) {
1692         synchronized (mLock) {
1693             int subId = getSubIdUsingPhoneId(slotIndex);
1694             if (mContext.getPackageManager().resolveContentProvider(
1695                     SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||
1696                     !SubscriptionManager.isValidSubscriptionId(subId)) {
1697                 // No place to store this info. Notify registrants of the change anyway as they
1698                 // might retrieve the SPN/PLMN text from the SST sticky broadcast.
1699                 // TODO: This can be removed once SubscriptionController is not running on devices
1700                 // that don't need it, such as TVs.
1701                 if (DBG) logd("[setPlmnSpn] No valid subscription to store info");
1702                 notifySubscriptionInfoChanged();
1703                 return false;
1704             }
1705             String carrierText = "";
1706             if (showPlmn) {
1707                 carrierText = plmn;
1708                 if (showSpn) {
1709                     // Need to show both plmn and spn if both are not same.
1710                     if(!Objects.equals(spn, plmn)) {
1711                         String separator = mContext.getString(
1712                                 com.android.internal.R.string.kg_text_message_separator).toString();
1713                         carrierText = new StringBuilder().append(carrierText).append(separator)
1714                                 .append(spn).toString();
1715                     }
1716                 }
1717             } else if (showSpn) {
1718                 carrierText = spn;
1719             }
1720             setCarrierText(carrierText, subId);
1721             return true;
1722         }
1723     }
1724 
1725     /**
1726      * Set carrier text by simInfo index
1727      * @param text new carrier text
1728      * @param subId the unique SubInfoRecord index in database
1729      * @return the number of records updated
1730      */
setCarrierText(String text, int subId)1731     private int setCarrierText(String text, int subId) {
1732         if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);
1733 
1734         enforceModifyPhoneState("setCarrierText");
1735 
1736         // Now that all security checks passes, perform the operation as ourselves.
1737         final long identity = Binder.clearCallingIdentity();
1738         try {
1739             ContentValues value = new ContentValues(1);
1740             value.put(SubscriptionManager.CARRIER_NAME, text);
1741 
1742             int result = mContext.getContentResolver().update(
1743                     SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
1744 
1745             // Refresh the Cache of Active Subscription Info List
1746             refreshCachedActiveSubscriptionInfoList();
1747 
1748             notifySubscriptionInfoChanged();
1749 
1750             return result;
1751         } finally {
1752             Binder.restoreCallingIdentity(identity);
1753         }
1754     }
1755 
1756     /**
1757      * Set SIM color tint by simInfo index
1758      * @param tint the tint color of the SIM
1759      * @param subId the unique SubInfoRecord index in database
1760      * @return the number of records updated
1761      */
1762     @Override
setIconTint(int tint, int subId)1763     public int setIconTint(int tint, int subId) {
1764         if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
1765 
1766         enforceModifyPhoneState("setIconTint");
1767 
1768         // Now that all security checks passes, perform the operation as ourselves.
1769         final long identity = Binder.clearCallingIdentity();
1770         try {
1771             validateSubId(subId);
1772             ContentValues value = new ContentValues(1);
1773             value.put(SubscriptionManager.HUE, tint);
1774             if (DBG) logd("[setIconTint]- tint:" + tint + " set");
1775 
1776             int result = mContext.getContentResolver().update(
1777                     SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
1778 
1779             // Refresh the Cache of Active Subscription Info List
1780             refreshCachedActiveSubscriptionInfoList();
1781 
1782             notifySubscriptionInfoChanged();
1783 
1784             return result;
1785         } finally {
1786             Binder.restoreCallingIdentity(identity);
1787         }
1788     }
1789 
1790     /**
1791      * This is only for internal use and the returned priority is arbitrary. The idea is to give a
1792      * higher value to name source that has higher priority to override other name sources.
1793      * @param nameSource Source of display name
1794      * @return int representing the priority. Higher value means higher priority.
1795      */
getNameSourcePriority(@imDisplayNameSource int nameSource)1796     public static int getNameSourcePriority(@SimDisplayNameSource int nameSource) {
1797         int index = Arrays.asList(
1798                 SubscriptionManager.NAME_SOURCE_CARRIER_ID,
1799                 SubscriptionManager.NAME_SOURCE_SIM_PNN,
1800                 SubscriptionManager.NAME_SOURCE_SIM_SPN,
1801                 SubscriptionManager.NAME_SOURCE_CARRIER,
1802                 SubscriptionManager.NAME_SOURCE_USER_INPUT // user has highest priority.
1803         ).indexOf(nameSource);
1804         return (index < 0) ? 0 : index;
1805     }
1806 
1807     /**
1808      * Validate whether the NAME_SOURCE_SIM_PNN, NAME_SOURCE_SIM_SPN and
1809      * NAME_SOURCE_CARRIER exist or not.
1810      */
1811     @VisibleForTesting
isExistingNameSourceStillValid(SubscriptionInfo subInfo)1812     public boolean isExistingNameSourceStillValid(SubscriptionInfo subInfo) {
1813 
1814         int subId = subInfo.getSubscriptionId();
1815         int phoneId = getPhoneId(subInfo.getSubscriptionId());
1816 
1817         Phone phone = PhoneFactory.getPhone(phoneId);
1818         if (phone == null) {
1819             return true;
1820         }
1821 
1822         String spn;
1823 
1824         switch (subInfo.getNameSource()) {
1825             case SubscriptionManager.NAME_SOURCE_SIM_PNN:
1826                 String pnn = phone.getPlmn();
1827                 return !TextUtils.isEmpty(pnn);
1828             case SubscriptionManager.NAME_SOURCE_SIM_SPN:
1829                 spn = getServiceProviderName(phoneId);
1830                 return !TextUtils.isEmpty(spn);
1831             case SubscriptionManager.NAME_SOURCE_CARRIER:
1832                 // Can not validate eSIM since it should not override with a lower priority source
1833                 // if the name is actually coming from eSIM and not from carrier config.
1834                 if (subInfo.isEmbedded()) {
1835                     return true;
1836                 }
1837                 CarrierConfigManager configLoader =
1838                         mContext.getSystemService(CarrierConfigManager.class);
1839                 PersistableBundle config =
1840                         configLoader.getConfigForSubId(subId);
1841                 if (config == null) {
1842                     return true;
1843                 }
1844                 boolean isCarrierNameOverride = config.getBoolean(
1845                         CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
1846                 String carrierName = config.getString(
1847                         CarrierConfigManager.KEY_CARRIER_NAME_STRING);
1848                 spn = getServiceProviderName(phoneId);
1849                 return isCarrierNameOverride
1850                         || (TextUtils.isEmpty(spn) && !TextUtils.isEmpty(carrierName));
1851             case SubscriptionManager.NAME_SOURCE_CARRIER_ID:
1852             case SubscriptionManager.NAME_SOURCE_USER_INPUT:
1853                 return true;
1854         }
1855         return false;
1856     }
1857 
1858     @VisibleForTesting
getServiceProviderName(int phoneId)1859     public String getServiceProviderName(int phoneId) {
1860         UiccProfile profile = mUiccController.getUiccProfileForPhone(phoneId);
1861         if (profile == null) {
1862             return null;
1863         }
1864         return profile.getServiceProviderName();
1865     }
1866 
1867     /**
1868      * Set display name by simInfo index with name source
1869      * @param displayName the display name of SIM card
1870      * @param subId the unique SubInfoRecord index in database
1871      * @param nameSource SIM display name source
1872      * @return the number of records updated
1873      */
1874     @Override
setDisplayNameUsingSrc(String displayName, int subId, @SimDisplayNameSource int nameSource)1875     public int setDisplayNameUsingSrc(String displayName, int subId,
1876                                       @SimDisplayNameSource int nameSource) {
1877         if (DBG) {
1878             logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
1879                 + " nameSource:" + nameSource);
1880         }
1881 
1882         enforceModifyPhoneState("setDisplayNameUsingSrc");
1883 
1884         // Now that all security checks passes, perform the operation as ourselves.
1885         final long identity = Binder.clearCallingIdentity();
1886         try {
1887             validateSubId(subId);
1888             List<SubscriptionInfo> allSubInfo = getSubInfo(null, null);
1889             // if there is no sub in the db, return 0 since subId does not exist in db
1890             if (allSubInfo == null || allSubInfo.isEmpty()) return 0;
1891             for (SubscriptionInfo subInfo : allSubInfo) {
1892                 int subInfoNameSource = subInfo.getNameSource();
1893                 boolean isHigherPriority = (getNameSourcePriority(subInfoNameSource)
1894                         > getNameSourcePriority(nameSource));
1895                 boolean isEqualPriorityAndName = (getNameSourcePriority(subInfoNameSource)
1896                         == getNameSourcePriority(nameSource))
1897                         && (TextUtils.equals(displayName, subInfo.getDisplayName()));
1898                 if (subInfo.getSubscriptionId() == subId
1899                         && isExistingNameSourceStillValid(subInfo)
1900                         && (isHigherPriority || isEqualPriorityAndName)) {
1901                     logd("Name source " + subInfoNameSource + "'s priority "
1902                             + getNameSourcePriority(subInfoNameSource) + " is greater than "
1903                             + "name source " + nameSource + "'s priority "
1904                             + getNameSourcePriority(nameSource) + ", return now.");
1905                     return 0;
1906                 }
1907             }
1908             String nameToSet;
1909             if (TextUtils.isEmpty(displayName) || displayName.trim().length() == 0) {
1910                 nameToSet = mTelephonyManager.getSimOperatorName(subId);
1911                 if (TextUtils.isEmpty(nameToSet)) {
1912                     if (nameSource == SubscriptionManager.NAME_SOURCE_USER_INPUT
1913                             && SubscriptionManager.isValidSlotIndex(getSlotIndex(subId))) {
1914                         nameToSet = "CARD " + (getSlotIndex(subId) + 1);
1915                     } else {
1916                         nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
1917                     }
1918                 }
1919             } else {
1920                 nameToSet = displayName;
1921             }
1922             ContentValues value = new ContentValues(1);
1923             value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
1924             if (nameSource >= SubscriptionManager.NAME_SOURCE_CARRIER_ID) {
1925                 if (DBG) logd("Set nameSource=" + nameSource);
1926                 value.put(SubscriptionManager.NAME_SOURCE, nameSource);
1927             }
1928             if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
1929 
1930             // Update the nickname on the eUICC chip if it's an embedded subscription.
1931             SubscriptionInfo sub = getSubscriptionInfo(subId);
1932             if (sub != null && sub.isEmbedded()) {
1933                 // Ignore the result.
1934                 int cardId = sub.getCardId();
1935                 if (DBG) logd("Updating embedded sub nickname on cardId: " + cardId);
1936                 EuiccManager euiccManager = ((EuiccManager)
1937                         mContext.getSystemService(Context.EUICC_SERVICE)).createForCardId(cardId);
1938                 euiccManager.updateSubscriptionNickname(subId, displayName,
1939                         // This PendingIntent simply fulfills the requirement to pass in a callback;
1940                         // we don't care about the result (hence 0 requestCode and no action
1941                         // specified on the intent).
1942                         PendingIntent.getService(
1943                             mContext, 0 /* requestCode */, new Intent(), 0 /* flags */));
1944             }
1945 
1946             int result = updateDatabase(value, subId, true);
1947 
1948             // Refresh the Cache of Active Subscription Info List
1949             refreshCachedActiveSubscriptionInfoList();
1950 
1951             notifySubscriptionInfoChanged();
1952 
1953             return result;
1954         } finally {
1955             Binder.restoreCallingIdentity(identity);
1956         }
1957     }
1958 
1959     /**
1960      * Set phone number by subId
1961      * @param number the phone number of the SIM
1962      * @param subId the unique SubInfoRecord index in database
1963      * @return the number of records updated
1964      */
1965     @Override
setDisplayNumber(String number, int subId)1966     public int setDisplayNumber(String number, int subId) {
1967         if (DBG) logd("[setDisplayNumber]+ subId:" + subId);
1968 
1969         enforceModifyPhoneState("setDisplayNumber");
1970 
1971         // Now that all security checks passes, perform the operation as ourselves.
1972         final long identity = Binder.clearCallingIdentity();
1973         try {
1974             validateSubId(subId);
1975             int result;
1976             int phoneId = getPhoneId(subId);
1977 
1978             if (number == null || phoneId < 0 ||
1979                     phoneId >= mTelephonyManager.getPhoneCount()) {
1980                 if (DBG) logd("[setDispalyNumber]- fail");
1981                 return -1;
1982             }
1983             ContentValues value = new ContentValues(1);
1984             value.put(SubscriptionManager.NUMBER, number);
1985 
1986             // This function had a call to update number on the SIM (Phone.setLine1Number()) but
1987             // that was removed as there doesn't seem to be a reason for that. If it is added
1988             // back, watch out for deadlocks.
1989 
1990             result = mContext.getContentResolver().update(
1991                     SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
1992 
1993             // Refresh the Cache of Active Subscription Info List
1994             refreshCachedActiveSubscriptionInfoList();
1995 
1996             if (DBG) logd("[setDisplayNumber]- update result :" + result);
1997             notifySubscriptionInfoChanged();
1998 
1999             return result;
2000         } finally {
2001             Binder.restoreCallingIdentity(identity);
2002         }
2003     }
2004 
2005     /**
2006      * Set the EHPLMNs and HPLMNs associated with the subscription.
2007      */
setAssociatedPlmns(String[] ehplmns, String[] hplmns, int subId)2008     public void setAssociatedPlmns(String[] ehplmns, String[] hplmns, int subId) {
2009         if (DBG) logd("[setAssociatedPlmns]+ subId:" + subId);
2010 
2011         validateSubId(subId);
2012         int phoneId = getPhoneId(subId);
2013 
2014         if (phoneId < 0 || phoneId >= mTelephonyManager.getPhoneCount()) {
2015             if (DBG) logd("[setAssociatedPlmns]- fail");
2016             return;
2017         }
2018 
2019         String formattedEhplmns = ehplmns == null ? "" : String.join(",", ehplmns);
2020         String formattedHplmns = hplmns == null ? "" : String.join(",", hplmns);
2021 
2022         ContentValues value = new ContentValues(2);
2023         value.put(SubscriptionManager.EHPLMNS, formattedEhplmns);
2024         value.put(SubscriptionManager.HPLMNS, formattedHplmns);
2025 
2026         int count = mContext.getContentResolver().update(
2027                 SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2028 
2029         // Refresh the Cache of Active Subscription Info List
2030         refreshCachedActiveSubscriptionInfoList();
2031 
2032         if (DBG) logd("[setAssociatedPlmns]- update result :" + count);
2033         notifySubscriptionInfoChanged();
2034     }
2035 
2036     /**
2037      * Set data roaming by simInfo index
2038      * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
2039      * @param subId the unique SubInfoRecord index in database
2040      * @return the number of records updated
2041      */
2042     @Override
setDataRoaming(int roaming, int subId)2043     public int setDataRoaming(int roaming, int subId) {
2044         if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
2045 
2046         enforceModifyPhoneState("setDataRoaming");
2047 
2048         // Now that all security checks passes, perform the operation as ourselves.
2049         final long identity = Binder.clearCallingIdentity();
2050         try {
2051             validateSubId(subId);
2052             if (roaming < 0) {
2053                 if (DBG) logd("[setDataRoaming]- fail");
2054                 return -1;
2055             }
2056             ContentValues value = new ContentValues(1);
2057             value.put(SubscriptionManager.DATA_ROAMING, roaming);
2058             if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
2059 
2060             int result = updateDatabase(value, subId, true);
2061 
2062             // Refresh the Cache of Active Subscription Info List
2063             refreshCachedActiveSubscriptionInfoList();
2064 
2065             notifySubscriptionInfoChanged();
2066 
2067             return result;
2068         } finally {
2069             Binder.restoreCallingIdentity(identity);
2070         }
2071     }
2072 
syncGroupedSetting(int refSubId)2073     public void syncGroupedSetting(int refSubId) {
2074         logd("syncGroupedSetting");
2075         try (Cursor cursor = mContext.getContentResolver().query(
2076                 SubscriptionManager.CONTENT_URI, null,
2077                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
2078                 new String[] {String.valueOf(refSubId)}, null)) {
2079             if (cursor == null || !cursor.moveToFirst()) {
2080                 logd("[syncGroupedSetting] failed. Can't find refSubId " + refSubId);
2081                 return;
2082             }
2083 
2084             ContentValues values = new ContentValues(GROUP_SHARING_PROPERTIES.size());
2085             for (String propKey : GROUP_SHARING_PROPERTIES) {
2086                 copyDataFromCursorToContentValue(propKey, cursor, values);
2087             }
2088             updateDatabase(values, refSubId, true);
2089         }
2090     }
2091 
copyDataFromCursorToContentValue(String propKey, Cursor cursor, ContentValues values)2092     private void copyDataFromCursorToContentValue(String propKey, Cursor cursor,
2093             ContentValues values) {
2094         int columnIndex = cursor.getColumnIndex(propKey);
2095         if (columnIndex == -1) {
2096             logd("[copyDataFromCursorToContentValue] can't find column " + propKey);
2097             return;
2098         }
2099 
2100         switch (propKey) {
2101             case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
2102             case SubscriptionManager.VT_IMS_ENABLED:
2103             case SubscriptionManager.WFC_IMS_ENABLED:
2104             case SubscriptionManager.WFC_IMS_MODE:
2105             case SubscriptionManager.WFC_IMS_ROAMING_MODE:
2106             case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
2107             case SubscriptionManager.DATA_ROAMING:
2108             case SubscriptionManager.IMS_RCS_UCE_ENABLED:
2109                 values.put(propKey, cursor.getInt(columnIndex));
2110                 break;
2111             case SubscriptionManager.DISPLAY_NAME:
2112             case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
2113                 values.put(propKey, cursor.getString(columnIndex));
2114                 break;
2115             default:
2116                 loge("[copyDataFromCursorToContentValue] invalid propKey " + propKey);
2117         }
2118     }
2119 
2120     // TODO: replace all updates with this helper method.
updateDatabase(ContentValues value, int subId, boolean updateEntireGroup)2121     private int updateDatabase(ContentValues value, int subId, boolean updateEntireGroup) {
2122         List<SubscriptionInfo> infoList = getSubscriptionsInGroup(getGroupUuid(subId),
2123                 mContext.getOpPackageName(), mContext.getAttributionTag());
2124         if (!updateEntireGroup || infoList == null || infoList.size() == 0) {
2125             // Only update specified subscriptions.
2126             return mContext.getContentResolver().update(
2127                     SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2128         } else {
2129             // Update all subscriptions in the same group.
2130             int[] subIdList = new int[infoList.size()];
2131             for (int i = 0; i < infoList.size(); i++) {
2132                 subIdList[i] = infoList.get(i).getSubscriptionId();
2133             }
2134             return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
2135                     value, getSelectionForSubIdList(subIdList), null);
2136         }
2137     }
2138 
2139     /**
2140      * Set carrier id by subId
2141      * @param carrierId the subscription carrier id.
2142      * @param subId the unique SubInfoRecord index in database
2143      * @return the number of records updated
2144      *
2145      * @see TelephonyManager#getSimCarrierId()
2146      */
setCarrierId(int carrierId, int subId)2147     public int setCarrierId(int carrierId, int subId) {
2148         if (DBG) logd("[setCarrierId]+ carrierId:" + carrierId + " subId:" + subId);
2149 
2150         enforceModifyPhoneState("setCarrierId");
2151 
2152         // Now that all security checks passes, perform the operation as ourselves.
2153         final long identity = Binder.clearCallingIdentity();
2154         try {
2155             validateSubId(subId);
2156             ContentValues value = new ContentValues(1);
2157             value.put(SubscriptionManager.CARRIER_ID, carrierId);
2158             int result = mContext.getContentResolver().update(
2159                     SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2160 
2161             // Refresh the Cache of Active Subscription Info List
2162             refreshCachedActiveSubscriptionInfoList();
2163 
2164             notifySubscriptionInfoChanged();
2165 
2166             return result;
2167         } finally {
2168             Binder.restoreCallingIdentity(identity);
2169         }
2170     }
2171 
2172     /**
2173      * Set MCC/MNC by subscription ID
2174      * @param mccMnc MCC/MNC associated with the subscription
2175      * @param subId the unique SubInfoRecord index in database
2176      * @return the number of records updated
2177      */
setMccMnc(String mccMnc, int subId)2178     public int setMccMnc(String mccMnc, int subId) {
2179         String mccString = mccMnc.substring(0, 3);
2180         String mncString = mccMnc.substring(3);
2181         int mcc = 0;
2182         int mnc = 0;
2183         try {
2184             mcc = Integer.parseInt(mccString);
2185             mnc = Integer.parseInt(mncString);
2186         } catch (NumberFormatException e) {
2187             loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
2188         }
2189         if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
2190         ContentValues value = new ContentValues(4);
2191         value.put(SubscriptionManager.MCC, mcc);
2192         value.put(SubscriptionManager.MNC, mnc);
2193         value.put(SubscriptionManager.MCC_STRING, mccString);
2194         value.put(SubscriptionManager.MNC_STRING, mncString);
2195 
2196         int result = mContext.getContentResolver().update(
2197                 SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2198 
2199         // Refresh the Cache of Active Subscription Info List
2200         refreshCachedActiveSubscriptionInfoList();
2201 
2202         notifySubscriptionInfoChanged();
2203 
2204         return result;
2205     }
2206 
2207     /**
2208      * Set IMSI by subscription ID
2209      * @param imsi IMSI (International Mobile Subscriber Identity)
2210      * @return the number of records updated
2211      */
setImsi(String imsi, int subId)2212     public int setImsi(String imsi, int subId) {
2213         if (DBG) logd("[setImsi]+ imsi:" + imsi + " subId:" + subId);
2214         ContentValues value = new ContentValues(1);
2215         value.put(SubscriptionManager.IMSI, imsi);
2216 
2217         int result = mContext.getContentResolver().update(
2218                 SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2219 
2220         // Refresh the Cache of Active Subscription Info List
2221         refreshCachedActiveSubscriptionInfoList();
2222 
2223         notifySubscriptionInfoChanged();
2224 
2225         return result;
2226     }
2227 
2228     /**
2229      * Set uicc applications being enabled or disabled.
2230      * @param enabled whether uicc applications are enabled or disabled.
2231      * @return the number of records updated
2232      */
setUiccApplicationsEnabled(boolean enabled, int subId)2233     public int setUiccApplicationsEnabled(boolean enabled, int subId) {
2234         if (DBG) logd("[setUiccApplicationsEnabled]+ enabled:" + enabled + " subId:" + subId);
2235 
2236         enforceModifyPhoneState("setUiccApplicationsEnabled");
2237 
2238         long identity = Binder.clearCallingIdentity();
2239         try {
2240             ContentValues value = new ContentValues(1);
2241             value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, enabled);
2242 
2243             int result = mContext.getContentResolver().update(
2244                     SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2245 
2246             // Refresh the Cache of Active Subscription Info List
2247             refreshCachedActiveSubscriptionInfoList();
2248 
2249             notifyUiccAppsEnableChanged();
2250             notifySubscriptionInfoChanged();
2251 
2252             return result;
2253         } finally {
2254             Binder.restoreCallingIdentity(identity);
2255         }
2256     }
2257 
2258     /**
2259      * Register to change of uicc applications enablement changes.
2260      * @param notifyNow whether to notify target upon registration.
2261      */
registerForUiccAppsEnabled(Handler handler, int what, Object object, boolean notifyNow)2262     public void registerForUiccAppsEnabled(Handler handler, int what, Object object,
2263             boolean notifyNow) {
2264         mUiccAppsEnableChangeRegList.addUnique(handler, what, object);
2265         if (notifyNow) {
2266             handler.obtainMessage(what, object).sendToTarget();
2267         }
2268     }
2269 
2270     /**
2271      * Unregister to change of uicc applications enablement changes.
2272      */
unregisterForUiccAppsEnabled(Handler handler)2273     public void unregisterForUiccAppsEnabled(Handler handler) {
2274         mUiccAppsEnableChangeRegList.remove(handler);
2275     }
2276 
notifyUiccAppsEnableChanged()2277     private void notifyUiccAppsEnableChanged() {
2278         mUiccAppsEnableChangeRegList.notifyRegistrants();
2279     }
2280 
2281     /**
2282      * Get IMSI by subscription ID
2283      * For active subIds, this will always return the corresponding imsi
2284      * For inactive subIds, once they are activated once, even if they are deactivated at the time
2285      *   of calling this function, the corresponding imsi will be returned
2286      * When calling this method, the permission check should have already been done to allow
2287      *   only privileged read
2288      *
2289      * @return imsi
2290      */
getImsiPrivileged(int subId)2291     public String getImsiPrivileged(int subId) {
2292         try (Cursor cursor = mContext.getContentResolver().query(
2293                 SubscriptionManager.CONTENT_URI, null,
2294                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
2295                 new String[] {String.valueOf(subId)}, null)) {
2296             String imsi = null;
2297             if (cursor != null) {
2298                 if (cursor.moveToNext()) {
2299                     imsi = getOptionalStringFromCursor(cursor, SubscriptionManager.IMSI,
2300                             /*defaultVal*/ null);
2301                 }
2302             } else {
2303                 logd("getImsiPrivileged: failed to retrieve imsi.");
2304             }
2305 
2306             return imsi;
2307         }
2308     }
2309 
2310     /**
2311      * Set ISO country code by subscription ID
2312      * @param iso iso country code associated with the subscription
2313      * @param subId the unique SubInfoRecord index in database
2314      * @return the number of records updated
2315      */
setCountryIso(String iso, int subId)2316     public int setCountryIso(String iso, int subId) {
2317         if (DBG) logd("[setCountryIso]+ iso:" + iso + " subId:" + subId);
2318         ContentValues value = new ContentValues();
2319         value.put(SubscriptionManager.ISO_COUNTRY_CODE, iso);
2320 
2321         int result = mContext.getContentResolver().update(
2322                 SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
2323 
2324         // Refresh the Cache of Active Subscription Info List
2325         refreshCachedActiveSubscriptionInfoList();
2326 
2327         notifySubscriptionInfoChanged();
2328         return result;
2329     }
2330 
2331     @Override
getSlotIndex(int subId)2332     public int getSlotIndex(int subId) {
2333         if (VDBG) printStackTrace("[getSlotIndex] subId=" + subId);
2334 
2335         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2336             subId = getDefaultSubId();
2337         }
2338         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2339             if (DBG) logd("[getSlotIndex]- subId invalid");
2340             return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
2341         }
2342 
2343         int size = sSlotIndexToSubIds.size();
2344 
2345         if (size == 0) {
2346             if (DBG) logd("[getSlotIndex]- size == 0, return SIM_NOT_INSERTED instead");
2347             return SubscriptionManager.SIM_NOT_INSERTED;
2348         }
2349 
2350         for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
2351             int sim = entry.getKey();
2352             ArrayList<Integer> subs = entry.getValue();
2353 
2354             if (subs != null && subs.contains(subId)) {
2355                 if (VDBG) logv("[getSlotIndex]- return = " + sim);
2356                 return sim;
2357             }
2358         }
2359 
2360         if (DBG) logd("[getSlotIndex]- return fail");
2361         return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
2362     }
2363 
2364     /**
2365      * Return the subId for specified slot Id.
2366      * @deprecated
2367      */
2368     @UnsupportedAppUsage
2369     @Override
2370     @Deprecated
getSubId(int slotIndex)2371     public int[] getSubId(int slotIndex) {
2372         if (VDBG) printStackTrace("[getSubId]+ slotIndex=" + slotIndex);
2373 
2374         // Map default slotIndex to the current default subId.
2375         // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous
2376         // as a slot maybe used for multiple different type of "connections"
2377         // such as: voice, data and sms. But we're doing the best we can and using
2378         // getDefaultSubId which makes a best guess.
2379         if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
2380             slotIndex = getSlotIndex(getDefaultSubId());
2381             if (VDBG) logd("[getSubId] map default slotIndex=" + slotIndex);
2382         }
2383 
2384         // Check that we have a valid slotIndex or the slotIndex is for a remote SIM (remote SIM
2385         // uses special slot index that may be invalid otherwise)
2386         if (!SubscriptionManager.isValidSlotIndex(slotIndex)
2387                 && slotIndex != SubscriptionManager.SLOT_INDEX_FOR_REMOTE_SIM_SUB) {
2388             if (DBG) logd("[getSubId]- invalid slotIndex=" + slotIndex);
2389             return null;
2390         }
2391 
2392         // Check if we've got any SubscriptionInfo records using slotIndexToSubId as a surrogate.
2393         int size = sSlotIndexToSubIds.size();
2394         if (size == 0) {
2395             if (VDBG) {
2396                 logd("[getSubId]- sSlotIndexToSubIds.size == 0, return null slotIndex="
2397                         + slotIndex);
2398             }
2399             return null;
2400         }
2401 
2402         // Convert ArrayList to array
2403         ArrayList<Integer> subIds = sSlotIndexToSubIds.getCopy(slotIndex);
2404         if (subIds != null && subIds.size() > 0) {
2405             int[] subIdArr = new int[subIds.size()];
2406             for (int i = 0; i < subIds.size(); i++) {
2407                 subIdArr[i] = subIds.get(i);
2408             }
2409             if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr);
2410             return subIdArr;
2411         } else {
2412             if (DBG) logd("[getSubId]- numSubIds == 0, return null slotIndex=" + slotIndex);
2413             return null;
2414         }
2415     }
2416 
2417     @UnsupportedAppUsage
2418     @Override
getPhoneId(int subId)2419     public int getPhoneId(int subId) {
2420         if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
2421         int phoneId;
2422 
2423         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2424             subId = getDefaultSubId();
2425             if (DBG) logd("[getPhoneId] asked for default subId=" + subId);
2426         }
2427 
2428         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2429             if (VDBG) {
2430                 logdl("[getPhoneId]- invalid subId return="
2431                         + SubscriptionManager.INVALID_PHONE_INDEX);
2432             }
2433             return SubscriptionManager.INVALID_PHONE_INDEX;
2434         }
2435 
2436         int size = sSlotIndexToSubIds.size();
2437         if (size == 0) {
2438             phoneId = mDefaultPhoneId;
2439             if (VDBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
2440             return phoneId;
2441         }
2442 
2443         // FIXME: Assumes phoneId == slotIndex
2444         for (Entry<Integer, ArrayList<Integer>> entry: sSlotIndexToSubIds.entrySet()) {
2445             int sim = entry.getKey();
2446             ArrayList<Integer> subs = entry.getValue();
2447 
2448             if (subs != null && subs.contains(subId)) {
2449                 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
2450                 return sim;
2451             }
2452         }
2453 
2454         phoneId = mDefaultPhoneId;
2455         if (VDBG) {
2456             logd("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId);
2457         }
2458         return phoneId;
2459 
2460     }
2461 
2462     /**
2463      * @return the number of records cleared
2464      */
2465     @Override
clearSubInfo()2466     public int clearSubInfo() {
2467         enforceModifyPhoneState("clearSubInfo");
2468 
2469         // Now that all security checks passes, perform the operation as ourselves.
2470         final long identity = Binder.clearCallingIdentity();
2471         try {
2472             int size = sSlotIndexToSubIds.size();
2473 
2474             if (size == 0) {
2475                 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
2476                 return 0;
2477             }
2478 
2479             sSlotIndexToSubIds.clear();
2480             if (DBG) logdl("[clearSubInfo]- clear size=" + size);
2481             return size;
2482         } finally {
2483             Binder.restoreCallingIdentity(identity);
2484         }
2485     }
2486 
logvl(String msg)2487     private void logvl(String msg) {
2488         logv(msg);
2489         mLocalLog.log(msg);
2490     }
2491 
logv(String msg)2492     private void logv(String msg) {
2493         Rlog.v(LOG_TAG, msg);
2494     }
2495 
2496     @UnsupportedAppUsage
logdl(String msg)2497     protected void logdl(String msg) {
2498         logd(msg);
2499         mLocalLog.log(msg);
2500     }
2501 
2502     @UnsupportedAppUsage
logd(String msg)2503     private void logd(String msg) {
2504         Rlog.d(LOG_TAG, msg);
2505     }
2506 
logel(String msg)2507     private void logel(String msg) {
2508         loge(msg);
2509         mLocalLog.log(msg);
2510     }
2511 
2512     @UnsupportedAppUsage
loge(String msg)2513     private void loge(String msg) {
2514         Rlog.e(LOG_TAG, msg);
2515     }
2516 
2517     @UnsupportedAppUsage
2518     @Override
getDefaultSubId()2519     public int getDefaultSubId() {
2520         int subId;
2521         boolean isVoiceCapable = mTelephonyManager.isVoiceCapable();
2522         if (isVoiceCapable) {
2523             subId = getDefaultVoiceSubId();
2524             if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId);
2525         } else {
2526             subId = getDefaultDataSubId();
2527             if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId);
2528         }
2529         if (!isActiveSubId(subId)) {
2530             subId = sDefaultFallbackSubId.get();
2531             if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId);
2532         }
2533         if (VDBG) logv("[getDefaultSubId]- value = " + subId);
2534         return subId;
2535     }
2536 
2537     @UnsupportedAppUsage
2538     @Override
setDefaultSmsSubId(int subId)2539     public void setDefaultSmsSubId(int subId) {
2540         enforceModifyPhoneState("setDefaultSmsSubId");
2541 
2542         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2543             throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
2544         }
2545         if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId);
2546         setGlobalSetting(Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId);
2547         broadcastDefaultSmsSubIdChanged(subId);
2548     }
2549 
broadcastDefaultSmsSubIdChanged(int subId)2550     private void broadcastDefaultSmsSubIdChanged(int subId) {
2551         // Broadcast an Intent for default sms sub change
2552         if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
2553         Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
2554         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
2555         SubscriptionManager.putSubscriptionIdExtra(intent, subId);
2556         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2557     }
2558 
2559     @UnsupportedAppUsage
2560     @Override
getDefaultSmsSubId()2561     public int getDefaultSmsSubId() {
2562         int subId = Settings.Global.getInt(mContext.getContentResolver(),
2563                 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
2564                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
2565         if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId);
2566         return subId;
2567     }
2568 
2569     @UnsupportedAppUsage
2570     @Override
setDefaultVoiceSubId(int subId)2571     public void setDefaultVoiceSubId(int subId) {
2572         enforceModifyPhoneState("setDefaultVoiceSubId");
2573 
2574         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2575             throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
2576         }
2577         if (DBG) logdl("[setDefaultVoiceSubId] subId=" + subId);
2578 
2579         int previousDefaultSub = getDefaultSubId();
2580 
2581         setGlobalSetting(Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId);
2582         broadcastDefaultVoiceSubIdChanged(subId);
2583 
2584         PhoneAccountHandle newHandle =
2585                 subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
2586                         ? null : mTelephonyManager.getPhoneAccountHandleForSubscriptionId(
2587                         subId);
2588 
2589         TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
2590         PhoneAccountHandle currentHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
2591 
2592         if (!Objects.equals(currentHandle, newHandle)) {
2593             telecomManager.setUserSelectedOutgoingPhoneAccount(newHandle);
2594             logd("[setDefaultVoiceSubId] change to phoneAccountHandle=" + newHandle);
2595         } else {
2596             logd("[setDefaultVoiceSubId] default phone account not changed");
2597         }
2598 
2599         if (previousDefaultSub != getDefaultSubId()) {
2600             sendDefaultChangedBroadcast(getDefaultSubId());
2601         }
2602     }
2603 
2604     /**
2605      * Broadcast intent of ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED.
2606      * @hide
2607      */
2608     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
broadcastDefaultVoiceSubIdChanged(int subId)2609     public void broadcastDefaultVoiceSubIdChanged(int subId) {
2610         // Broadcast an Intent for default voice sub change
2611         if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
2612         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
2613         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
2614         SubscriptionManager.putSubscriptionIdExtra(intent, subId);
2615         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2616     }
2617 
2618     @UnsupportedAppUsage
2619     @Override
getDefaultVoiceSubId()2620     public int getDefaultVoiceSubId() {
2621         int subId = Settings.Global.getInt(mContext.getContentResolver(),
2622                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
2623                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
2624         if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId);
2625         return subId;
2626     }
2627 
2628     @UnsupportedAppUsage
2629     @Override
getDefaultDataSubId()2630     public int getDefaultDataSubId() {
2631         int subId = Settings.Global.getInt(mContext.getContentResolver(),
2632                 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
2633                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
2634         if (VDBG) logd("[getDefaultDataSubId] subId=" + subId);
2635         return subId;
2636     }
2637 
2638     @UnsupportedAppUsage
2639     @Override
setDefaultDataSubId(int subId)2640     public void setDefaultDataSubId(int subId) {
2641         enforceModifyPhoneState("setDefaultDataSubId");
2642 
2643         final long identity = Binder.clearCallingIdentity();
2644         try {
2645             if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2646                 throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
2647             }
2648 
2649             ProxyController proxyController = ProxyController.getInstance();
2650             int len = TelephonyManager.from(mContext).getActiveModemCount();
2651             logdl("[setDefaultDataSubId] num phones=" + len + ", subId=" + subId);
2652 
2653             if (SubscriptionManager.isValidSubscriptionId(subId)) {
2654                 // Only re-map modems if the new default data sub is valid
2655                 RadioAccessFamily[] rafs = new RadioAccessFamily[len];
2656                 boolean atLeastOneMatch = false;
2657                 for (int phoneId = 0; phoneId < len; phoneId++) {
2658                     Phone phone = PhoneFactory.getPhone(phoneId);
2659                     int raf;
2660                     int id = phone.getSubId();
2661                     if (id == subId) {
2662                         // TODO Handle the general case of N modems and M subscriptions.
2663                         raf = proxyController.getMaxRafSupported();
2664                         atLeastOneMatch = true;
2665                     } else {
2666                         // TODO Handle the general case of N modems and M subscriptions.
2667                         raf = proxyController.getMinRafSupported();
2668                     }
2669                     logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF="
2670                             + raf);
2671                     rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
2672                 }
2673                 if (atLeastOneMatch) {
2674                     proxyController.setRadioCapability(rafs);
2675                 } else {
2676                     if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating.");
2677                 }
2678             }
2679 
2680             int previousDefaultSub = getDefaultSubId();
2681             setGlobalSetting(Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId);
2682             MultiSimSettingController.getInstance().notifyDefaultDataSubChanged();
2683             broadcastDefaultDataSubIdChanged(subId);
2684             if (previousDefaultSub != getDefaultSubId()) {
2685                 sendDefaultChangedBroadcast(getDefaultSubId());
2686             }
2687         } finally {
2688             Binder.restoreCallingIdentity(identity);
2689         }
2690     }
2691 
2692     @UnsupportedAppUsage
broadcastDefaultDataSubIdChanged(int subId)2693     private void broadcastDefaultDataSubIdChanged(int subId) {
2694         // Broadcast an Intent for default data sub change
2695         if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
2696         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
2697         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
2698         SubscriptionManager.putSubscriptionIdExtra(intent, subId);
2699         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2700     }
2701 
2702     /* Sets the default subscription. If only one sub is active that
2703      * sub is set as default subId. If two or more  sub's are active
2704      * the first sub is set as default subscription
2705      */
2706     @UnsupportedAppUsage
setDefaultFallbackSubId(int subId, int subscriptionType)2707     protected void setDefaultFallbackSubId(int subId, int subscriptionType) {
2708         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2709             throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
2710         }
2711         if (DBG) {
2712             logdl("[setDefaultFallbackSubId] subId=" + subId + ", subscriptionType="
2713                     + subscriptionType);
2714         }
2715         int previousDefaultSub = getDefaultSubId();
2716         if (isSubscriptionForRemoteSim(subscriptionType)) {
2717             sDefaultFallbackSubId.set(subId);
2718             return;
2719         }
2720         if (SubscriptionManager.isValidSubscriptionId(subId)) {
2721             int phoneId = getPhoneId(subId);
2722             if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount()
2723                     || mTelephonyManager.getSimCount() == 1)) {
2724                 if (DBG) logdl("[setDefaultFallbackSubId] set sDefaultFallbackSubId=" + subId);
2725                 sDefaultFallbackSubId.set(subId);
2726                 // Update MCC MNC device configuration information
2727                 String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId);
2728                 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc);
2729             } else {
2730                 if (DBG) {
2731                     logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId
2732                             + " subId=" + subId);
2733                 }
2734             }
2735         }
2736         if (previousDefaultSub != getDefaultSubId()) {
2737             sendDefaultChangedBroadcast(getDefaultSubId());
2738         }
2739     }
2740 
sendDefaultChangedBroadcast(int subId)2741     public void sendDefaultChangedBroadcast(int subId) {
2742         // Broadcast an Intent for default sub change
2743         int phoneId = SubscriptionManager.getPhoneId(subId);
2744         Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
2745         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
2746         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
2747         if (DBG) {
2748             logdl("[sendDefaultChangedBroadcast] broadcast default subId changed phoneId="
2749                     + phoneId + " subId=" + subId);
2750         }
2751         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2752     }
2753 
2754     /**
2755      * Whether a subscription is opportunistic or not.
2756      */
isOpportunistic(int subId)2757     public boolean isOpportunistic(int subId) {
2758         SubscriptionInfo info = getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
2759                 mContext.getAttributionTag());
2760         return (info != null) && info.isOpportunistic();
2761     }
2762 
2763     // FIXME: We need we should not be assuming phoneId == slotIndex as it will not be true
2764     // when there are multiple subscriptions per sim and probably for other reasons.
2765     @UnsupportedAppUsage
getSubIdUsingPhoneId(int phoneId)2766     public int getSubIdUsingPhoneId(int phoneId) {
2767         int[] subIds = getSubId(phoneId);
2768         if (subIds == null || subIds.length == 0) {
2769             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
2770         }
2771         return subIds[0];
2772     }
2773 
2774     /** Must be public for access from instrumentation tests. */
2775     @VisibleForTesting
getSubInfoUsingSlotIndexPrivileged(int slotIndex)2776     public List<SubscriptionInfo> getSubInfoUsingSlotIndexPrivileged(int slotIndex) {
2777         if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]+ slotIndex:" + slotIndex);
2778         if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
2779             slotIndex = getSlotIndex(getDefaultSubId());
2780         }
2781         if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
2782             if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- invalid slotIndex");
2783             return null;
2784         }
2785 
2786         Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
2787                 null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
2788                 new String[]{String.valueOf(slotIndex)}, null);
2789         ArrayList<SubscriptionInfo> subList = null;
2790         try {
2791             if (cursor != null) {
2792                 while (cursor.moveToNext()) {
2793                     SubscriptionInfo subInfo = getSubInfoRecord(cursor);
2794                     if (subInfo != null) {
2795                         if (subList == null) {
2796                             subList = new ArrayList<SubscriptionInfo>();
2797                         }
2798                         subList.add(subInfo);
2799                     }
2800                 }
2801             }
2802         } finally {
2803             if (cursor != null) {
2804                 cursor.close();
2805             }
2806         }
2807         if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return");
2808 
2809         return subList;
2810     }
2811 
2812     @UnsupportedAppUsage
validateSubId(int subId)2813     private void validateSubId(int subId) {
2814         if (DBG) logd("validateSubId subId: " + subId);
2815         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2816             throw new RuntimeException("Invalid sub id passed as parameter");
2817         } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
2818             throw new RuntimeException("Default sub id passed as parameter");
2819         }
2820     }
2821 
getActiveSubIdArrayList()2822     private synchronized ArrayList<Integer> getActiveSubIdArrayList() {
2823         // Clone the sub id list so it can't change out from under us while iterating
2824         List<Entry<Integer, ArrayList<Integer>>> simInfoList =
2825                 new ArrayList<>(sSlotIndexToSubIds.entrySet());
2826 
2827         // Put the set of sub ids in slot index order
2828         Collections.sort(simInfoList, (x, y) -> x.getKey().compareTo(y.getKey()));
2829 
2830         // Collect the sub ids for each slot in turn
2831         ArrayList<Integer> allSubs = new ArrayList<>();
2832         for (Entry<Integer, ArrayList<Integer>> slot : simInfoList) {
2833             allSubs.addAll(slot.getValue());
2834         }
2835         return allSubs;
2836     }
2837 
isSubscriptionVisible(int subId)2838     private boolean isSubscriptionVisible(int subId) {
2839         synchronized (mSubInfoListLock) {
2840             for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
2841                 if (info.getSubscriptionId() == subId) {
2842                     // If group UUID is null, it's stand alone opportunistic profile. So it's
2843                     // visible. Otherwise, it's bundled opportunistic profile, and is not visible.
2844                     return info.getGroupUuid() == null;
2845                 }
2846             }
2847         }
2848 
2849         return true;
2850     }
2851 
2852     /**
2853      * @return the list of subId's that are active, is never null but the length maybe 0.
2854      */
2855     @Override
getActiveSubIdList(boolean visibleOnly)2856     public int[] getActiveSubIdList(boolean visibleOnly) {
2857         List<Integer> allSubs = getActiveSubIdArrayList();
2858 
2859         if (visibleOnly) {
2860             // Grouped opportunistic subscriptions should be hidden.
2861             allSubs = allSubs.stream().filter(subId -> isSubscriptionVisible(subId))
2862                     .collect(Collectors.toList());
2863         }
2864 
2865         int[] subIdArr = new int[allSubs.size()];
2866         int i = 0;
2867         for (int sub : allSubs) {
2868             subIdArr[i] = sub;
2869             i++;
2870         }
2871 
2872         if (VDBG) {
2873             logdl("[getActiveSubIdList] allSubs=" + allSubs + " subIdArr.length="
2874                     + subIdArr.length);
2875         }
2876         return subIdArr;
2877     }
2878 
2879     @Override
isActiveSubId(int subId, String callingPackage, String callingFeatureId)2880     public boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId) {
2881         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
2882                 callingFeatureId, "isActiveSubId")) {
2883             throw new SecurityException("Requires READ_PHONE_STATE permission.");
2884         }
2885         final long identity = Binder.clearCallingIdentity();
2886         try {
2887             return isActiveSubId(subId);
2888         } finally {
2889             Binder.restoreCallingIdentity(identity);
2890         }
2891     }
2892 
2893     @UnsupportedAppUsage
2894     @Deprecated // This should be moved into isActiveSubId(int, String)
isActiveSubId(int subId)2895     public boolean isActiveSubId(int subId) {
2896         boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
2897                 && getActiveSubIdArrayList().contains(subId);
2898 
2899         if (VDBG) logdl("[isActiveSubId]- " + retVal);
2900         return retVal;
2901     }
2902 
2903     /**
2904      * Get the SIM state for the slot index.
2905      * For Remote-SIMs, this method returns {@link #IccCardConstants.State.UNKNOWN}
2906      * @return SIM state as the ordinal of {@See IccCardConstants.State}
2907      */
2908     @Override
getSimStateForSlotIndex(int slotIndex)2909     public int getSimStateForSlotIndex(int slotIndex) {
2910         State simState;
2911         String err;
2912         if (slotIndex < 0) {
2913             simState = IccCardConstants.State.UNKNOWN;
2914             err = "invalid slotIndex";
2915         } else {
2916             Phone phone = null;
2917             try {
2918                 phone = PhoneFactory.getPhone(slotIndex);
2919             } catch (IllegalStateException e) {
2920                 // ignore
2921             }
2922             if (phone == null) {
2923                 simState = IccCardConstants.State.UNKNOWN;
2924                 err = "phone == null";
2925             } else {
2926                 IccCard icc = phone.getIccCard();
2927                 if (icc == null) {
2928                     simState = IccCardConstants.State.UNKNOWN;
2929                     err = "icc == null";
2930                 } else {
2931                     simState = icc.getState();
2932                     err = "";
2933                 }
2934             }
2935         }
2936         if (VDBG) {
2937             logd("getSimStateForSlotIndex: " + err + " simState=" + simState
2938                     + " ordinal=" + simState.ordinal() + " slotIndex=" + slotIndex);
2939         }
2940         return simState.ordinal();
2941     }
2942 
2943     /**
2944      * Store properties associated with SubscriptionInfo in database
2945      * @param subId Subscription Id of Subscription
2946      * @param propKey Column name in database associated with SubscriptionInfo
2947      * @param propValue Value to store in DB for particular subId & column name
2948      *
2949      * @return number of rows updated.
2950      * @hide
2951      */
2952     @Override
setSubscriptionProperty(int subId, String propKey, String propValue)2953     public int setSubscriptionProperty(int subId, String propKey, String propValue) {
2954         enforceModifyPhoneState("setSubscriptionProperty");
2955         final long token = Binder.clearCallingIdentity();
2956 
2957         try {
2958             validateSubId(subId);
2959             ContentResolver resolver = mContext.getContentResolver();
2960             int result = setSubscriptionPropertyIntoContentResolver(
2961                     subId, propKey, propValue, resolver);
2962             // Refresh the Cache of Active Subscription Info List
2963             refreshCachedActiveSubscriptionInfoList();
2964 
2965             return result;
2966         } finally {
2967             Binder.restoreCallingIdentity(token);
2968         }
2969     }
2970 
setSubscriptionPropertyIntoContentResolver( int subId, String propKey, String propValue, ContentResolver resolver)2971     private int setSubscriptionPropertyIntoContentResolver(
2972             int subId, String propKey, String propValue, ContentResolver resolver) {
2973         ContentValues value = new ContentValues();
2974         boolean updateEntireGroup = GROUP_SHARING_PROPERTIES.contains(propKey);
2975         switch (propKey) {
2976             case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
2977             case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
2978             case SubscriptionManager.CB_AMBER_ALERT:
2979             case SubscriptionManager.CB_EMERGENCY_ALERT:
2980             case SubscriptionManager.CB_ALERT_SOUND_DURATION:
2981             case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
2982             case SubscriptionManager.CB_ALERT_VIBRATE:
2983             case SubscriptionManager.CB_ALERT_SPEECH:
2984             case SubscriptionManager.CB_ETWS_TEST_ALERT:
2985             case SubscriptionManager.CB_CHANNEL_50_ALERT:
2986             case SubscriptionManager.CB_CMAS_TEST_ALERT:
2987             case SubscriptionManager.CB_OPT_OUT_DIALOG:
2988             case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
2989             case SubscriptionManager.IS_OPPORTUNISTIC:
2990             case SubscriptionManager.VT_IMS_ENABLED:
2991             case SubscriptionManager.WFC_IMS_ENABLED:
2992             case SubscriptionManager.WFC_IMS_MODE:
2993             case SubscriptionManager.WFC_IMS_ROAMING_MODE:
2994             case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
2995             case SubscriptionManager.IMS_RCS_UCE_ENABLED:
2996                 value.put(propKey, Integer.parseInt(propValue));
2997                 break;
2998             case SubscriptionManager.ALLOWED_NETWORK_TYPES:
2999                 value.put(propKey, Long.parseLong(propValue));
3000                 break;
3001             default:
3002                 if (DBG) logd("Invalid column name");
3003                 break;
3004         }
3005 
3006         return updateDatabase(value, subId, updateEntireGroup);
3007     }
3008 
3009     /**
3010      * Get properties associated with SubscriptionInfo from database
3011      *
3012      * @param subId Subscription Id of Subscription
3013      * @param propKey Column name in SubscriptionInfo database
3014      * @return Value associated with subId and propKey column in database
3015      */
3016     @Override
getSubscriptionProperty(int subId, String propKey, String callingPackage, String callingFeatureId)3017     public String getSubscriptionProperty(int subId, String propKey, String callingPackage,
3018             String callingFeatureId) {
3019         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
3020                 callingFeatureId, "getSubscriptionProperty")) {
3021             return null;
3022         }
3023 
3024         final long identity = Binder.clearCallingIdentity();
3025         try {
3026             return getSubscriptionProperty(subId, propKey);
3027         } finally {
3028             Binder.restoreCallingIdentity(identity);
3029         }
3030     }
3031 
3032     /**
3033      * Get properties associated with SubscriptionInfo from database. Note this is the version
3034      * without permission check for telephony internal use only.
3035      *
3036      * @param subId Subscription Id of Subscription
3037      * @param propKey Column name in SubscriptionInfo database
3038      * @return Value associated with subId and propKey column in database
3039      */
getSubscriptionProperty(int subId, String propKey)3040     public String getSubscriptionProperty(int subId, String propKey) {
3041         String resultValue = null;
3042         try (Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
3043                 new String[]{propKey},
3044                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
3045                 new String[]{subId + ""}, null)) {
3046             if (cursor != null) {
3047                 if (cursor.moveToFirst()) {
3048                     switch (propKey) {
3049                         case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
3050                         case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
3051                         case SubscriptionManager.CB_AMBER_ALERT:
3052                         case SubscriptionManager.CB_EMERGENCY_ALERT:
3053                         case SubscriptionManager.CB_ALERT_SOUND_DURATION:
3054                         case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
3055                         case SubscriptionManager.CB_ALERT_VIBRATE:
3056                         case SubscriptionManager.CB_ALERT_SPEECH:
3057                         case SubscriptionManager.CB_ETWS_TEST_ALERT:
3058                         case SubscriptionManager.CB_CHANNEL_50_ALERT:
3059                         case SubscriptionManager.CB_CMAS_TEST_ALERT:
3060                         case SubscriptionManager.CB_OPT_OUT_DIALOG:
3061                         case SubscriptionManager.ENHANCED_4G_MODE_ENABLED:
3062                         case SubscriptionManager.VT_IMS_ENABLED:
3063                         case SubscriptionManager.WFC_IMS_ENABLED:
3064                         case SubscriptionManager.WFC_IMS_MODE:
3065                         case SubscriptionManager.WFC_IMS_ROAMING_MODE:
3066                         case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
3067                         case SubscriptionManager.IMS_RCS_UCE_ENABLED:
3068                         case SubscriptionManager.IS_OPPORTUNISTIC:
3069                         case SubscriptionManager.GROUP_UUID:
3070                         case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
3071                         case SubscriptionManager.ALLOWED_NETWORK_TYPES:
3072                             resultValue = cursor.getString(0);
3073                             break;
3074                         default:
3075                             if(DBG) logd("Invalid column name");
3076                             break;
3077                     }
3078                 } else {
3079                     if(DBG) logd("Valid row not present in db");
3080                 }
3081             } else {
3082                 if(DBG) logd("Query failed");
3083             }
3084         }
3085 
3086         if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
3087         return resultValue;
3088     }
3089 
printStackTrace(String msg)3090     private void printStackTrace(String msg) {
3091         RuntimeException re = new RuntimeException();
3092         logd("StackTrace - " + msg);
3093         StackTraceElement[] st = re.getStackTrace();
3094         boolean first = true;
3095         for (StackTraceElement ste : st) {
3096             if (first) {
3097                 first = false;
3098             } else {
3099                 logd(ste.toString());
3100             }
3101         }
3102     }
3103 
3104     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)3105     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3106         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
3107                 "Requires DUMP");
3108         final long token = Binder.clearCallingIdentity();
3109         try {
3110             pw.println("SubscriptionController:");
3111             pw.println(" mLastISubServiceRegTime=" + mLastISubServiceRegTime);
3112             pw.println(" defaultSubId=" + getDefaultSubId());
3113             pw.println(" defaultDataSubId=" + getDefaultDataSubId());
3114             pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
3115             pw.println(" defaultSmsSubId=" + getDefaultSmsSubId());
3116 
3117             pw.println(" defaultDataPhoneId=" + SubscriptionManager
3118                     .from(mContext).getDefaultDataPhoneId());
3119             pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId());
3120             pw.println(" defaultSmsPhoneId=" + SubscriptionManager
3121                     .from(mContext).getDefaultSmsPhoneId());
3122             pw.flush();
3123 
3124             for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
3125                 pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subIds=" + entry);
3126             }
3127             pw.flush();
3128             pw.println("++++++++++++++++++++++++++++++++");
3129 
3130             List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(
3131                     mContext.getOpPackageName(), mContext.getAttributionTag());
3132             if (sirl != null) {
3133                 pw.println(" ActiveSubInfoList:");
3134                 for (SubscriptionInfo entry : sirl) {
3135                     pw.println("  " + entry.toString());
3136                 }
3137             } else {
3138                 pw.println(" ActiveSubInfoList: is null");
3139             }
3140             pw.flush();
3141             pw.println("++++++++++++++++++++++++++++++++");
3142 
3143             sirl = getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag());
3144             if (sirl != null) {
3145                 pw.println(" AllSubInfoList:");
3146                 for (SubscriptionInfo entry : sirl) {
3147                     pw.println("  " + entry.toString());
3148                 }
3149             } else {
3150                 pw.println(" AllSubInfoList: is null");
3151             }
3152             pw.flush();
3153             pw.println("++++++++++++++++++++++++++++++++");
3154 
3155             mLocalLog.dump(fd, pw, args);
3156             pw.flush();
3157             pw.println("++++++++++++++++++++++++++++++++");
3158             pw.flush();
3159         } finally {
3160             Binder.restoreCallingIdentity(token);
3161         }
3162     }
3163 
3164     /**
3165      * Migrating Ims settings from global setting to subscription DB, if not already done.
3166      */
3167     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
migrateImsSettings()3168     public void migrateImsSettings() {
3169         migrateImsSettingHelper(
3170                 Settings.Global.ENHANCED_4G_MODE_ENABLED,
3171                 SubscriptionManager.ENHANCED_4G_MODE_ENABLED);
3172         migrateImsSettingHelper(
3173                 Settings.Global.VT_IMS_ENABLED,
3174                 SubscriptionManager.VT_IMS_ENABLED);
3175         migrateImsSettingHelper(
3176                 Settings.Global.WFC_IMS_ENABLED,
3177                 SubscriptionManager.WFC_IMS_ENABLED);
3178         migrateImsSettingHelper(
3179                 Settings.Global.WFC_IMS_MODE,
3180                 SubscriptionManager.WFC_IMS_MODE);
3181         migrateImsSettingHelper(
3182                 Settings.Global.WFC_IMS_ROAMING_MODE,
3183                 SubscriptionManager.WFC_IMS_ROAMING_MODE);
3184         migrateImsSettingHelper(
3185                 Settings.Global.WFC_IMS_ROAMING_ENABLED,
3186                 SubscriptionManager.WFC_IMS_ROAMING_ENABLED);
3187     }
3188 
migrateImsSettingHelper(String settingGlobal, String subscriptionProperty)3189     private void migrateImsSettingHelper(String settingGlobal, String subscriptionProperty) {
3190         ContentResolver resolver = mContext.getContentResolver();
3191         int defaultSubId = getDefaultVoiceSubId();
3192         if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
3193             return;
3194         }
3195         try {
3196             int prevSetting = Settings.Global.getInt(resolver, settingGlobal);
3197 
3198             if (prevSetting != DEPRECATED_SETTING) {
3199                 // Write previous setting into Subscription DB.
3200                 setSubscriptionPropertyIntoContentResolver(defaultSubId, subscriptionProperty,
3201                         Integer.toString(prevSetting), resolver);
3202                 // Write global setting value with DEPRECATED_SETTING making sure
3203                 // migration only happen once.
3204                 Settings.Global.putInt(resolver, settingGlobal, DEPRECATED_SETTING);
3205             }
3206         } catch (Settings.SettingNotFoundException e) {
3207         }
3208     }
3209 
3210     /**
3211      * Set whether a subscription is opportunistic.
3212      *
3213      * Throws SecurityException if doesn't have required permission.
3214      *
3215      * @param opportunistic whether it’s opportunistic subscription.
3216      * @param subId the unique SubscriptionInfo index in database
3217      * @param callingPackage The package making the IPC.
3218      * @return the number of records updated
3219      */
3220     @Override
setOpportunistic(boolean opportunistic, int subId, String callingPackage)3221     public int setOpportunistic(boolean opportunistic, int subId, String callingPackage) {
3222         try {
3223             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
3224                     mContext, subId, callingPackage);
3225         } catch (SecurityException e) {
3226             // The subscription may be inactive eSIM profile. If so, check the access rule in
3227             // database.
3228             enforceCarrierPrivilegeOnInactiveSub(subId, callingPackage,
3229                     "Caller requires permission on sub " + subId);
3230         }
3231 
3232         long token = Binder.clearCallingIdentity();
3233         try {
3234             int ret = setSubscriptionProperty(subId, SubscriptionManager.IS_OPPORTUNISTIC,
3235                     String.valueOf(opportunistic ? 1 : 0));
3236 
3237             if (ret != 0) notifySubscriptionInfoChanged();
3238 
3239             return ret;
3240         } finally {
3241             Binder.restoreCallingIdentity(token);
3242         }
3243     }
3244 
3245     /**
3246      * Get subscription info from database, and check whether caller has carrier privilege
3247      * permission with it. If checking fails, throws SecurityException.
3248      */
enforceCarrierPrivilegeOnInactiveSub(int subId, String callingPackage, String message)3249     private void enforceCarrierPrivilegeOnInactiveSub(int subId, String callingPackage,
3250             String message) {
3251         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
3252 
3253         SubscriptionManager subManager = (SubscriptionManager)
3254                 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
3255         List<SubscriptionInfo> subInfo = getSubInfo(
3256                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
3257 
3258         try {
3259             if (!isActiveSubId(subId) && subInfo != null && subInfo.size() == 1
3260                     && subManager.canManageSubscription(subInfo.get(0), callingPackage)) {
3261                 return;
3262             }
3263             throw new SecurityException(message);
3264         } catch (IllegalArgumentException e) {
3265             // canManageSubscription will throw IllegalArgumentException if sub is not embedded
3266             // or package name is unknown. In this case, we also see it as permission check failure
3267             // and throw a SecurityException.
3268             throw new SecurityException(message);
3269         }
3270     }
3271 
3272     @Override
setPreferredDataSubscriptionId(int subId, boolean needValidation, ISetOpportunisticDataCallback callback)3273     public void setPreferredDataSubscriptionId(int subId, boolean needValidation,
3274             ISetOpportunisticDataCallback callback) {
3275         enforceModifyPhoneState("setPreferredDataSubscriptionId");
3276         final long token = Binder.clearCallingIdentity();
3277 
3278         try {
3279             PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
3280             if (phoneSwitcher == null) {
3281                 logd("Set preferred data sub: phoneSwitcher is null.");
3282                 AnomalyReporter.reportAnomaly(
3283                         UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
3284                         "Set preferred data sub: phoneSwitcher is null.");
3285                 if (callback != null) {
3286                     try {
3287                         callback.onComplete(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
3288                     } catch (RemoteException exception) {
3289                         logd("RemoteException " + exception);
3290                     }
3291                 }
3292                 return;
3293             }
3294 
3295             phoneSwitcher.trySetOpportunisticDataSubscription(subId, needValidation, callback);
3296         } finally {
3297             Binder.restoreCallingIdentity(token);
3298         }
3299     }
3300 
3301     @Override
getPreferredDataSubscriptionId()3302     public int getPreferredDataSubscriptionId() {
3303         enforceReadPrivilegedPhoneState("getPreferredDataSubscriptionId");
3304         final long token = Binder.clearCallingIdentity();
3305 
3306         try {
3307             PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
3308             if (phoneSwitcher == null) {
3309                 AnomalyReporter.reportAnomaly(
3310                         UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
3311                         "Get preferred data sub: phoneSwitcher is null.");
3312                 return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
3313             }
3314 
3315             return phoneSwitcher.getOpportunisticDataSubscriptionId();
3316         } finally {
3317             Binder.restoreCallingIdentity(token);
3318         }
3319     }
3320 
3321     @Override
getOpportunisticSubscriptions(String callingPackage, String callingFeatureId)3322     public List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage,
3323             String callingFeatureId) {
3324         return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId,
3325                 makeCacheListCopyWithLock(mCacheOpportunisticSubInfoList));
3326     }
3327 
3328     /**
3329      * Inform SubscriptionManager that subscriptions in the list are bundled
3330      * as a group. Typically it's a primary subscription and an opportunistic
3331      * subscription. It should only affect multi-SIM scenarios where primary
3332      * and opportunistic subscriptions can be activated together.
3333      * Being in the same group means they might be activated or deactivated
3334      * together, some of them may be invisible to the users, etc.
3335      *
3336      * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
3337      * permission or had carrier privilege permission on the subscriptions:
3338      * {@link TelephonyManager#hasCarrierPrivileges(int)} or
3339      * {@link SubscriptionManager#canManageSubscription(SubscriptionInfo)}
3340      *
3341      * @throws SecurityException if the caller doesn't meet the requirements
3342      *             outlined above.
3343      * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
3344      *
3345      * @param subIdList list of subId that will be in the same group
3346      * @return groupUUID a UUID assigned to the subscription group. It returns
3347      * null if fails.
3348      *
3349      */
3350     @Override
createSubscriptionGroup(int[] subIdList, String callingPackage)3351     public ParcelUuid createSubscriptionGroup(int[] subIdList, String callingPackage) {
3352         if (subIdList == null || subIdList.length == 0) {
3353             throw new IllegalArgumentException("Invalid subIdList " + subIdList);
3354         }
3355 
3356         // Makes sure calling package matches caller UID.
3357         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
3358         // If it doesn't have modify phone state permission, or carrier privilege permission,
3359         // a SecurityException will be thrown.
3360         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
3361                 != PERMISSION_GRANTED && !checkCarrierPrivilegeOnSubList(
3362                         subIdList, callingPackage)) {
3363             throw new SecurityException("CreateSubscriptionGroup needs MODIFY_PHONE_STATE or"
3364                     + " carrier privilege permission on all specified subscriptions");
3365         }
3366 
3367         long identity = Binder.clearCallingIdentity();
3368 
3369         try {
3370             // Generate a UUID.
3371             ParcelUuid groupUUID = new ParcelUuid(UUID.randomUUID());
3372 
3373             ContentValues value = new ContentValues();
3374             value.put(SubscriptionManager.GROUP_UUID, groupUUID.toString());
3375             value.put(SubscriptionManager.GROUP_OWNER, callingPackage);
3376             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
3377                     value, getSelectionForSubIdList(subIdList), null);
3378 
3379             if (DBG) logdl("createSubscriptionGroup update DB result: " + result);
3380 
3381             refreshCachedActiveSubscriptionInfoList();
3382 
3383             notifySubscriptionInfoChanged();
3384 
3385             MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUUID);
3386 
3387             return groupUUID;
3388         } finally {
3389             Binder.restoreCallingIdentity(identity);
3390         }
3391     }
3392 
getOwnerPackageOfSubGroup(ParcelUuid groupUuid)3393     private String getOwnerPackageOfSubGroup(ParcelUuid groupUuid) {
3394         if (groupUuid == null) return null;
3395 
3396         List<SubscriptionInfo> infoList = getSubInfo(SubscriptionManager.GROUP_UUID
3397                 + "=\'" + groupUuid.toString() + "\'", null);
3398 
3399         return ArrayUtils.isEmpty(infoList) ? null : infoList.get(0).getGroupOwner();
3400     }
3401 
3402     /**
3403      * @param groupUuid a UUID assigned to the subscription group.
3404      * @param callingPackage the package making the IPC.
3405      * @return if callingPackage has carrier privilege on sublist.
3406      *
3407      */
canPackageManageGroup(ParcelUuid groupUuid, String callingPackage)3408     public boolean canPackageManageGroup(ParcelUuid groupUuid, String callingPackage) {
3409         if (groupUuid == null) {
3410             throw new IllegalArgumentException("Invalid groupUuid");
3411         }
3412 
3413         if (TextUtils.isEmpty(callingPackage)) {
3414             throw new IllegalArgumentException("Empty callingPackage");
3415         }
3416 
3417         List<SubscriptionInfo> infoList;
3418 
3419         // Getting all subscriptions in the group.
3420         long identity = Binder.clearCallingIdentity();
3421         try {
3422             infoList = getSubInfo(SubscriptionManager.GROUP_UUID
3423                     + "=\'" + groupUuid.toString() + "\'", null);
3424         } finally {
3425             Binder.restoreCallingIdentity(identity);
3426         }
3427 
3428         // If the group does not exist, then by default the UUID is up for grabs so no need to
3429         // restrict management of a group (that someone may be attempting to create).
3430         if (ArrayUtils.isEmpty(infoList)) {
3431             return true;
3432         }
3433 
3434         // If the calling package is the group owner, skip carrier permission check and return
3435         // true as it was done before.
3436         if (callingPackage.equals(infoList.get(0).getGroupOwner())) return true;
3437 
3438         // Check carrier privilege for all subscriptions in the group.
3439         int[] subIdArray = infoList.stream().mapToInt(info -> info.getSubscriptionId())
3440                 .toArray();
3441         return (checkCarrierPrivilegeOnSubList(subIdArray, callingPackage));
3442     }
3443 
updateGroupOwner(ParcelUuid groupUuid, String groupOwner)3444     private int updateGroupOwner(ParcelUuid groupUuid, String groupOwner) {
3445         // If the existing group owner is different from current caller, make caller the new
3446         // owner of all subscriptions in group.
3447         // This is for use-case of:
3448         // 1) Both package1 and package2 has permission (MODIFY_PHONE_STATE or carrier
3449         // privilege permission) of all related subscriptions.
3450         // 2) Package 1 created a group.
3451         // 3) Package 2 wants to add a subscription into it.
3452         // Step 3 should be granted as all operations are permission based. Which means as
3453         // long as the package passes the permission check, it can modify the subscription
3454         // and the group. And package 2 becomes the new group owner as it's the last to pass
3455         // permission checks on all members.
3456         ContentValues value = new ContentValues(1);
3457         value.put(SubscriptionManager.GROUP_OWNER, groupOwner);
3458         return mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
3459                 value, SubscriptionManager.GROUP_UUID + "=\"" + groupUuid + "\"", null);
3460     }
3461 
3462     @Override
addSubscriptionsIntoGroup(int[] subIdList, ParcelUuid groupUuid, String callingPackage)3463     public void addSubscriptionsIntoGroup(int[] subIdList, ParcelUuid groupUuid,
3464             String callingPackage) {
3465         if (subIdList == null || subIdList.length == 0) {
3466             throw new IllegalArgumentException("Invalid subId list");
3467         }
3468 
3469         if (groupUuid == null || groupUuid.equals(INVALID_GROUP_UUID)) {
3470             throw new IllegalArgumentException("Invalid groupUuid");
3471         }
3472 
3473         // Makes sure calling package matches caller UID.
3474         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
3475         // If it doesn't have modify phone state permission, or carrier privilege permission,
3476         // a SecurityException will be thrown.
3477         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
3478                 != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
3479                 && canPackageManageGroup(groupUuid, callingPackage))) {
3480             throw new SecurityException("Requires MODIFY_PHONE_STATE or carrier privilege"
3481                     + " permissions on subscriptions and the group.");
3482         }
3483 
3484         long identity = Binder.clearCallingIdentity();
3485 
3486         try {
3487             if (DBG) {
3488                 logdl("addSubscriptionsIntoGroup sub list "
3489                         + Arrays.toString(subIdList) + " into group " + groupUuid);
3490             }
3491 
3492             ContentValues value = new ContentValues();
3493             value.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
3494             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
3495                     value, getSelectionForSubIdList(subIdList), null);
3496 
3497             if (DBG) logdl("addSubscriptionsIntoGroup update DB result: " + result);
3498 
3499             if (result > 0) {
3500                 updateGroupOwner(groupUuid, callingPackage);
3501                 refreshCachedActiveSubscriptionInfoList();
3502                 notifySubscriptionInfoChanged();
3503                 MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
3504             }
3505         } finally {
3506             Binder.restoreCallingIdentity(identity);
3507         }
3508     }
3509 
3510     /**
3511      * Remove a list of subscriptions from their subscription group.
3512      * See {@link SubscriptionManager#createSubscriptionGroup(List<Integer>)} for more details.
3513      *
3514      * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
3515      * permission or had carrier privilege permission on the subscriptions:
3516      * {@link TelephonyManager#hasCarrierPrivileges()} or
3517      * {@link SubscriptionManager#canManageSubscription(SubscriptionInfo)}
3518      *
3519      * @throws SecurityException if the caller doesn't meet the requirements
3520      *             outlined above.
3521      * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong
3522      *             the specified group.
3523      *
3524      * @param subIdList list of subId that need removing from their groups.
3525      *
3526      */
removeSubscriptionsFromGroup(int[] subIdList, ParcelUuid groupUuid, String callingPackage)3527     public void removeSubscriptionsFromGroup(int[] subIdList, ParcelUuid groupUuid,
3528             String callingPackage) {
3529         if (subIdList == null || subIdList.length == 0) {
3530             return;
3531         }
3532 
3533         // Makes sure calling package matches caller UID.
3534         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
3535         // If it doesn't have modify phone state permission, or carrier privilege permission,
3536         // a SecurityException will be thrown. If it's due to invalid parameter or internal state,
3537         // it will return null.
3538         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
3539                 != PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage)
3540                 && canPackageManageGroup(groupUuid, callingPackage))) {
3541             throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or"
3542                     + " carrier privilege permission on all specified subscriptions");
3543         }
3544 
3545         long identity = Binder.clearCallingIdentity();
3546 
3547         try {
3548             List<SubscriptionInfo> subInfoList = getSubInfo(getSelectionForSubIdList(subIdList),
3549                     null);
3550             for (SubscriptionInfo info : subInfoList) {
3551                 if (!groupUuid.equals(info.getGroupUuid())) {
3552                     throw new IllegalArgumentException("Subscription " + info.getSubscriptionId()
3553                         + " doesn't belong to group " + groupUuid);
3554                 }
3555             }
3556             ContentValues value = new ContentValues();
3557             value.put(SubscriptionManager.GROUP_UUID, (String) null);
3558             value.put(SubscriptionManager.GROUP_OWNER, (String) null);
3559             int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
3560                     value, getSelectionForSubIdList(subIdList), null);
3561 
3562             if (DBG) logdl("removeSubscriptionsFromGroup update DB result: " + result);
3563 
3564             if (result > 0) {
3565                 updateGroupOwner(groupUuid, callingPackage);
3566                 refreshCachedActiveSubscriptionInfoList();
3567                 notifySubscriptionInfoChanged();
3568             }
3569         } finally {
3570             Binder.restoreCallingIdentity(identity);
3571         }
3572     }
3573 
3574     /**
3575      *  Helper function to check if the caller has carrier privilege permissions on a list of subId.
3576      *  The check can either be processed against access rules on currently active SIM cards, or
3577      *  the access rules we keep in our database for currently inactive eSIMs.
3578      *
3579      * @throws IllegalArgumentException if the some subId is invalid or doesn't exist.
3580      *
3581      *  @return true if checking passes on all subId, false otherwise.
3582      */
checkCarrierPrivilegeOnSubList(int[] subIdList, String callingPackage)3583     private boolean checkCarrierPrivilegeOnSubList(int[] subIdList, String callingPackage) {
3584         // Check carrier privilege permission on active subscriptions first.
3585         // If it fails, they could be inactive. So keep them in a HashSet and later check
3586         // access rules in our database.
3587         Set<Integer> checkSubList = new HashSet<>();
3588         for (int subId : subIdList) {
3589             if (isActiveSubId(subId)) {
3590                 if (!mTelephonyManager.hasCarrierPrivileges(subId)) {
3591                     return false;
3592                 }
3593             } else {
3594                 checkSubList.add(subId);
3595             }
3596         }
3597 
3598         if (checkSubList.isEmpty()) {
3599             return true;
3600         }
3601 
3602         long identity = Binder.clearCallingIdentity();
3603 
3604         try {
3605             // Check access rules for each sub info.
3606             SubscriptionManager subscriptionManager = (SubscriptionManager)
3607                     mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
3608             List<SubscriptionInfo> subInfoList = getSubInfo(
3609                     getSelectionForSubIdList(subIdList), null);
3610 
3611             // Didn't find all the subscriptions specified in subIdList.
3612             if (subInfoList == null || subInfoList.size() != subIdList.length) {
3613                 throw new IllegalArgumentException("Invalid subInfoList.");
3614             }
3615 
3616             for (SubscriptionInfo subInfo : subInfoList) {
3617                 if (checkSubList.contains(subInfo.getSubscriptionId())) {
3618                     if (subInfo.isEmbedded() && subscriptionManager.canManageSubscription(
3619                             subInfo, callingPackage)) {
3620                         checkSubList.remove(subInfo.getSubscriptionId());
3621                     } else {
3622                         return false;
3623                     }
3624                 }
3625             }
3626 
3627             return checkSubList.isEmpty();
3628         } finally {
3629             Binder.restoreCallingIdentity(identity);
3630         }
3631     }
3632 
3633     /**
3634      * Helper function to create selection argument of a list of subId.
3635      * The result should be: "in (subId1, subId2, ...)".
3636      */
getSelectionForSubIdList(int[] subId)3637     public static String getSelectionForSubIdList(int[] subId) {
3638         StringBuilder selection = new StringBuilder();
3639         selection.append(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID);
3640         selection.append(" IN (");
3641         for (int i = 0; i < subId.length - 1; i++) {
3642             selection.append(subId[i] + ", ");
3643         }
3644         selection.append(subId[subId.length - 1]);
3645         selection.append(")");
3646 
3647         return selection.toString();
3648     }
3649 
3650     /**
3651      * Helper function to create selection argument of a list of subId.
3652      * The result should be: "in (iccId1, iccId2, ...)".
3653      */
getSelectionForIccIdList(String[] iccIds)3654     private String getSelectionForIccIdList(String[] iccIds) {
3655         StringBuilder selection = new StringBuilder();
3656         selection.append(SubscriptionManager.ICC_ID);
3657         selection.append(" IN (");
3658         for (int i = 0; i < iccIds.length - 1; i++) {
3659             selection.append("\"" + iccIds[i] + "\", ");
3660         }
3661         selection.append("\"" + iccIds[iccIds.length - 1] + "\"");
3662         selection.append(")");
3663 
3664         return selection.toString();
3665     }
3666 
3667     /**
3668      * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
3669      * See {@link #createSubscriptionGroup(int[], String)} for more details.
3670      *
3671      * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
3672      * permission or had carrier privilege permission on the subscription.
3673      * {@link TelephonyManager#hasCarrierPrivileges(int)}
3674      *
3675      * @throws SecurityException if the caller doesn't meet the requirements
3676      *             outlined above.
3677      *
3678      * @param groupUuid of which list of subInfo will be returned.
3679      * @return list of subscriptionInfo that belong to the same group, including the given
3680      * subscription itself. It will return an empty list if no subscription belongs to the group.
3681      *
3682      */
3683     @Override
getSubscriptionsInGroup(ParcelUuid groupUuid, String callingPackage, String callingFeatureId)3684     public List<SubscriptionInfo> getSubscriptionsInGroup(ParcelUuid groupUuid,
3685             String callingPackage, String callingFeatureId) {
3686         long identity = Binder.clearCallingIdentity();
3687         List<SubscriptionInfo> subInfoList;
3688 
3689         try {
3690             subInfoList = getAllSubInfoList(mContext.getOpPackageName(),
3691                     mContext.getAttributionTag());
3692             if (groupUuid == null || subInfoList == null || subInfoList.isEmpty()) {
3693                 return new ArrayList<>();
3694             }
3695         } finally {
3696             Binder.restoreCallingIdentity(identity);
3697         }
3698 
3699         return subInfoList.stream().filter(info -> {
3700             if (!groupUuid.equals(info.getGroupUuid())) return false;
3701             int subId = info.getSubscriptionId();
3702             return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId,
3703                     callingPackage, callingFeatureId, "getSubscriptionsInGroup")
3704                     || info.canManageSubscription(mContext, callingPackage);
3705         }).map(subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
3706                 callingPackage, callingFeatureId, "getSubscriptionsInGroup"))
3707         .collect(Collectors.toList());
3708 
3709     }
3710 
getGroupUuid(int subId)3711     public ParcelUuid getGroupUuid(int subId) {
3712         ParcelUuid groupUuid;
3713         List<SubscriptionInfo> subInfo = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
3714                         + "=" + subId, null);
3715         if (subInfo == null || subInfo.size() == 0) {
3716             groupUuid = null;
3717         } else {
3718             groupUuid = subInfo.get(0).getGroupUuid();
3719         }
3720 
3721         return groupUuid;
3722     }
3723 
3724 
3725     /**
3726      * Enable/Disable a subscription
3727      * @param enable true if enabling, false if disabling
3728      * @param subId the unique SubInfoRecord index in database
3729      *
3730      * @return true if success, false if fails or the further action is
3731      * needed hence it's redirected to Euicc.
3732      */
3733     @Override
setSubscriptionEnabled(boolean enable, int subId)3734     public boolean setSubscriptionEnabled(boolean enable, int subId) {
3735         enforceModifyPhoneState("setSubscriptionEnabled");
3736 
3737         final long identity = Binder.clearCallingIdentity();
3738         try {
3739             logd("setSubscriptionEnabled" + (enable ? " enable " : " disable ")
3740                     + " subId " + subId);
3741 
3742             // Error checking.
3743             if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
3744                 throw new IllegalArgumentException(
3745                         "setSubscriptionEnabled not usable subId " + subId);
3746             }
3747 
3748             // Nothing to do if it's already active or inactive.
3749             if (enable == isActiveSubscriptionId(subId)) return true;
3750 
3751             SubscriptionInfo info = SubscriptionController.getInstance()
3752                     .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag())
3753                     .stream()
3754                     .filter(subInfo -> subInfo.getSubscriptionId() == subId)
3755                     .findFirst()
3756                     .get();
3757 
3758             if (info == null) {
3759                 logd("setSubscriptionEnabled subId " + subId + " doesn't exist.");
3760                 return false;
3761             }
3762 
3763             // TODO: make sure after slot mapping, we enable the uicc applications for the
3764             // subscription we are enabling.
3765             if (info.isEmbedded()) {
3766                 return enableEmbeddedSubscription(info, enable);
3767             } else {
3768                 return enablePhysicalSubscription(info, enable);
3769             }
3770         } finally {
3771             Binder.restoreCallingIdentity(identity);
3772         }
3773     }
3774 
enableEmbeddedSubscription(SubscriptionInfo info, boolean enable)3775     private boolean enableEmbeddedSubscription(SubscriptionInfo info, boolean enable) {
3776         // We need to send intents to Euicc for operations:
3777 
3778         // 1) In single SIM mode, turning on a eSIM subscription while pSIM is the active slot.
3779         //    Euicc will ask user to switch to DSDS if supported or to confirm SIM slot
3780         //    switching.
3781         // 2) In DSDS mode, turning on / off an eSIM profile. Euicc can ask user whether
3782         //    to turn on DSDS, or whether to switch from current active eSIM profile to it, or
3783         //    to simply show a progress dialog.
3784         // 3) In future, similar operations on triple SIM devices.
3785         enableSubscriptionOverEuiccManager(info.getSubscriptionId(), enable,
3786                 SubscriptionManager.INVALID_SIM_SLOT_INDEX);
3787         // returning false to indicate state is not changed. If changed, a subscriptionInfo
3788         // change will be filed separately.
3789         return false;
3790 
3791         // TODO: uncomment or clean up if we decide whether to support standalone CBRS for Q.
3792         // subId = enable ? subId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
3793         // updateEnabledSubscriptionGlobalSetting(subId, physicalSlotIndex);
3794     }
3795 
enablePhysicalSubscription(SubscriptionInfo info, boolean enable)3796     private boolean enablePhysicalSubscription(SubscriptionInfo info, boolean enable) {
3797         if (info == null || !SubscriptionManager.isValidSubscriptionId(info.getSubscriptionId())) {
3798             return false;
3799         }
3800 
3801         int subId = info.getSubscriptionId();
3802 
3803         UiccSlotInfo slotInfo = null;
3804         int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
3805         UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo();
3806         if (slotsInfo == null) return false;
3807         for (int i = 0; i < slotsInfo.length; i++) {
3808             UiccSlotInfo curSlotInfo = slotsInfo[i];
3809             if (curSlotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT) {
3810                 if (TextUtils.equals(IccUtils.stripTrailingFs(curSlotInfo.getCardId()),
3811                         IccUtils.stripTrailingFs(info.getCardString()))) {
3812                     slotInfo = curSlotInfo;
3813                     physicalSlotIndex = i;
3814                     break;
3815                 }
3816             }
3817         }
3818 
3819         // Can't find the existing SIM.
3820         if (slotInfo == null) return false;
3821 
3822         if (enable && !slotInfo.getIsActive()) {
3823             // We need to send intents to Euicc if we are turning on an inactive slot.
3824             // Euicc will decide whether to ask user to switch to DSDS, or change SIM
3825             // slot mapping.
3826             EuiccManager euiccManager =
3827                     (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
3828             if (euiccManager != null && euiccManager.isEnabled()) {
3829                 enableSubscriptionOverEuiccManager(subId, enable, physicalSlotIndex);
3830             } else {
3831                 // Enable / disable uicc applications.
3832                 if (!info.areUiccApplicationsEnabled()) setUiccApplicationsEnabled(enable, subId);
3833                 // If euiccManager is not enabled, we try to switch to DSDS if possible,
3834                 // or switch slot if not.
3835                 if (mTelephonyManager.isMultiSimSupported() == MULTISIM_ALLOWED) {
3836                     PhoneConfigurationManager.getInstance().switchMultiSimConfig(
3837                             mTelephonyManager.getSupportedModemCount());
3838                 } else {
3839                     UiccController.getInstance().switchSlots(new int[]{physicalSlotIndex}, null);
3840                 }
3841             }
3842             return true;
3843         } else {
3844             // Enable / disable uicc applications.
3845             setUiccApplicationsEnabled(enable, subId);
3846             return true;
3847         }
3848     }
3849 
enableSubscriptionOverEuiccManager(int subId, boolean enable, int physicalSlotIndex)3850     private void enableSubscriptionOverEuiccManager(int subId, boolean enable,
3851             int physicalSlotIndex) {
3852         logdl("enableSubscriptionOverEuiccManager" + (enable ? " enable " : " disable ")
3853                 + "subId " + subId + " on slotIndex " + physicalSlotIndex);
3854         Intent intent = new Intent(EuiccManager.ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED);
3855         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3856         intent.putExtra(EuiccManager.EXTRA_SUBSCRIPTION_ID, subId);
3857         intent.putExtra(EuiccManager.EXTRA_ENABLE_SUBSCRIPTION, enable);
3858         if (physicalSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
3859             intent.putExtra(EuiccManager.EXTRA_PHYSICAL_SLOT_ID, physicalSlotIndex);
3860         }
3861         mContext.startActivity(intent);
3862     }
3863 
updateEnabledSubscriptionGlobalSetting(int subId, int physicalSlotIndex)3864     private void updateEnabledSubscriptionGlobalSetting(int subId, int physicalSlotIndex) {
3865         // Write the value which subscription is enabled into global setting.
3866         Settings.Global.putInt(mContext.getContentResolver(),
3867                 Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex, subId);
3868     }
3869 
updateModemStackEnabledGlobalSetting(boolean enabled, int physicalSlotIndex)3870     private void updateModemStackEnabledGlobalSetting(boolean enabled, int physicalSlotIndex) {
3871         // Write the whether a modem stack is disabled into global setting.
3872         Settings.Global.putInt(mContext.getContentResolver(),
3873                 Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT
3874                         + physicalSlotIndex, enabled ? 1 : 0);
3875     }
3876 
getPhysicalSlotIndex(boolean isEmbedded, int subId)3877     private int getPhysicalSlotIndex(boolean isEmbedded, int subId) {
3878         UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
3879         int logicalSlotIndex = getSlotIndex(subId);
3880         int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
3881         boolean isLogicalSlotIndexValid = SubscriptionManager.isValidSlotIndex(logicalSlotIndex);
3882 
3883         for (int i = 0; i < slotInfos.length; i++) {
3884             // If we can know the logicalSlotIndex from subId, we should find the exact matching
3885             // physicalSlotIndex. However for some cases like inactive eSIM, the logicalSlotIndex
3886             // will be -1. In this case, we assume there's only one eSIM, and return the
3887             // physicalSlotIndex of that eSIM.
3888             if ((isLogicalSlotIndexValid && slotInfos[i].getLogicalSlotIdx() == logicalSlotIndex)
3889                     || (!isLogicalSlotIndexValid && slotInfos[i].getIsEuicc() && isEmbedded)) {
3890                 physicalSlotIndex = i;
3891                 break;
3892             }
3893         }
3894 
3895         return physicalSlotIndex;
3896     }
3897 
getPhysicalSlotIndexFromLogicalSlotIndex(int logicalSlotIndex)3898     private int getPhysicalSlotIndexFromLogicalSlotIndex(int logicalSlotIndex) {
3899         int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
3900         UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
3901         for (int i = 0; i < slotInfos.length; i++) {
3902             if (slotInfos[i].getLogicalSlotIdx() == logicalSlotIndex) {
3903                 physicalSlotIndex = i;
3904                 break;
3905             }
3906         }
3907 
3908         return physicalSlotIndex;
3909     }
3910 
3911     @Override
isSubscriptionEnabled(int subId)3912     public boolean isSubscriptionEnabled(int subId) {
3913         // TODO: b/123314365 support multi-eSIM and removable eSIM.
3914         enforceReadPrivilegedPhoneState("isSubscriptionEnabled");
3915 
3916         long identity = Binder.clearCallingIdentity();
3917         try {
3918             // Error checking.
3919             if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
3920                 throw new IllegalArgumentException(
3921                         "isSubscriptionEnabled not usable subId " + subId);
3922             }
3923 
3924             List<SubscriptionInfo> infoList = getSubInfo(
3925                     SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
3926             if (infoList == null || infoList.isEmpty()) {
3927                 // Subscription doesn't exist.
3928                 return false;
3929             }
3930 
3931             boolean isEmbedded = infoList.get(0).isEmbedded();
3932 
3933             if (isEmbedded) {
3934                 return isActiveSubId(subId);
3935             } else {
3936                 // For pSIM, we also need to check if modem is disabled or not.
3937                 return isActiveSubId(subId) && PhoneConfigurationManager.getInstance()
3938                         .getPhoneStatus(PhoneFactory.getPhone(getPhoneId(subId)));
3939             }
3940 
3941         } finally {
3942             Binder.restoreCallingIdentity(identity);
3943         }
3944     }
3945 
3946     @Override
getEnabledSubscriptionId(int logicalSlotIndex)3947     public int getEnabledSubscriptionId(int logicalSlotIndex) {
3948         // TODO: b/123314365 support multi-eSIM and removable eSIM.
3949         enforceReadPrivilegedPhoneState("getEnabledSubscriptionId");
3950 
3951         long identity = Binder.clearCallingIdentity();
3952         try {
3953             if (!SubscriptionManager.isValidPhoneId(logicalSlotIndex)) {
3954                 throw new IllegalArgumentException(
3955                         "getEnabledSubscriptionId with invalid logicalSlotIndex "
3956                                 + logicalSlotIndex);
3957             }
3958 
3959             // Getting and validating the physicalSlotIndex.
3960             int physicalSlotIndex = getPhysicalSlotIndexFromLogicalSlotIndex(logicalSlotIndex);
3961             if (physicalSlotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
3962                 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
3963             }
3964 
3965             // if modem stack is disabled, return INVALID_SUBSCRIPTION_ID without reading
3966             // Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT.
3967             int modemStackEnabled = Settings.Global.getInt(mContext.getContentResolver(),
3968                     Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT + physicalSlotIndex, 1);
3969             if (modemStackEnabled != 1) {
3970                 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
3971             }
3972 
3973             int subId;
3974             try {
3975                 subId = Settings.Global.getInt(mContext.getContentResolver(),
3976                         Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT + physicalSlotIndex);
3977             } catch (Settings.SettingNotFoundException e) {
3978                 // Value never set. Return whether it's currently active.
3979                 subId = getSubIdUsingPhoneId(logicalSlotIndex);
3980             }
3981 
3982             return subId;
3983         } finally {
3984             Binder.restoreCallingIdentity(identity);
3985         }
3986     }
3987 
3988     /**
3989      * Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
3990      * They are doing similar things except operating on different cache.
3991      *
3992      * NOTE: the cacheSubList passed in is a *copy* of mCacheActiveSubInfoList or
3993      * mCacheOpportunisticSubInfoList, so mSubInfoListLock is not required to access it. Also, this
3994      * method may modify cacheSubList depending on the permissions the caller has.
3995      */
getSubscriptionInfoListFromCacheHelper( String callingPackage, String callingFeatureId, List<SubscriptionInfo> cacheSubList)3996     private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
3997             String callingPackage, String callingFeatureId, List<SubscriptionInfo> cacheSubList) {
3998         boolean canReadPhoneState = false;
3999         boolean canReadIdentifiers = false;
4000         boolean canReadPhoneNumber = false;
4001         try {
4002             canReadPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
4003                     SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
4004                     Binder.getCallingUid(), callingPackage, callingFeatureId,
4005                     "getSubscriptionInfoList");
4006             // If the calling package has the READ_PHONE_STATE permission then check if the caller
4007             // also has access to subscriber identifiers and the phone number to ensure that the ICC
4008             // ID and any other unique identifiers are removed if the caller should not have access.
4009             if (canReadPhoneState) {
4010                 canReadIdentifiers = hasSubscriberIdentifierAccess(
4011                         SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
4012                         callingFeatureId, "getSubscriptionInfoList");
4013                 canReadPhoneNumber = hasPhoneNumberAccess(
4014                         SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
4015                         callingFeatureId, "getSubscriptionInfoList");
4016             }
4017         } catch (SecurityException e) {
4018             // If a SecurityException is thrown during the READ_PHONE_STATE check then the only way
4019             // to access a subscription is to have carrier privileges for its subId; an app with
4020             // carrier privileges for a subscription is also granted access to all identifiers so
4021             // the identifier and phone number access checks are not required.
4022         }
4023 
4024         if (canReadIdentifiers && canReadPhoneNumber) {
4025             return cacheSubList;
4026         }
4027         // Filter the list to only include subscriptions which the caller can manage.
4028         for (int subIndex = cacheSubList.size() - 1; subIndex >= 0; subIndex--) {
4029             SubscriptionInfo subscriptionInfo = cacheSubList.get(subIndex);
4030 
4031             int subId = subscriptionInfo.getSubscriptionId();
4032             boolean hasCarrierPrivileges = TelephonyPermissions.checkCarrierPrivilegeForSubId(
4033                     mContext, subId);
4034             // If the caller has carrier privileges then they are granted access to all
4035             // identifiers for their subscription.
4036             if (hasCarrierPrivileges) continue;
4037 
4038             cacheSubList.remove(subIndex);
4039             if (canReadPhoneState) {
4040                 // The caller does not have carrier privileges for this subId, filter the
4041                 // identifiers in the subscription based on the results of the initial
4042                 // permission checks.
4043                 cacheSubList.add(subIndex, conditionallyRemoveIdentifiers(
4044                         subscriptionInfo, canReadIdentifiers, canReadPhoneNumber));
4045             }
4046         }
4047         return cacheSubList;
4048     }
4049 
4050     /**
4051      * Conditionally removes identifiers from the provided {@code subInfo} if the {@code
4052      * callingPackage} does not meet the access requirements for identifiers and returns the
4053      * potentially modified object..
4054      *
4055      * <p>If the caller does not meet the access requirements for identifiers a clone of the
4056      * provided SubscriptionInfo is created and modified to avoid altering SubscriptionInfo objects
4057      * in a cache.
4058      */
conditionallyRemoveIdentifiers(SubscriptionInfo subInfo, String callingPackage, String callingFeatureId, String message)4059     private SubscriptionInfo conditionallyRemoveIdentifiers(SubscriptionInfo subInfo,
4060             String callingPackage, String callingFeatureId, String message) {
4061         SubscriptionInfo result = subInfo;
4062         int subId = subInfo.getSubscriptionId();
4063         boolean hasIdentifierAccess = hasSubscriberIdentifierAccess(subId, callingPackage,
4064                 callingFeatureId, message);
4065         boolean hasPhoneNumberAccess = hasPhoneNumberAccess(subId, callingPackage, callingFeatureId,
4066                 message);
4067         return conditionallyRemoveIdentifiers(subInfo, hasIdentifierAccess, hasPhoneNumberAccess);
4068     }
4069 
4070     /**
4071      * Conditionally removes identifiers from the provided {@code subInfo} based on if the calling
4072      * package {@code hasIdentifierAccess} and {@code hasPhoneNumberAccess} and returns the
4073      * potentially modified object.
4074      *
4075      * <p>If the caller specifies the package does not have identifier or phone number access
4076      * a clone of the provided SubscriptionInfo is created and modified to avoid altering
4077      * SubscriptionInfo objects in a cache.
4078      */
conditionallyRemoveIdentifiers(SubscriptionInfo subInfo, boolean hasIdentifierAccess, boolean hasPhoneNumberAccess)4079     private SubscriptionInfo conditionallyRemoveIdentifiers(SubscriptionInfo subInfo,
4080             boolean hasIdentifierAccess, boolean hasPhoneNumberAccess) {
4081         if (hasIdentifierAccess && hasPhoneNumberAccess) {
4082             return subInfo;
4083         }
4084         SubscriptionInfo result = new SubscriptionInfo(subInfo);
4085         if (!hasIdentifierAccess) {
4086             result.clearIccId();
4087             result.clearCardString();
4088             result.clearGroupUuid();
4089         }
4090         if (!hasPhoneNumberAccess) {
4091             result.clearNumber();
4092         }
4093         return result;
4094     }
4095 
addToSubIdList(int slotIndex, int subId, int subscriptionType)4096     private synchronized boolean addToSubIdList(int slotIndex, int subId, int subscriptionType) {
4097         ArrayList<Integer> subIdsList = sSlotIndexToSubIds.getCopy(slotIndex);
4098         if (subIdsList == null) {
4099             subIdsList = new ArrayList<>();
4100             sSlotIndexToSubIds.put(slotIndex, subIdsList);
4101         }
4102 
4103         // add the given subId unless it already exists
4104         if (subIdsList.contains(subId)) {
4105             logdl("slotIndex, subId combo already exists in the map. Not adding it again.");
4106             return false;
4107         }
4108         if (isSubscriptionForRemoteSim(subscriptionType)) {
4109             // For Remote SIM subscriptions, a slot can have multiple subscriptions.
4110             sSlotIndexToSubIds.addToSubIdList(slotIndex, subId);
4111         } else {
4112             // for all other types of subscriptions, a slot can have only one subscription at a time
4113             sSlotIndexToSubIds.clearSubIdList(slotIndex);
4114             sSlotIndexToSubIds.addToSubIdList(slotIndex, subId);
4115         }
4116 
4117 
4118         // Remove the slot from sSlotIndexToSubIds if it has the same sub id with the added slot
4119         for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
4120             if (entry.getKey() != slotIndex && entry.getValue() != null
4121                     && entry.getValue().contains(subId)) {
4122                 logdl("addToSubIdList - remove " + entry.getKey());
4123                 sSlotIndexToSubIds.remove(entry.getKey());
4124             }
4125         }
4126 
4127         if (DBG) logdl("slotIndex, subId combo is added to the map.");
4128         return true;
4129     }
4130 
isSubscriptionForRemoteSim(int subscriptionType)4131     private boolean isSubscriptionForRemoteSim(int subscriptionType) {
4132         return subscriptionType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
4133     }
4134 
4135     /**
4136      * This is only for testing
4137      * @hide
4138      */
4139     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getSlotIndexToSubIdsMap()4140     public Map<Integer, ArrayList<Integer>> getSlotIndexToSubIdsMap() {
4141         return sSlotIndexToSubIds.getMap();
4142     }
4143 
4144     /**
4145      * This is only for testing
4146      * @hide
4147      */
4148     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
resetStaticMembers()4149     public void resetStaticMembers() {
4150         sDefaultFallbackSubId.set(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
4151         mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
4152     }
4153 
notifyOpportunisticSubscriptionInfoChanged()4154     private void notifyOpportunisticSubscriptionInfoChanged() {
4155         TelephonyRegistryManager trm =
4156                 (TelephonyRegistryManager)
4157                         mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
4158         if (DBG) logd("notifyOpptSubscriptionInfoChanged:");
4159         trm.notifyOpportunisticSubscriptionInfoChanged();
4160     }
4161 
refreshCachedOpportunisticSubscriptionInfoList()4162     private void refreshCachedOpportunisticSubscriptionInfoList() {
4163         List<SubscriptionInfo> subList = getSubInfo(
4164                 SubscriptionManager.IS_OPPORTUNISTIC + "=1 AND ("
4165                         + SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
4166                         + SubscriptionManager.IS_EMBEDDED + "=1)", null);
4167         synchronized (mSubInfoListLock) {
4168             List<SubscriptionInfo> oldOpptCachedList = mCacheOpportunisticSubInfoList;
4169 
4170             if (subList != null) {
4171                 subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
4172             } else {
4173                 subList = new ArrayList<>();
4174             }
4175 
4176             mCacheOpportunisticSubInfoList = subList;
4177 
4178             for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
4179                 if (shouldDisableSubGroup(info.getGroupUuid())) {
4180                     info.setGroupDisabled(true);
4181                 }
4182             }
4183 
4184             if (DBG_CACHE) {
4185                 if (!mCacheOpportunisticSubInfoList.isEmpty()) {
4186                     for (SubscriptionInfo si : mCacheOpportunisticSubInfoList) {
4187                         logd("[refreshCachedOpptSubscriptionInfoList] Setting Cached info="
4188                                 + si);
4189                     }
4190                 } else {
4191                     logdl("[refreshCachedOpptSubscriptionInfoList]- no info return");
4192                 }
4193             }
4194 
4195             if (!oldOpptCachedList.equals(mCacheOpportunisticSubInfoList)) {
4196                 mOpptSubInfoListChangedDirtyBit.set(true);
4197             }
4198         }
4199     }
4200 
shouldDisableSubGroup(ParcelUuid groupUuid)4201     private boolean shouldDisableSubGroup(ParcelUuid groupUuid) {
4202         if (groupUuid == null) return false;
4203 
4204         synchronized (mSubInfoListLock) {
4205             for (SubscriptionInfo activeInfo : mCacheActiveSubInfoList) {
4206                 if (!activeInfo.isOpportunistic() && groupUuid.equals(activeInfo.getGroupUuid())) {
4207                     return false;
4208                 }
4209             }
4210         }
4211 
4212         return true;
4213     }
4214 
4215     /**
4216      * Set allowing mobile data during voice call.
4217      *
4218      * @param subId Subscription index
4219      * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride}
4220      * for details.
4221      * @return {@code true} if settings changed, otherwise {@code false}.
4222      */
setDataEnabledOverrideRules(int subId, @NonNull String rules)4223     public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) {
4224         if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId);
4225 
4226         validateSubId(subId);
4227         ContentValues value = new ContentValues(1);
4228         value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules);
4229 
4230         boolean result = updateDatabase(value, subId, true) > 0;
4231 
4232         if (result) {
4233             // Refresh the Cache of Active Subscription Info List
4234             refreshCachedActiveSubscriptionInfoList();
4235             notifySubscriptionInfoChanged();
4236         }
4237 
4238         return result;
4239     }
4240 
4241     /**
4242      * Get data enabled override rules.
4243      *
4244      * @param subId Subscription index
4245      * @return Data enabled override rules in string
4246      */
4247     @NonNull
getDataEnabledOverrideRules(int subId)4248     public String getDataEnabledOverrideRules(int subId) {
4249         return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId,
4250                 SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES));
4251     }
4252 
4253     /**
4254      * Get active data subscription id.
4255      *
4256      * @return Active data subscription id
4257      *
4258      * @hide
4259      */
4260     @Override
getActiveDataSubscriptionId()4261     public int getActiveDataSubscriptionId() {
4262         final long token = Binder.clearCallingIdentity();
4263 
4264         try {
4265             PhoneSwitcher phoneSwitcher = PhoneSwitcher.getInstance();
4266             if (phoneSwitcher != null) {
4267                 int activeDataSubId = phoneSwitcher.getActiveDataSubId();
4268                 if (SubscriptionManager.isUsableSubscriptionId(activeDataSubId)) {
4269                     return activeDataSubId;
4270                 }
4271             }
4272             // If phone switcher isn't ready, or active data sub id is not available, use default
4273             // sub id from settings.
4274             return getDefaultDataSubId();
4275         } finally {
4276             Binder.restoreCallingIdentity(token);
4277         }
4278     }
4279 
4280     /**
4281      * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM.
4282      */
4283     @Override
canDisablePhysicalSubscription()4284     public boolean canDisablePhysicalSubscription() {
4285         enforceReadPrivilegedPhoneState("canToggleUiccApplicationsEnablement");
4286 
4287         final long identity = Binder.clearCallingIdentity();
4288         try {
4289             Phone phone = PhoneFactory.getDefaultPhone();
4290             return phone != null && phone.canDisablePhysicalSubscription();
4291         } finally {
4292             Binder.restoreCallingIdentity(identity);
4293         }
4294     }
4295 
4296     /**
4297      * @hide
4298      */
setGlobalSetting(String name, int value)4299     private void setGlobalSetting(String name, int value) {
4300         Settings.Global.putInt(mContext.getContentResolver(), name, value);
4301         if (name == Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION) {
4302             invalidateDefaultDataSubIdCaches();
4303             invalidateActiveDataSubIdCaches();
4304             invalidateDefaultSubIdCaches();
4305             invalidateSlotIndexCaches();
4306         } else if (name == Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION) {
4307             invalidateDefaultSubIdCaches();
4308             invalidateSlotIndexCaches();
4309         } else if (name == Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION) {
4310             invalidateDefaultSmsSubIdCaches();
4311         }
4312     }
4313 
4314     /**
4315      * @hide
4316      */
invalidateDefaultSubIdCaches()4317     private static void invalidateDefaultSubIdCaches() {
4318         if (sCachingEnabled) {
4319             SubscriptionManager.invalidateDefaultSubIdCaches();
4320         }
4321     }
4322 
4323     /**
4324      * @hide
4325      */
invalidateDefaultDataSubIdCaches()4326     private static void invalidateDefaultDataSubIdCaches() {
4327         if (sCachingEnabled) {
4328             SubscriptionManager.invalidateDefaultDataSubIdCaches();
4329         }
4330     }
4331 
4332     /**
4333      * @hide
4334      */
invalidateDefaultSmsSubIdCaches()4335     private static void invalidateDefaultSmsSubIdCaches() {
4336         if (sCachingEnabled) {
4337             SubscriptionManager.invalidateDefaultSmsSubIdCaches();
4338         }
4339     }
4340 
4341     /**
4342      * @hide
4343      */
invalidateActiveDataSubIdCaches()4344     protected static void invalidateActiveDataSubIdCaches() {
4345         if (sCachingEnabled) {
4346             SubscriptionManager.invalidateActiveDataSubIdCaches();
4347         }
4348     }
4349 
4350     /**
4351      * @hide
4352      */
invalidateSlotIndexCaches()4353     protected static void invalidateSlotIndexCaches() {
4354         if (sCachingEnabled) {
4355             SubscriptionManager.invalidateSlotIndexCaches();
4356         }
4357     }
4358 
4359     /**
4360      * @hide
4361      */
4362     @VisibleForTesting
disableCaching()4363     public static void disableCaching() {
4364         sCachingEnabled = false;
4365     }
4366 
4367     /**
4368      * @hide
4369      */
4370     @VisibleForTesting
enableCaching()4371     public static void enableCaching() {
4372         sCachingEnabled = true;
4373     }
4374 }
4375