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