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