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