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