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