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