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