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