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