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