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 android.Manifest; 20 import android.annotation.Nullable; 21 import android.app.AppOpsManager; 22 import android.content.ContentResolver; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.database.Cursor; 27 import android.graphics.Bitmap; 28 import android.graphics.BitmapFactory; 29 import android.net.Uri; 30 import android.os.Binder; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.os.UserHandle; 34 import android.provider.Settings; 35 import android.telephony.RadioAccessFamily; 36 import android.telephony.Rlog; 37 import android.telephony.SubscriptionInfo; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.TelephonyManager; 40 import android.telephony.UiccAccessRule; 41 import android.telephony.euicc.EuiccManager; 42 import android.text.TextUtils; 43 import android.text.format.Time; 44 import android.util.Log; 45 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.internal.telephony.IccCardConstants.State; 48 import com.android.internal.telephony.uicc.IccUtils; 49 import com.android.internal.telephony.uicc.UiccCard; 50 import com.android.internal.telephony.uicc.UiccController; 51 52 import java.io.FileDescriptor; 53 import java.io.PrintWriter; 54 import java.util.ArrayList; 55 import java.util.Arrays; 56 import java.util.Collections; 57 import java.util.Comparator; 58 import java.util.HashSet; 59 import java.util.Iterator; 60 import java.util.LinkedList; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Map.Entry; 64 import java.util.Objects; 65 import java.util.Set; 66 import java.util.concurrent.ConcurrentHashMap; 67 import java.util.stream.Collectors; 68 69 /** 70 * SubscriptionController to provide an inter-process communication to 71 * access Sms in Icc. 72 * 73 * Any setters which take subId, slotIndex or phoneId as a parameter will throw an exception if the 74 * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID. 75 * 76 * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling 77 * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()). 78 * 79 * Finally, any getters which perform the mapping between subscriptions, slots and phones will 80 * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters 81 * will fail and return the appropriate error value. Ie calling 82 * getSlotIndex(INVALID_SUBSCRIPTION_ID) will return INVALID_SIM_SLOT_INDEX and calling 83 * getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID) will return null. 84 * 85 */ 86 public class SubscriptionController extends ISub.Stub { 87 static final String LOG_TAG = "SubscriptionController"; 88 static final boolean DBG = true; 89 static final boolean VDBG = false; 90 static final boolean DBG_CACHE = false; 91 static final int MAX_LOCAL_LOG_LINES = 500; // TODO: Reduce to 100 when 17678050 is fixed 92 private static final int DEPRECATED_SETTING = -1; 93 private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES); 94 95 /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */ 96 private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>(); 97 98 /** 99 * Copied from android.util.LocalLog with flush() adding flush and line number 100 * TODO: Update LocalLog 101 */ 102 static class ScLocalLog { 103 104 private LinkedList<String> mLog; 105 private int mMaxLines; 106 private Time mNow; 107 108 public ScLocalLog(int maxLines) { 109 mLog = new LinkedList<String>(); 110 mMaxLines = maxLines; 111 mNow = new Time(); 112 } 113 114 public synchronized void log(String msg) { 115 if (mMaxLines > 0) { 116 int pid = android.os.Process.myPid(); 117 int tid = android.os.Process.myTid(); 118 mNow.setToNow(); 119 mLog.add(mNow.format("%m-%d %H:%M:%S") + " pid=" + pid + " tid=" + tid + " " + msg); 120 while (mLog.size() > mMaxLines) mLog.remove(); 121 } 122 } 123 124 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 125 final int LOOPS_PER_FLUSH = 10; // Flush every N loops. 126 Iterator<String> itr = mLog.listIterator(0); 127 int i = 0; 128 while (itr.hasNext()) { 129 pw.println(Integer.toString(i++) + ": " + itr.next()); 130 // Flush periodically so we don't drop lines 131 if ((i % LOOPS_PER_FLUSH) == 0) pw.flush(); 132 } 133 } 134 } 135 136 private static final Comparator<SubscriptionInfo> SUBSCRIPTION_INFO_COMPARATOR = 137 (arg0, arg1) -> { 138 // Primary sort key on SimSlotIndex 139 int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex(); 140 if (flag == 0) { 141 // Secondary sort on SubscriptionId 142 return arg0.getSubscriptionId() - arg1.getSubscriptionId(); 143 } 144 return flag; 145 }; 146 147 protected final Object mLock = new Object(); 148 149 /** The singleton instance. */ 150 private static SubscriptionController sInstance = null; 151 protected static Phone[] sPhones; 152 protected Context mContext; 153 protected TelephonyManager mTelephonyManager; 154 protected CallManager mCM; 155 156 private AppOpsManager mAppOps; 157 158 // FIXME: Does not allow for multiple subs in a slot and change to SparseArray 159 private static Map<Integer, Integer> sSlotIndexToSubId = 160 new ConcurrentHashMap<Integer, Integer>(); 161 private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 162 private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX; 163 164 private int[] colorArr; 165 private long mLastISubServiceRegTime; 166 167 public static SubscriptionController init(Phone phone) { 168 synchronized (SubscriptionController.class) { 169 if (sInstance == null) { 170 sInstance = new SubscriptionController(phone); 171 } else { 172 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 173 } 174 return sInstance; 175 } 176 } 177 178 public static SubscriptionController init(Context c, CommandsInterface[] ci) { 179 synchronized (SubscriptionController.class) { 180 if (sInstance == null) { 181 sInstance = new SubscriptionController(c); 182 } else { 183 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 184 } 185 return sInstance; 186 } 187 } 188 189 public static SubscriptionController getInstance() { 190 if (sInstance == null) 191 { 192 Log.wtf(LOG_TAG, "getInstance null"); 193 } 194 195 return sInstance; 196 } 197 198 protected SubscriptionController(Context c) { 199 init(c); 200 migrateImsSettings(); 201 } 202 203 protected void init(Context c) { 204 mContext = c; 205 mCM = CallManager.getInstance(); 206 mTelephonyManager = TelephonyManager.from(mContext); 207 208 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 209 210 if(ServiceManager.getService("isub") == null) { 211 ServiceManager.addService("isub", this); 212 mLastISubServiceRegTime = System.currentTimeMillis(); 213 } 214 215 if (DBG) logdl("[SubscriptionController] init by Context"); 216 } 217 218 private boolean isSubInfoReady() { 219 return sSlotIndexToSubId.size() > 0; 220 } 221 222 private SubscriptionController(Phone phone) { 223 mContext = phone.getContext(); 224 mCM = CallManager.getInstance(); 225 mAppOps = mContext.getSystemService(AppOpsManager.class); 226 227 if(ServiceManager.getService("isub") == null) { 228 ServiceManager.addService("isub", this); 229 } 230 231 migrateImsSettings(); 232 233 if (DBG) logdl("[SubscriptionController] init by Phone"); 234 } 235 236 private void enforceModifyPhoneState(String message) { 237 mContext.enforceCallingOrSelfPermission( 238 android.Manifest.permission.MODIFY_PHONE_STATE, message); 239 } 240 241 /** 242 * Broadcast when SubscriptionInfo has changed 243 * FIXME: Hopefully removed if the API council accepts SubscriptionInfoListener 244 */ 245 private void broadcastSimInfoContentChanged() { 246 Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE); 247 mContext.sendBroadcast(intent); 248 intent = new Intent(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED); 249 mContext.sendBroadcast(intent); 250 } 251 252 public void notifySubscriptionInfoChanged() { 253 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( 254 "telephony.registry")); 255 try { 256 if (DBG) logd("notifySubscriptionInfoChanged:"); 257 tr.notifySubscriptionInfoChanged(); 258 } catch (RemoteException ex) { 259 // Should never happen because its always available. 260 } 261 262 // FIXME: Remove if listener technique accepted. 263 broadcastSimInfoContentChanged(); 264 } 265 266 /** 267 * New SubInfoRecord instance and fill in detail info 268 * @param cursor 269 * @return the query result of desired SubInfoRecord 270 */ 271 private SubscriptionInfo getSubInfoRecord(Cursor cursor) { 272 int id = cursor.getInt(cursor.getColumnIndexOrThrow( 273 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID)); 274 String iccId = cursor.getString(cursor.getColumnIndexOrThrow( 275 SubscriptionManager.ICC_ID)); 276 int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow( 277 SubscriptionManager.SIM_SLOT_INDEX)); 278 String displayName = cursor.getString(cursor.getColumnIndexOrThrow( 279 SubscriptionManager.DISPLAY_NAME)); 280 String carrierName = cursor.getString(cursor.getColumnIndexOrThrow( 281 SubscriptionManager.CARRIER_NAME)); 282 int nameSource = cursor.getInt(cursor.getColumnIndexOrThrow( 283 SubscriptionManager.NAME_SOURCE)); 284 int iconTint = cursor.getInt(cursor.getColumnIndexOrThrow( 285 SubscriptionManager.COLOR)); 286 String number = cursor.getString(cursor.getColumnIndexOrThrow( 287 SubscriptionManager.NUMBER)); 288 int dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow( 289 SubscriptionManager.DATA_ROAMING)); 290 // Get the blank bitmap for this SubInfoRecord 291 Bitmap iconBitmap = BitmapFactory.decodeResource(mContext.getResources(), 292 com.android.internal.R.drawable.ic_sim_card_multi_24px_clr); 293 int mcc = cursor.getInt(cursor.getColumnIndexOrThrow( 294 SubscriptionManager.MCC)); 295 int mnc = cursor.getInt(cursor.getColumnIndexOrThrow( 296 SubscriptionManager.MNC)); 297 String cardId = cursor.getString(cursor.getColumnIndexOrThrow( 298 SubscriptionManager.CARD_ID)); 299 // FIXME: consider stick this into database too 300 String countryIso = getSubscriptionCountryIso(id); 301 boolean isEmbedded = cursor.getInt(cursor.getColumnIndexOrThrow( 302 SubscriptionManager.IS_EMBEDDED)) == 1; 303 UiccAccessRule[] accessRules; 304 if (isEmbedded) { 305 accessRules = UiccAccessRule.decodeRules(cursor.getBlob( 306 cursor.getColumnIndexOrThrow(SubscriptionManager.ACCESS_RULES))); 307 } else { 308 accessRules = null; 309 } 310 311 if (VDBG) { 312 String iccIdToPrint = SubscriptionInfo.givePrintableIccid(iccId); 313 String cardIdToPrint = SubscriptionInfo.givePrintableIccid(cardId); 314 logd("[getSubInfoRecord] id:" + id + " iccid:" + iccIdToPrint + " simSlotIndex:" 315 + simSlotIndex + " displayName:" + displayName + " nameSource:" + nameSource 316 + " iconTint:" + iconTint + " dataRoaming:" + dataRoaming 317 + " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso + " isEmbedded:" 318 + isEmbedded + " accessRules:" + Arrays.toString(accessRules) 319 + " cardId:" + cardIdToPrint); 320 } 321 322 // If line1number has been set to a different number, use it instead. 323 String line1Number = mTelephonyManager.getLine1Number(id); 324 if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) { 325 number = line1Number; 326 } 327 return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName, 328 nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso, 329 isEmbedded, accessRules, cardId); 330 } 331 332 /** 333 * Get ISO country code for the subscription's provider 334 * 335 * @param subId The subscription ID 336 * @return The ISO country code for the subscription's provider 337 */ 338 private String getSubscriptionCountryIso(int subId) { 339 final int phoneId = getPhoneId(subId); 340 if (phoneId < 0) { 341 return ""; 342 } 343 return mTelephonyManager.getSimCountryIsoForPhone(phoneId); 344 } 345 346 /** 347 * Query SubInfoRecord(s) from subinfo database 348 * @param selection A filter declaring which rows to return 349 * @param queryKey query key content 350 * @return Array list of queried result from database 351 */ 352 private List<SubscriptionInfo> getSubInfo(String selection, Object queryKey) { 353 if (VDBG) logd("selection:" + selection + " " + queryKey); 354 String[] selectionArgs = null; 355 if (queryKey != null) { 356 selectionArgs = new String[] {queryKey.toString()}; 357 } 358 ArrayList<SubscriptionInfo> subList = null; 359 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 360 null, selection, selectionArgs, null); 361 try { 362 if (cursor != null) { 363 while (cursor.moveToNext()) { 364 SubscriptionInfo subInfo = getSubInfoRecord(cursor); 365 if (subInfo != null) 366 { 367 if (subList == null) 368 { 369 subList = new ArrayList<SubscriptionInfo>(); 370 } 371 subList.add(subInfo); 372 } 373 } 374 } else { 375 if (DBG) logd("Query fail"); 376 } 377 } finally { 378 if (cursor != null) { 379 cursor.close(); 380 } 381 } 382 383 return subList; 384 } 385 386 /** 387 * Find unused color to be set for new SubInfoRecord 388 * @param callingPackage The package making the IPC. 389 * @return RGB integer value of color 390 */ 391 private int getUnusedColor(String callingPackage) { 392 List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage); 393 colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors); 394 int colorIdx = 0; 395 396 if (availableSubInfos != null) { 397 for (int i = 0; i < colorArr.length; i++) { 398 int j; 399 for (j = 0; j < availableSubInfos.size(); j++) { 400 if (colorArr[i] == availableSubInfos.get(j).getIconTint()) { 401 break; 402 } 403 } 404 if (j == availableSubInfos.size()) { 405 return colorArr[i]; 406 } 407 } 408 colorIdx = availableSubInfos.size() % colorArr.length; 409 } 410 return colorArr[colorIdx]; 411 } 412 413 /** 414 * Get the active SubscriptionInfo with the subId key 415 * @param subId The unique SubscriptionInfo key in database 416 * @param callingPackage The package making the IPC. 417 * @return SubscriptionInfo, maybe null if its not active 418 */ 419 @Override 420 public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) { 421 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 422 mContext, subId, callingPackage, "getActiveSubscriptionInfo")) { 423 return null; 424 } 425 426 // Now that all security checks passes, perform the operation as ourselves. 427 final long identity = Binder.clearCallingIdentity(); 428 try { 429 List<SubscriptionInfo> subList = getActiveSubscriptionInfoList( 430 mContext.getOpPackageName()); 431 if (subList != null) { 432 for (SubscriptionInfo si : subList) { 433 if (si.getSubscriptionId() == subId) { 434 if (DBG) { 435 logd("[getActiveSubscriptionInfo]+ subId=" + subId + " subInfo=" + si); 436 } 437 438 return si; 439 } 440 } 441 } 442 if (DBG) { 443 logd("[getActiveSubInfoForSubscriber]- subId=" + subId 444 + " subList=" + subList + " subInfo=null"); 445 } 446 } finally { 447 Binder.restoreCallingIdentity(identity); 448 } 449 450 return null; 451 } 452 453 /** 454 * Get the active SubscriptionInfo associated with the iccId 455 * @param iccId the IccId of SIM card 456 * @param callingPackage The package making the IPC. 457 * @return SubscriptionInfo, maybe null if its not active 458 */ 459 @Override 460 public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage) { 461 // Query the subscriptions unconditionally, and then check whether the caller has access to 462 // the given subscription. 463 final SubscriptionInfo si = getActiveSubscriptionInfoForIccIdInternal(iccId); 464 465 final int subId = si != null 466 ? si.getSubscriptionId() : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 467 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 468 mContext, subId, callingPackage, "getActiveSubscriptionInfoForIccId")) { 469 return null; 470 } 471 472 return si; 473 } 474 475 /** 476 * Get the active SubscriptionInfo associated with the given iccId. The caller *must* perform 477 * permission checks when using this method. 478 */ 479 private SubscriptionInfo getActiveSubscriptionInfoForIccIdInternal(String iccId) { 480 if (iccId == null) { 481 return null; 482 } 483 484 final long identity = Binder.clearCallingIdentity(); 485 try { 486 List<SubscriptionInfo> subList = getActiveSubscriptionInfoList( 487 mContext.getOpPackageName()); 488 if (subList != null) { 489 for (SubscriptionInfo si : subList) { 490 if (iccId.equals(si.getIccId())) { 491 if (DBG) 492 logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si); 493 return si; 494 } 495 } 496 } 497 if (DBG) { 498 logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId 499 + " subList=" + subList + " subInfo=null"); 500 } 501 } finally { 502 Binder.restoreCallingIdentity(identity); 503 } 504 505 return null; 506 } 507 508 /** 509 * Get the active SubscriptionInfo associated with the slotIndex 510 * @param slotIndex the slot which the subscription is inserted 511 * @param callingPackage The package making the IPC. 512 * @return SubscriptionInfo, maybe null if its not active 513 */ 514 @Override 515 public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, 516 String callingPackage) { 517 Phone phone = PhoneFactory.getPhone(slotIndex); 518 if (phone == null) { 519 if (DBG) { 520 loge("[getActiveSubscriptionInfoForSimSlotIndex] no phone, slotIndex=" + slotIndex); 521 } 522 return null; 523 } 524 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 525 mContext, phone.getSubId(), callingPackage, 526 "getActiveSubscriptionInfoForSimSlotIndex")) { 527 return null; 528 } 529 530 // Now that all security checks passes, perform the operation as ourselves. 531 final long identity = Binder.clearCallingIdentity(); 532 try { 533 List<SubscriptionInfo> subList = getActiveSubscriptionInfoList( 534 mContext.getOpPackageName()); 535 if (subList != null) { 536 for (SubscriptionInfo si : subList) { 537 if (si.getSimSlotIndex() == slotIndex) { 538 if (DBG) { 539 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" 540 + slotIndex + " subId=" + si); 541 } 542 return si; 543 } 544 } 545 if (DBG) { 546 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex 547 + " subId=null"); 548 } 549 } else { 550 if (DBG) { 551 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null"); 552 } 553 } 554 } finally { 555 Binder.restoreCallingIdentity(identity); 556 } 557 558 return null; 559 } 560 561 /** 562 * @param callingPackage The package making the IPC. 563 * @return List of all SubscriptionInfo records in database, 564 * include those that were inserted before, maybe empty but not null. 565 * @hide 566 */ 567 @Override 568 public List<SubscriptionInfo> getAllSubInfoList(String callingPackage) { 569 if (DBG) logd("[getAllSubInfoList]+"); 570 571 // This API isn't public, so no need to provide a valid subscription ID - we're not worried 572 // about carrier-privileged callers not having access. 573 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 574 mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, 575 "getAllSubInfoList")) { 576 return null; 577 } 578 579 // Now that all security checks passes, perform the operation as ourselves. 580 final long identity = Binder.clearCallingIdentity(); 581 try { 582 List<SubscriptionInfo> subList = null; 583 subList = getSubInfo(null, null); 584 if (subList != null) { 585 if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return"); 586 } else { 587 if (DBG) logd("[getAllSubInfoList]- no info return"); 588 } 589 return subList; 590 } finally { 591 Binder.restoreCallingIdentity(identity); 592 } 593 } 594 595 /** 596 * Get the SubInfoRecord(s) of the currently inserted SIM(s) 597 * @param callingPackage The package making the IPC. 598 * @return Array list of currently inserted SubInfoRecord(s) 599 */ 600 @Override 601 public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) { 602 if (!isSubInfoReady()) { 603 if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready"); 604 return null; 605 } 606 607 boolean canReadAllPhoneState; 608 try { 609 canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext, 610 SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(), 611 Binder.getCallingUid(), callingPackage, "getActiveSubscriptionInfoList"); 612 } catch (SecurityException e) { 613 canReadAllPhoneState = false; 614 } 615 616 synchronized (mCacheActiveSubInfoList) { 617 // If the caller can read all phone state, just return the full list. 618 if (canReadAllPhoneState) { 619 return new ArrayList<>(mCacheActiveSubInfoList); 620 } 621 622 // Filter the list to only include subscriptions which the caller can manage. 623 return mCacheActiveSubInfoList.stream() 624 .filter(subscriptionInfo -> { 625 try { 626 return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, 627 subscriptionInfo.getSubscriptionId(), callingPackage, 628 "getActiveSubscriptionInfoList"); 629 } catch (SecurityException e) { 630 return false; 631 } 632 }) 633 .collect(Collectors.toList()); 634 } 635 } 636 637 /** 638 * Refresh the cache of SubInfoRecord(s) of the currently inserted SIM(s) 639 */ 640 @VisibleForTesting // For mockito to mock this method 641 public void refreshCachedActiveSubscriptionInfoList() { 642 if (!isSubInfoReady()) { 643 if (DBG_CACHE) { 644 logdl("[refreshCachedActiveSubscriptionInfoList] " 645 + "Sub Controller not ready "); 646 } 647 return; 648 } 649 650 synchronized (mCacheActiveSubInfoList) { 651 mCacheActiveSubInfoList.clear(); 652 List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo( 653 SubscriptionManager.SIM_SLOT_INDEX + ">=0", null); 654 if (activeSubscriptionInfoList != null) { 655 mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList); 656 } 657 if (DBG_CACHE) { 658 if (!mCacheActiveSubInfoList.isEmpty()) { 659 for (SubscriptionInfo si : mCacheActiveSubInfoList) { 660 logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached info=" 661 + si); 662 } 663 } else { 664 logdl("[refreshCachedActiveSubscriptionInfoList]- no info return"); 665 } 666 } 667 } 668 } 669 670 /** 671 * Get the SUB count of active SUB(s) 672 * @param callingPackage The package making the IPC. 673 * @return active SIM count 674 */ 675 @Override 676 public int getActiveSubInfoCount(String callingPackage) { 677 // Let getActiveSubscriptionInfoList perform permission checks / filtering. 678 List<SubscriptionInfo> records = getActiveSubscriptionInfoList(callingPackage); 679 if (records == null) { 680 if (VDBG) logd("[getActiveSubInfoCount] records null"); 681 return 0; 682 } 683 if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size()); 684 return records.size(); 685 } 686 687 /** 688 * Get the SUB count of all SUB(s) in SubscriptoinInfo database 689 * @param callingPackage The package making the IPC. 690 * @return all SIM count in database, include what was inserted before 691 */ 692 @Override 693 public int getAllSubInfoCount(String callingPackage) { 694 if (DBG) logd("[getAllSubInfoCount]+"); 695 696 // This API isn't public, so no need to provide a valid subscription ID - we're not worried 697 // about carrier-privileged callers not having access. 698 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 699 mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, 700 "getAllSubInfoCount")) { 701 return 0; 702 } 703 704 // Now that all security checks passes, perform the operation as ourselves. 705 final long identity = Binder.clearCallingIdentity(); 706 try { 707 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 708 null, null, null, null); 709 try { 710 if (cursor != null) { 711 int count = cursor.getCount(); 712 if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB"); 713 return count; 714 } 715 } finally { 716 if (cursor != null) { 717 cursor.close(); 718 } 719 } 720 if (DBG) logd("[getAllSubInfoCount]- no SUB in DB"); 721 722 return 0; 723 } finally { 724 Binder.restoreCallingIdentity(identity); 725 } 726 } 727 728 /** 729 * @return the maximum number of subscriptions this device will support at any one time. 730 */ 731 @Override 732 public int getActiveSubInfoCountMax() { 733 // FIXME: This valid now but change to use TelephonyDevController in the future 734 return mTelephonyManager.getSimCount(); 735 } 736 737 @Override 738 public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage) { 739 // This API isn't public, so no need to provide a valid subscription ID - we're not worried 740 // about carrier-privileged callers not having access. 741 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 742 mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, 743 "getAvailableSubscriptionInfoList")) { 744 throw new SecurityException("Need READ_PHONE_STATE to call " 745 + " getAvailableSubscriptionInfoList"); 746 } 747 748 // Now that all security checks pass, perform the operation as ourselves. 749 final long identity = Binder.clearCallingIdentity(); 750 try { 751 EuiccManager euiccManager = 752 (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE); 753 if (!euiccManager.isEnabled()) { 754 if (DBG) logdl("[getAvailableSubInfoList] Embedded subscriptions are disabled"); 755 return null; 756 } 757 758 List<SubscriptionInfo> subList = getSubInfo( 759 SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR " 760 + SubscriptionManager.IS_EMBEDDED + "=1", null); 761 762 if (subList != null) { 763 subList.sort(SUBSCRIPTION_INFO_COMPARATOR); 764 765 if (VDBG) logdl("[getAvailableSubInfoList]- " + subList.size() + " infos return"); 766 } else { 767 if (DBG) logdl("[getAvailableSubInfoList]- no info return"); 768 } 769 770 return subList; 771 } finally { 772 Binder.restoreCallingIdentity(identity); 773 } 774 } 775 776 @Override 777 public List<SubscriptionInfo> getAccessibleSubscriptionInfoList(String callingPackage) { 778 EuiccManager euiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE); 779 if (!euiccManager.isEnabled()) { 780 if (DBG) { 781 logdl("[getAccessibleSubInfoList] Embedded subscriptions are disabled"); 782 } 783 return null; 784 } 785 786 // Verify that the given package belongs to the calling UID. 787 mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); 788 789 // Perform the operation as ourselves. If the caller cannot read phone state, they may still 790 // have carrier privileges per the subscription metadata, so we always need to make the 791 // query and then filter the results. 792 final long identity = Binder.clearCallingIdentity(); 793 List<SubscriptionInfo> subList; 794 try { 795 subList = getSubInfo(SubscriptionManager.IS_EMBEDDED + "=1", null); 796 } finally { 797 Binder.restoreCallingIdentity(identity); 798 } 799 800 if (subList == null) { 801 if (DBG) logdl("[getAccessibleSubInfoList] No info returned"); 802 return null; 803 } 804 805 // Filter the list to only include subscriptions which the (restored) caller can manage. 806 List<SubscriptionInfo> filteredList = subList.stream() 807 .filter(subscriptionInfo -> 808 subscriptionInfo.canManageSubscription(mContext, callingPackage)) 809 .sorted(SUBSCRIPTION_INFO_COMPARATOR) 810 .collect(Collectors.toList()); 811 if (VDBG) { 812 logdl("[getAccessibleSubInfoList] " + filteredList.size() + " infos returned"); 813 } 814 return filteredList; 815 } 816 817 /** 818 * Return the list of subscriptions in the database which are either: 819 * <ul> 820 * <li>Embedded (but see note about {@code includeNonRemovableSubscriptions}, or 821 * <li>In the given list of current embedded ICCIDs (which may not yet be in the database, or 822 * which may not currently be marked as embedded). 823 * </ul> 824 * 825 * <p>NOTE: This is not accessible to external processes, so it does not need a permission 826 * check. It is only intended for use by {@link SubscriptionInfoUpdater}. 827 * 828 * @param embeddedIccids all ICCIDs of available embedded subscriptions. This is used to surface 829 * entries for profiles which had been previously deleted. 830 * @param isEuiccRemovable whether the current ICCID is removable. Non-removable subscriptions 831 * will only be returned if the current ICCID is not removable; otherwise, they are left 832 * alone (not returned here unless in the embeddedIccids list) under the assumption that 833 * they will still be accessible when the eUICC containing them is activated. 834 */ 835 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 836 public List<SubscriptionInfo> getSubscriptionInfoListForEmbeddedSubscriptionUpdate( 837 String[] embeddedIccids, boolean isEuiccRemovable) { 838 StringBuilder whereClause = new StringBuilder(); 839 whereClause.append("(").append(SubscriptionManager.IS_EMBEDDED).append("=1"); 840 if (isEuiccRemovable) { 841 // Current eUICC is removable, so don't return non-removable subscriptions (which would 842 // be deleted), as these are expected to still be present on a different, non-removable 843 // eUICC. 844 whereClause.append(" AND ").append(SubscriptionManager.IS_REMOVABLE).append("=1"); 845 } 846 // Else, return both removable and non-removable subscriptions. This is expected to delete 847 // all removable subscriptions, which is desired as they may not be accessible. 848 849 whereClause.append(") OR ").append(SubscriptionManager.ICC_ID).append(" IN ("); 850 // ICCIDs are validated to contain only numbers when passed in, and come from a trusted 851 // app, so no need to escape. 852 for (int i = 0; i < embeddedIccids.length; i++) { 853 if (i > 0) { 854 whereClause.append(","); 855 } 856 whereClause.append("\"").append(embeddedIccids[i]).append("\""); 857 } 858 whereClause.append(")"); 859 860 List<SubscriptionInfo> list = getSubInfo(whereClause.toString(), null); 861 if (list == null) { 862 return Collections.emptyList(); 863 } 864 return list; 865 } 866 867 @Override 868 public void requestEmbeddedSubscriptionInfoListRefresh() { 869 mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, 870 "requestEmbeddedSubscriptionInfoListRefresh"); 871 long token = Binder.clearCallingIdentity(); 872 try { 873 PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(null /* callback */); 874 } finally { 875 Binder.restoreCallingIdentity(token); 876 } 877 } 878 879 /** 880 * Asynchronously refresh the embedded subscription info list. 881 * 882 * @param callback Optional callback to execute after the refresh completes. Must terminate 883 * quickly as it will be called from SubscriptionInfoUpdater's handler thread. 884 */ 885 // No permission check needed as this is not exposed via AIDL. 886 public void requestEmbeddedSubscriptionInfoListRefresh(@Nullable Runnable callback) { 887 PhoneFactory.requestEmbeddedSubscriptionInfoListRefresh(callback); 888 } 889 890 /** 891 * Add a new SubInfoRecord to subinfo database if needed 892 * @param iccId the IccId of the SIM card 893 * @param slotIndex the slot which the SIM is inserted 894 * @return 0 if success, < 0 on error. 895 */ 896 @Override 897 public int addSubInfoRecord(String iccId, int slotIndex) { 898 if (DBG) logdl("[addSubInfoRecord]+ iccId:" + SubscriptionInfo.givePrintableIccid(iccId) + 899 " slotIndex:" + slotIndex); 900 901 enforceModifyPhoneState("addSubInfoRecord"); 902 903 // Now that all security checks passes, perform the operation as ourselves. 904 final long identity = Binder.clearCallingIdentity(); 905 try { 906 if (iccId == null) { 907 if (DBG) logdl("[addSubInfoRecord]- null iccId"); 908 return -1; 909 } 910 911 ContentResolver resolver = mContext.getContentResolver(); 912 Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI, 913 new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, 914 SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE, 915 SubscriptionManager.ICC_ID, SubscriptionManager.CARD_ID}, 916 SubscriptionManager.ICC_ID + "=?" + " OR " + SubscriptionManager.ICC_ID + "=?", 917 new String[]{iccId, IccUtils.getDecimalSubstring(iccId)}, null); 918 919 boolean setDisplayName = false; 920 try { 921 if (cursor == null || !cursor.moveToFirst()) { 922 setDisplayName = true; 923 Uri uri = insertEmptySubInfoRecord(iccId, slotIndex); 924 if (DBG) logdl("[addSubInfoRecord] New record created: " + uri); 925 } else { 926 int subId = cursor.getInt(0); 927 int oldSimInfoId = cursor.getInt(1); 928 int nameSource = cursor.getInt(2); 929 String oldIccId = cursor.getString(3); 930 String oldCardId = cursor.getString(4); 931 ContentValues value = new ContentValues(); 932 933 if (slotIndex != oldSimInfoId) { 934 value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex); 935 } 936 937 if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) { 938 setDisplayName = true; 939 } 940 941 if (oldIccId != null && oldIccId.length() < iccId.length() 942 && (oldIccId.equals(IccUtils.getDecimalSubstring(iccId)))) { 943 value.put(SubscriptionManager.ICC_ID, iccId); 944 } 945 946 UiccCard card = UiccController.getInstance().getUiccCardForPhone(slotIndex); 947 if (card != null) { 948 String cardId = card.getCardId(); 949 if (cardId != null && cardId != oldCardId) { 950 value.put(SubscriptionManager.CARD_ID, cardId); 951 } 952 } 953 954 if (value.size() > 0) { 955 resolver.update(SubscriptionManager.CONTENT_URI, value, 956 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + 957 "=" + Long.toString(subId), null); 958 959 // Refresh the Cache of Active Subscription Info List 960 refreshCachedActiveSubscriptionInfoList(); 961 } 962 963 if (DBG) logdl("[addSubInfoRecord] Record already exists"); 964 } 965 } finally { 966 if (cursor != null) { 967 cursor.close(); 968 } 969 } 970 971 cursor = resolver.query(SubscriptionManager.CONTENT_URI, null, 972 SubscriptionManager.SIM_SLOT_INDEX + "=?", 973 new String[] {String.valueOf(slotIndex)}, null); 974 try { 975 if (cursor != null && cursor.moveToFirst()) { 976 do { 977 int subId = cursor.getInt(cursor.getColumnIndexOrThrow( 978 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID)); 979 // If sSlotIndexToSubId already has the same subId for a slotIndex/phoneId, 980 // do not add it. 981 Integer currentSubId = sSlotIndexToSubId.get(slotIndex); 982 if (currentSubId == null 983 || currentSubId != subId 984 || !SubscriptionManager.isValidSubscriptionId(currentSubId)) { 985 // TODO While two subs active, if user deactivats first 986 // one, need to update the default subId with second one. 987 988 // FIXME: Currently we assume phoneId == slotIndex which in the future 989 // may not be true, for instance with multiple subs per slot. 990 // But is true at the moment. 991 sSlotIndexToSubId.put(slotIndex, subId); 992 int subIdCountMax = getActiveSubInfoCountMax(); 993 int defaultSubId = getDefaultSubId(); 994 if (DBG) { 995 logdl("[addSubInfoRecord]" 996 + " sSlotIndexToSubId.size=" + sSlotIndexToSubId.size() 997 + " slotIndex=" + slotIndex + " subId=" + subId 998 + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax); 999 } 1000 1001 // Set the default sub if not set or if single sim device 1002 if (!SubscriptionManager.isValidSubscriptionId(defaultSubId) 1003 || subIdCountMax == 1) { 1004 setDefaultFallbackSubId(subId); 1005 } 1006 // If single sim device, set this subscription as the default for everything 1007 if (subIdCountMax == 1) { 1008 if (DBG) { 1009 logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId); 1010 } 1011 setDefaultDataSubId(subId); 1012 setDefaultSmsSubId(subId); 1013 setDefaultVoiceSubId(subId); 1014 } 1015 } else { 1016 if (DBG) { 1017 logdl("[addSubInfoRecord] currentSubId != null" 1018 + " && currentSubId is valid, IGNORE"); 1019 } 1020 } 1021 if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotIndex + "," + subId + ")"); 1022 } while (cursor.moveToNext()); 1023 } 1024 } finally { 1025 if (cursor != null) { 1026 cursor.close(); 1027 } 1028 } 1029 1030 // Set Display name after sub id is set above so as to get valid simCarrierName 1031 int subId = getSubIdUsingPhoneId(slotIndex); 1032 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1033 if (DBG) { 1034 logdl("[addSubInfoRecord]- getSubId failed invalid subId = " + subId); 1035 } 1036 return -1; 1037 } 1038 if (setDisplayName) { 1039 String simCarrierName = mTelephonyManager.getSimOperatorName(subId); 1040 String nameToSet; 1041 1042 if (!TextUtils.isEmpty(simCarrierName)) { 1043 nameToSet = simCarrierName; 1044 } else { 1045 nameToSet = "CARD " + Integer.toString(slotIndex + 1); 1046 } 1047 1048 ContentValues value = new ContentValues(); 1049 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 1050 resolver.update(SubscriptionManager.CONTENT_URI, value, 1051 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + 1052 "=" + Long.toString(subId), null); 1053 1054 // Refresh the Cache of Active Subscription Info List 1055 refreshCachedActiveSubscriptionInfoList(); 1056 1057 if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet); 1058 } 1059 1060 // Once the records are loaded, notify DcTracker 1061 sPhones[slotIndex].updateDataConnectionTracker(); 1062 1063 if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubId.size()); 1064 1065 } finally { 1066 Binder.restoreCallingIdentity(identity); 1067 } 1068 return 0; 1069 } 1070 1071 /** 1072 * Insert an empty SubInfo record into the database. 1073 * 1074 * <p>NOTE: This is not accessible to external processes, so it does not need a permission 1075 * check. It is only intended for use by {@link SubscriptionInfoUpdater}. 1076 * 1077 * <p>Precondition: No record exists with this iccId. 1078 */ 1079 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 1080 public Uri insertEmptySubInfoRecord(String iccId, int slotIndex) { 1081 ContentResolver resolver = mContext.getContentResolver(); 1082 ContentValues value = new ContentValues(); 1083 value.put(SubscriptionManager.ICC_ID, iccId); 1084 int color = getUnusedColor(mContext.getOpPackageName()); 1085 // default SIM color differs between slots 1086 value.put(SubscriptionManager.COLOR, color); 1087 value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex); 1088 value.put(SubscriptionManager.CARRIER_NAME, ""); 1089 UiccCard card = UiccController.getInstance().getUiccCardForPhone(slotIndex); 1090 if (card != null) { 1091 String cardId = card.getCardId(); 1092 if (cardId != null) { 1093 value.put(SubscriptionManager.CARD_ID, cardId); 1094 } else { 1095 value.put(SubscriptionManager.CARD_ID, iccId); 1096 } 1097 } else { 1098 value.put(SubscriptionManager.CARD_ID, iccId); 1099 } 1100 1101 Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value); 1102 1103 // Refresh the Cache of Active Subscription Info List 1104 refreshCachedActiveSubscriptionInfoList(); 1105 1106 return uri; 1107 } 1108 1109 /** 1110 * Generate and set carrier text based on input parameters 1111 * @param showPlmn flag to indicate if plmn should be included in carrier text 1112 * @param plmn plmn to be included in carrier text 1113 * @param showSpn flag to indicate if spn should be included in carrier text 1114 * @param spn spn to be included in carrier text 1115 * @return true if carrier text is set, false otherwise 1116 */ 1117 public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn, 1118 String spn) { 1119 synchronized (mLock) { 1120 int subId = getSubIdUsingPhoneId(slotIndex); 1121 if (mContext.getPackageManager().resolveContentProvider( 1122 SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null || 1123 !SubscriptionManager.isValidSubscriptionId(subId)) { 1124 // No place to store this info. Notify registrants of the change anyway as they 1125 // might retrieve the SPN/PLMN text from the SST sticky broadcast. 1126 // TODO: This can be removed once SubscriptionController is not running on devices 1127 // that don't need it, such as TVs. 1128 if (DBG) logd("[setPlmnSpn] No valid subscription to store info"); 1129 notifySubscriptionInfoChanged(); 1130 return false; 1131 } 1132 String carrierText = ""; 1133 if (showPlmn) { 1134 carrierText = plmn; 1135 if (showSpn) { 1136 // Need to show both plmn and spn if both are not same. 1137 if(!Objects.equals(spn, plmn)) { 1138 String separator = mContext.getString( 1139 com.android.internal.R.string.kg_text_message_separator).toString(); 1140 carrierText = new StringBuilder().append(carrierText).append(separator) 1141 .append(spn).toString(); 1142 } 1143 } 1144 } else if (showSpn) { 1145 carrierText = spn; 1146 } 1147 setCarrierText(carrierText, subId); 1148 return true; 1149 } 1150 } 1151 1152 /** 1153 * Set carrier text by simInfo index 1154 * @param text new carrier text 1155 * @param subId the unique SubInfoRecord index in database 1156 * @return the number of records updated 1157 */ 1158 private int setCarrierText(String text, int subId) { 1159 if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId); 1160 1161 enforceModifyPhoneState("setCarrierText"); 1162 1163 // Now that all security checks passes, perform the operation as ourselves. 1164 final long identity = Binder.clearCallingIdentity(); 1165 try { 1166 ContentValues value = new ContentValues(1); 1167 value.put(SubscriptionManager.CARRIER_NAME, text); 1168 1169 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, 1170 value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1171 Long.toString(subId), null); 1172 1173 // Refresh the Cache of Active Subscription Info List 1174 refreshCachedActiveSubscriptionInfoList(); 1175 1176 notifySubscriptionInfoChanged(); 1177 1178 return result; 1179 } finally { 1180 Binder.restoreCallingIdentity(identity); 1181 } 1182 } 1183 1184 /** 1185 * Set SIM color tint by simInfo index 1186 * @param tint the tint color of the SIM 1187 * @param subId the unique SubInfoRecord index in database 1188 * @return the number of records updated 1189 */ 1190 @Override 1191 public int setIconTint(int tint, int subId) { 1192 if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); 1193 1194 enforceModifyPhoneState("setIconTint"); 1195 1196 // Now that all security checks passes, perform the operation as ourselves. 1197 final long identity = Binder.clearCallingIdentity(); 1198 try { 1199 validateSubId(subId); 1200 ContentValues value = new ContentValues(1); 1201 value.put(SubscriptionManager.COLOR, tint); 1202 if (DBG) logd("[setIconTint]- tint:" + tint + " set"); 1203 1204 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, 1205 value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1206 Long.toString(subId), null); 1207 1208 // Refresh the Cache of Active Subscription Info List 1209 refreshCachedActiveSubscriptionInfoList(); 1210 1211 notifySubscriptionInfoChanged(); 1212 1213 return result; 1214 } finally { 1215 Binder.restoreCallingIdentity(identity); 1216 } 1217 } 1218 1219 /** 1220 * Set display name by simInfo index 1221 * @param displayName the display name of SIM card 1222 * @param subId the unique SubInfoRecord index in database 1223 * @return the number of records updated 1224 */ 1225 @Override 1226 public int setDisplayName(String displayName, int subId) { 1227 return setDisplayNameUsingSrc(displayName, subId, -1); 1228 } 1229 1230 /** 1231 * Set display name by simInfo index with name source 1232 * @param displayName the display name of SIM card 1233 * @param subId the unique SubInfoRecord index in database 1234 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 1235 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED 1236 * @return the number of records updated 1237 */ 1238 @Override 1239 public int setDisplayNameUsingSrc(String displayName, int subId, long nameSource) { 1240 if (DBG) { 1241 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId 1242 + " nameSource:" + nameSource); 1243 } 1244 1245 enforceModifyPhoneState("setDisplayNameUsingSrc"); 1246 1247 // Now that all security checks passes, perform the operation as ourselves. 1248 final long identity = Binder.clearCallingIdentity(); 1249 try { 1250 validateSubId(subId); 1251 String nameToSet; 1252 if (displayName == null) { 1253 nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES); 1254 } else { 1255 nameToSet = displayName; 1256 } 1257 ContentValues value = new ContentValues(1); 1258 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 1259 if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) { 1260 if (DBG) logd("Set nameSource=" + nameSource); 1261 value.put(SubscriptionManager.NAME_SOURCE, nameSource); 1262 } 1263 if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); 1264 // TODO(b/33075886): If this is an embedded subscription, we must also save the new name 1265 // to the eSIM itself. Currently it will be blown away the next time the subscription 1266 // list is updated. 1267 1268 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, 1269 value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1270 Long.toString(subId), null); 1271 1272 // Refresh the Cache of Active Subscription Info List 1273 refreshCachedActiveSubscriptionInfoList(); 1274 1275 notifySubscriptionInfoChanged(); 1276 1277 return result; 1278 } finally { 1279 Binder.restoreCallingIdentity(identity); 1280 } 1281 } 1282 1283 /** 1284 * Set phone number by subId 1285 * @param number the phone number of the SIM 1286 * @param subId the unique SubInfoRecord index in database 1287 * @return the number of records updated 1288 */ 1289 @Override 1290 public int setDisplayNumber(String number, int subId) { 1291 if (DBG) logd("[setDisplayNumber]+ subId:" + subId); 1292 1293 enforceModifyPhoneState("setDisplayNumber"); 1294 1295 // Now that all security checks passes, perform the operation as ourselves. 1296 final long identity = Binder.clearCallingIdentity(); 1297 try { 1298 validateSubId(subId); 1299 int result; 1300 int phoneId = getPhoneId(subId); 1301 1302 if (number == null || phoneId < 0 || 1303 phoneId >= mTelephonyManager.getPhoneCount()) { 1304 if (DBG) logd("[setDispalyNumber]- fail"); 1305 return -1; 1306 } 1307 ContentValues value = new ContentValues(1); 1308 value.put(SubscriptionManager.NUMBER, number); 1309 1310 // This function had a call to update number on the SIM (Phone.setLine1Number()) but 1311 // that was removed as there doesn't seem to be a reason for that. If it is added 1312 // back, watch out for deadlocks. 1313 1314 result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 1315 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID 1316 + "=" + Long.toString(subId), null); 1317 1318 // Refresh the Cache of Active Subscription Info List 1319 refreshCachedActiveSubscriptionInfoList(); 1320 1321 if (DBG) logd("[setDisplayNumber]- update result :" + result); 1322 notifySubscriptionInfoChanged(); 1323 1324 return result; 1325 } finally { 1326 Binder.restoreCallingIdentity(identity); 1327 } 1328 } 1329 1330 /** 1331 * Set data roaming by simInfo index 1332 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 1333 * @param subId the unique SubInfoRecord index in database 1334 * @return the number of records updated 1335 */ 1336 @Override 1337 public int setDataRoaming(int roaming, int subId) { 1338 if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 1339 1340 enforceModifyPhoneState("setDataRoaming"); 1341 1342 // Now that all security checks passes, perform the operation as ourselves. 1343 final long identity = Binder.clearCallingIdentity(); 1344 try { 1345 validateSubId(subId); 1346 if (roaming < 0) { 1347 if (DBG) logd("[setDataRoaming]- fail"); 1348 return -1; 1349 } 1350 ContentValues value = new ContentValues(1); 1351 value.put(SubscriptionManager.DATA_ROAMING, roaming); 1352 if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set"); 1353 1354 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, 1355 value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 1356 Long.toString(subId), null); 1357 1358 // Refresh the Cache of Active Subscription Info List 1359 refreshCachedActiveSubscriptionInfoList(); 1360 1361 notifySubscriptionInfoChanged(); 1362 1363 return result; 1364 } finally { 1365 Binder.restoreCallingIdentity(identity); 1366 } 1367 } 1368 1369 /** 1370 * Set MCC/MNC by subscription ID 1371 * @param mccMnc MCC/MNC associated with the subscription 1372 * @param subId the unique SubInfoRecord index in database 1373 * @return the number of records updated 1374 */ 1375 public int setMccMnc(String mccMnc, int subId) { 1376 int mcc = 0; 1377 int mnc = 0; 1378 try { 1379 mcc = Integer.parseInt(mccMnc.substring(0,3)); 1380 mnc = Integer.parseInt(mccMnc.substring(3)); 1381 } catch (NumberFormatException e) { 1382 loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc); 1383 } 1384 if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId); 1385 ContentValues value = new ContentValues(2); 1386 value.put(SubscriptionManager.MCC, mcc); 1387 value.put(SubscriptionManager.MNC, mnc); 1388 1389 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 1390 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 1391 1392 // Refresh the Cache of Active Subscription Info List 1393 refreshCachedActiveSubscriptionInfoList(); 1394 1395 notifySubscriptionInfoChanged(); 1396 1397 return result; 1398 } 1399 1400 @Override 1401 public int getSlotIndex(int subId) { 1402 if (VDBG) printStackTrace("[getSlotIndex] subId=" + subId); 1403 1404 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1405 subId = getDefaultSubId(); 1406 } 1407 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1408 if (DBG) logd("[getSlotIndex]- subId invalid"); 1409 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 1410 } 1411 1412 int size = sSlotIndexToSubId.size(); 1413 1414 if (size == 0) 1415 { 1416 if (DBG) logd("[getSlotIndex]- size == 0, return SIM_NOT_INSERTED instead"); 1417 return SubscriptionManager.SIM_NOT_INSERTED; 1418 } 1419 1420 for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) { 1421 int sim = entry.getKey(); 1422 int sub = entry.getValue(); 1423 1424 if (subId == sub) 1425 { 1426 if (VDBG) logv("[getSlotIndex]- return = " + sim); 1427 return sim; 1428 } 1429 } 1430 1431 if (DBG) logd("[getSlotIndex]- return fail"); 1432 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 1433 } 1434 1435 /** 1436 * Return the subId for specified slot Id. 1437 * @deprecated 1438 */ 1439 @Override 1440 @Deprecated 1441 public int[] getSubId(int slotIndex) { 1442 if (VDBG) printStackTrace("[getSubId]+ slotIndex=" + slotIndex); 1443 1444 // Map default slotIndex to the current default subId. 1445 // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous 1446 // as a slot maybe used for multiple different type of "connections" 1447 // such as: voice, data and sms. But we're doing the best we can and using 1448 // getDefaultSubId which makes a best guess. 1449 if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { 1450 slotIndex = getSlotIndex(getDefaultSubId()); 1451 if (VDBG) logd("[getSubId] map default slotIndex=" + slotIndex); 1452 } 1453 1454 // Check that we have a valid slotIndex 1455 if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { 1456 if (DBG) logd("[getSubId]- invalid slotIndex=" + slotIndex); 1457 return null; 1458 } 1459 1460 // Check if we've got any SubscriptionInfo records using slotIndexToSubId as a surrogate. 1461 int size = sSlotIndexToSubId.size(); 1462 if (size == 0) { 1463 if (VDBG) { 1464 logd("[getSubId]- sSlotIndexToSubId.size == 0, return DummySubIds slotIndex=" 1465 + slotIndex); 1466 } 1467 return getDummySubIds(slotIndex); 1468 } 1469 1470 // Create an array of subIds that are in this slot? 1471 ArrayList<Integer> subIds = new ArrayList<Integer>(); 1472 for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) { 1473 int slot = entry.getKey(); 1474 int sub = entry.getValue(); 1475 if (slotIndex == slot) { 1476 subIds.add(sub); 1477 } 1478 } 1479 1480 // Convert ArrayList to array 1481 int numSubIds = subIds.size(); 1482 if (numSubIds > 0) { 1483 int[] subIdArr = new int[numSubIds]; 1484 for (int i = 0; i < numSubIds; i++) { 1485 subIdArr[i] = subIds.get(i); 1486 } 1487 if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr); 1488 return subIdArr; 1489 } else { 1490 if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIndex=" + slotIndex); 1491 return getDummySubIds(slotIndex); 1492 } 1493 } 1494 1495 @Override 1496 public int getPhoneId(int subId) { 1497 if (VDBG) printStackTrace("[getPhoneId] subId=" + subId); 1498 int phoneId; 1499 1500 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1501 subId = getDefaultSubId(); 1502 if (DBG) logdl("[getPhoneId] asked for default subId=" + subId); 1503 } 1504 1505 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1506 if (VDBG) { 1507 logdl("[getPhoneId]- invalid subId return=" 1508 + SubscriptionManager.INVALID_PHONE_INDEX); 1509 } 1510 return SubscriptionManager.INVALID_PHONE_INDEX; 1511 } 1512 1513 int size = sSlotIndexToSubId.size(); 1514 if (size == 0) { 1515 phoneId = mDefaultPhoneId; 1516 if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId); 1517 return phoneId; 1518 } 1519 1520 // FIXME: Assumes phoneId == slotIndex 1521 for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) { 1522 int sim = entry.getKey(); 1523 int sub = entry.getValue(); 1524 1525 if (subId == sub) { 1526 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim); 1527 return sim; 1528 } 1529 } 1530 1531 phoneId = mDefaultPhoneId; 1532 if (DBG) { 1533 logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId); 1534 } 1535 return phoneId; 1536 1537 } 1538 1539 private int[] getDummySubIds(int slotIndex) { 1540 // FIXME: Remove notion of Dummy SUBSCRIPTION_ID. 1541 // I tested this returning null as no one appears to care, 1542 // but no connection came up on sprout with two sims. 1543 // We need to figure out why and hopefully remove DummySubsIds!!! 1544 int numSubs = getActiveSubInfoCountMax(); 1545 if (numSubs > 0) { 1546 int[] dummyValues = new int[numSubs]; 1547 for (int i = 0; i < numSubs; i++) { 1548 dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIndex; 1549 } 1550 if (VDBG) { 1551 logd("getDummySubIds: slotIndex=" + slotIndex 1552 + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]); 1553 } 1554 return dummyValues; 1555 } else { 1556 return null; 1557 } 1558 } 1559 1560 /** 1561 * @return the number of records cleared 1562 */ 1563 @Override 1564 public int clearSubInfo() { 1565 enforceModifyPhoneState("clearSubInfo"); 1566 1567 // Now that all security checks passes, perform the operation as ourselves. 1568 final long identity = Binder.clearCallingIdentity(); 1569 try { 1570 int size = sSlotIndexToSubId.size(); 1571 1572 if (size == 0) { 1573 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size); 1574 return 0; 1575 } 1576 1577 sSlotIndexToSubId.clear(); 1578 if (DBG) logdl("[clearSubInfo]- clear size=" + size); 1579 return size; 1580 } finally { 1581 Binder.restoreCallingIdentity(identity); 1582 } 1583 } 1584 1585 private void logvl(String msg) { 1586 logv(msg); 1587 mLocalLog.log(msg); 1588 } 1589 1590 private void logv(String msg) { 1591 Rlog.v(LOG_TAG, msg); 1592 } 1593 1594 private void logdl(String msg) { 1595 logd(msg); 1596 mLocalLog.log(msg); 1597 } 1598 1599 private static void slogd(String msg) { 1600 Rlog.d(LOG_TAG, msg); 1601 } 1602 1603 private void logd(String msg) { 1604 Rlog.d(LOG_TAG, msg); 1605 } 1606 1607 private void logel(String msg) { 1608 loge(msg); 1609 mLocalLog.log(msg); 1610 } 1611 1612 private void loge(String msg) { 1613 Rlog.e(LOG_TAG, msg); 1614 } 1615 1616 @Override 1617 public int getDefaultSubId() { 1618 int subId; 1619 boolean isVoiceCapable = mContext.getResources().getBoolean( 1620 com.android.internal.R.bool.config_voice_capable); 1621 if (isVoiceCapable) { 1622 subId = getDefaultVoiceSubId(); 1623 if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId); 1624 } else { 1625 subId = getDefaultDataSubId(); 1626 if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId); 1627 } 1628 if (!isActiveSubId(subId)) { 1629 subId = mDefaultFallbackSubId; 1630 if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId); 1631 } 1632 if (VDBG) logv("[getDefaultSubId]- value = " + subId); 1633 return subId; 1634 } 1635 1636 @Override 1637 public void setDefaultSmsSubId(int subId) { 1638 enforceModifyPhoneState("setDefaultSmsSubId"); 1639 1640 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1641 throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID"); 1642 } 1643 if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId); 1644 Settings.Global.putInt(mContext.getContentResolver(), 1645 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId); 1646 broadcastDefaultSmsSubIdChanged(subId); 1647 } 1648 1649 private void broadcastDefaultSmsSubIdChanged(int subId) { 1650 // Broadcast an Intent for default sms sub change 1651 if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId); 1652 Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED); 1653 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 1654 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1655 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1656 intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); 1657 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1658 } 1659 1660 @Override 1661 public int getDefaultSmsSubId() { 1662 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1663 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, 1664 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1665 if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId); 1666 return subId; 1667 } 1668 1669 @Override 1670 public void setDefaultVoiceSubId(int subId) { 1671 enforceModifyPhoneState("setDefaultVoiceSubId"); 1672 1673 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1674 throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID"); 1675 } 1676 if (DBG) logdl("[setDefaultVoiceSubId] subId=" + subId); 1677 Settings.Global.putInt(mContext.getContentResolver(), 1678 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); 1679 broadcastDefaultVoiceSubIdChanged(subId); 1680 } 1681 1682 /** 1683 * Broadcast intent of ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED. 1684 * @hide 1685 */ 1686 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 1687 public void broadcastDefaultVoiceSubIdChanged(int subId) { 1688 // Broadcast an Intent for default voice sub change 1689 if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId); 1690 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 1691 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 1692 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1693 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1694 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1695 } 1696 1697 @Override 1698 public int getDefaultVoiceSubId() { 1699 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1700 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, 1701 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1702 if (VDBG) slogd("[getDefaultVoiceSubId] subId=" + subId); 1703 return subId; 1704 } 1705 1706 @Override 1707 public int getDefaultDataSubId() { 1708 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1709 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, 1710 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1711 if (VDBG) logd("[getDefaultDataSubId] subId= " + subId); 1712 return subId; 1713 } 1714 1715 @Override 1716 public void setDefaultDataSubId(int subId) { 1717 enforceModifyPhoneState("setDefaultDataSubId"); 1718 1719 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1720 throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID"); 1721 } 1722 1723 ProxyController proxyController = ProxyController.getInstance(); 1724 int len = sPhones.length; 1725 logdl("[setDefaultDataSubId] num phones=" + len + ", subId=" + subId); 1726 1727 if (SubscriptionManager.isValidSubscriptionId(subId)) { 1728 // Only re-map modems if the new default data sub is valid 1729 RadioAccessFamily[] rafs = new RadioAccessFamily[len]; 1730 boolean atLeastOneMatch = false; 1731 for (int phoneId = 0; phoneId < len; phoneId++) { 1732 Phone phone = sPhones[phoneId]; 1733 int raf; 1734 int id = phone.getSubId(); 1735 if (id == subId) { 1736 // TODO Handle the general case of N modems and M subscriptions. 1737 raf = proxyController.getMaxRafSupported(); 1738 atLeastOneMatch = true; 1739 } else { 1740 // TODO Handle the general case of N modems and M subscriptions. 1741 raf = proxyController.getMinRafSupported(); 1742 } 1743 logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf); 1744 rafs[phoneId] = new RadioAccessFamily(phoneId, raf); 1745 } 1746 if (atLeastOneMatch) { 1747 proxyController.setRadioCapability(rafs); 1748 } else { 1749 if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating."); 1750 } 1751 } 1752 1753 // FIXME is this still needed? 1754 updateAllDataConnectionTrackers(); 1755 1756 Settings.Global.putInt(mContext.getContentResolver(), 1757 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); 1758 broadcastDefaultDataSubIdChanged(subId); 1759 } 1760 1761 private void updateAllDataConnectionTrackers() { 1762 // Tell Phone Proxies to update data connection tracker 1763 int len = sPhones.length; 1764 if (DBG) logdl("[updateAllDataConnectionTrackers] sPhones.length=" + len); 1765 for (int phoneId = 0; phoneId < len; phoneId++) { 1766 if (DBG) logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId); 1767 sPhones[phoneId].updateDataConnectionTracker(); 1768 } 1769 } 1770 1771 private void broadcastDefaultDataSubIdChanged(int subId) { 1772 // Broadcast an Intent for default data sub change 1773 if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId); 1774 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 1775 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 1776 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1777 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1778 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1779 } 1780 1781 /* Sets the default subscription. If only one sub is active that 1782 * sub is set as default subId. If two or more sub's are active 1783 * the first sub is set as default subscription 1784 */ 1785 private void setDefaultFallbackSubId(int subId) { 1786 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1787 throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID"); 1788 } 1789 if (DBG) logdl("[setDefaultFallbackSubId] subId=" + subId); 1790 if (SubscriptionManager.isValidSubscriptionId(subId)) { 1791 int phoneId = getPhoneId(subId); 1792 if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount() 1793 || mTelephonyManager.getSimCount() == 1)) { 1794 if (DBG) logdl("[setDefaultFallbackSubId] set mDefaultFallbackSubId=" + subId); 1795 mDefaultFallbackSubId = subId; 1796 // Update MCC MNC device configuration information 1797 String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId); 1798 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); 1799 1800 // Broadcast an Intent for default sub change 1801 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); 1802 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING 1803 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1804 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId); 1805 if (DBG) { 1806 logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" + 1807 phoneId + " subId=" + subId); 1808 } 1809 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1810 } else { 1811 if (DBG) { 1812 logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId 1813 + " subId=" + subId); 1814 } 1815 } 1816 } 1817 } 1818 1819 @Override 1820 public void clearDefaultsForInactiveSubIds() { 1821 enforceModifyPhoneState("clearDefaultsForInactiveSubIds"); 1822 1823 // Now that all security checks passes, perform the operation as ourselves. 1824 final long identity = Binder.clearCallingIdentity(); 1825 try { 1826 final List<SubscriptionInfo> records = getActiveSubscriptionInfoList( 1827 mContext.getOpPackageName()); 1828 if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records); 1829 if (shouldDefaultBeCleared(records, getDefaultDataSubId())) { 1830 if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id"); 1831 setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1832 } 1833 if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) { 1834 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id"); 1835 setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1836 } 1837 if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) { 1838 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id"); 1839 setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1840 } 1841 } finally { 1842 Binder.restoreCallingIdentity(identity); 1843 } 1844 } 1845 1846 private boolean shouldDefaultBeCleared(List<SubscriptionInfo> records, int subId) { 1847 if (DBG) logdl("[shouldDefaultBeCleared: subId] " + subId); 1848 if (records == null) { 1849 if (DBG) logdl("[shouldDefaultBeCleared] return true no records subId=" + subId); 1850 return true; 1851 } 1852 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1853 // If the subId parameter is not valid its already cleared so return false. 1854 if (DBG) logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId); 1855 return false; 1856 } 1857 for (SubscriptionInfo record : records) { 1858 int id = record.getSubscriptionId(); 1859 if (DBG) logdl("[shouldDefaultBeCleared] Record.id: " + id); 1860 if (id == subId) { 1861 logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId); 1862 return false; 1863 } 1864 } 1865 if (DBG) logdl("[shouldDefaultBeCleared] return true not active subId=" + subId); 1866 return true; 1867 } 1868 1869 // FIXME: We need we should not be assuming phoneId == slotIndex as it will not be true 1870 // when there are multiple subscriptions per sim and probably for other reasons. 1871 public int getSubIdUsingPhoneId(int phoneId) { 1872 int[] subIds = getSubId(phoneId); 1873 if (subIds == null || subIds.length == 0) { 1874 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 1875 } 1876 return subIds[0]; 1877 } 1878 1879 /** Must be public for access from instrumentation tests. */ 1880 @VisibleForTesting 1881 public List<SubscriptionInfo> getSubInfoUsingSlotIndexPrivileged(int slotIndex, 1882 boolean needCheck) { 1883 if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]+ slotIndex:" + slotIndex); 1884 if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { 1885 slotIndex = getSlotIndex(getDefaultSubId()); 1886 } 1887 if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { 1888 if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- invalid slotIndex"); 1889 return null; 1890 } 1891 1892 if (needCheck && !isSubInfoReady()) { 1893 if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- not ready"); 1894 return null; 1895 } 1896 1897 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 1898 null, SubscriptionManager.SIM_SLOT_INDEX + "=?", 1899 new String[]{String.valueOf(slotIndex)}, null); 1900 ArrayList<SubscriptionInfo> subList = null; 1901 try { 1902 if (cursor != null) { 1903 while (cursor.moveToNext()) { 1904 SubscriptionInfo subInfo = getSubInfoRecord(cursor); 1905 if (subInfo != null) { 1906 if (subList == null) { 1907 subList = new ArrayList<SubscriptionInfo>(); 1908 } 1909 subList.add(subInfo); 1910 } 1911 } 1912 } 1913 } finally { 1914 if (cursor != null) { 1915 cursor.close(); 1916 } 1917 } 1918 if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return"); 1919 1920 return subList; 1921 } 1922 1923 private void validateSubId(int subId) { 1924 if (DBG) logd("validateSubId subId: " + subId); 1925 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1926 throw new RuntimeException("Invalid sub id passed as parameter"); 1927 } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1928 throw new RuntimeException("Default sub id passed as parameter"); 1929 } 1930 } 1931 1932 public void updatePhonesAvailability(Phone[] phones) { 1933 sPhones = phones; 1934 } 1935 1936 /** 1937 * @return the list of subId's that are active, is never null but the length maybe 0. 1938 */ 1939 @Override 1940 public int[] getActiveSubIdList() { 1941 Set<Entry<Integer, Integer>> simInfoSet = new HashSet<>(sSlotIndexToSubId.entrySet()); 1942 1943 int[] subIdArr = new int[simInfoSet.size()]; 1944 int i = 0; 1945 for (Entry<Integer, Integer> entry: simInfoSet) { 1946 int sub = entry.getValue(); 1947 subIdArr[i] = sub; 1948 i++; 1949 } 1950 1951 if (VDBG) { 1952 logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet + " subIdArr.length=" 1953 + subIdArr.length); 1954 } 1955 return subIdArr; 1956 } 1957 1958 @Override 1959 public boolean isActiveSubId(int subId) { 1960 boolean retVal = SubscriptionManager.isValidSubscriptionId(subId) 1961 && sSlotIndexToSubId.containsValue(subId); 1962 1963 if (VDBG) logdl("[isActiveSubId]- " + retVal); 1964 return retVal; 1965 } 1966 1967 /** 1968 * Get the SIM state for the slot index 1969 * @return SIM state as the ordinal of {@See IccCardConstants.State} 1970 */ 1971 @Override 1972 public int getSimStateForSlotIndex(int slotIndex) { 1973 State simState; 1974 String err; 1975 if (slotIndex < 0) { 1976 simState = IccCardConstants.State.UNKNOWN; 1977 err = "invalid slotIndex"; 1978 } else { 1979 Phone phone = PhoneFactory.getPhone(slotIndex); 1980 if (phone == null) { 1981 simState = IccCardConstants.State.UNKNOWN; 1982 err = "phone == null"; 1983 } else { 1984 IccCard icc = phone.getIccCard(); 1985 if (icc == null) { 1986 simState = IccCardConstants.State.UNKNOWN; 1987 err = "icc == null"; 1988 } else { 1989 simState = icc.getState(); 1990 err = ""; 1991 } 1992 } 1993 } 1994 if (VDBG) { 1995 logd("getSimStateForSlotIndex: " + err + " simState=" + simState 1996 + " ordinal=" + simState.ordinal() + " slotIndex=" + slotIndex); 1997 } 1998 return simState.ordinal(); 1999 } 2000 2001 /** 2002 * Store properties associated with SubscriptionInfo in database 2003 * @param subId Subscription Id of Subscription 2004 * @param propKey Column name in database associated with SubscriptionInfo 2005 * @param propValue Value to store in DB for particular subId & column name 2006 * @hide 2007 */ 2008 @Override 2009 public void setSubscriptionProperty(int subId, String propKey, String propValue) { 2010 enforceModifyPhoneState("setSubscriptionProperty"); 2011 final long token = Binder.clearCallingIdentity(); 2012 ContentResolver resolver = mContext.getContentResolver(); 2013 2014 setSubscriptionPropertyIntoContentResolver(subId, propKey, propValue, resolver); 2015 2016 // Refresh the Cache of Active Subscription Info List 2017 refreshCachedActiveSubscriptionInfoList(); 2018 2019 Binder.restoreCallingIdentity(token); 2020 } 2021 2022 private static void setSubscriptionPropertyIntoContentResolver( 2023 int subId, String propKey, String propValue, ContentResolver resolver) { 2024 ContentValues value = new ContentValues(); 2025 switch (propKey) { 2026 case SubscriptionManager.CB_EXTREME_THREAT_ALERT: 2027 case SubscriptionManager.CB_SEVERE_THREAT_ALERT: 2028 case SubscriptionManager.CB_AMBER_ALERT: 2029 case SubscriptionManager.CB_EMERGENCY_ALERT: 2030 case SubscriptionManager.CB_ALERT_SOUND_DURATION: 2031 case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL: 2032 case SubscriptionManager.CB_ALERT_VIBRATE: 2033 case SubscriptionManager.CB_ALERT_SPEECH: 2034 case SubscriptionManager.CB_ETWS_TEST_ALERT: 2035 case SubscriptionManager.CB_CHANNEL_50_ALERT: 2036 case SubscriptionManager.CB_CMAS_TEST_ALERT: 2037 case SubscriptionManager.CB_OPT_OUT_DIALOG: 2038 case SubscriptionManager.ENHANCED_4G_MODE_ENABLED: 2039 case SubscriptionManager.VT_IMS_ENABLED: 2040 case SubscriptionManager.WFC_IMS_ENABLED: 2041 case SubscriptionManager.WFC_IMS_MODE: 2042 case SubscriptionManager.WFC_IMS_ROAMING_MODE: 2043 case SubscriptionManager.WFC_IMS_ROAMING_ENABLED: 2044 value.put(propKey, Integer.parseInt(propValue)); 2045 break; 2046 default: 2047 if (DBG) slogd("Invalid column name"); 2048 break; 2049 } 2050 2051 resolver.update(SubscriptionManager.CONTENT_URI, value, 2052 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + 2053 "=" + Integer.toString(subId), null); 2054 } 2055 2056 /** 2057 * Store properties associated with SubscriptionInfo in database 2058 * @param subId Subscription Id of Subscription 2059 * @param propKey Column name in SubscriptionInfo database 2060 * @return Value associated with subId and propKey column in database 2061 * @hide 2062 */ 2063 @Override 2064 public String getSubscriptionProperty(int subId, String propKey, String callingPackage) { 2065 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( 2066 mContext, subId, callingPackage, "getSubscriptionProperty")) { 2067 return null; 2068 } 2069 String resultValue = null; 2070 ContentResolver resolver = mContext.getContentResolver(); 2071 Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI, 2072 new String[]{propKey}, 2073 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?", 2074 new String[]{subId + ""}, null); 2075 2076 try { 2077 if (cursor != null) { 2078 if (cursor.moveToFirst()) { 2079 switch (propKey) { 2080 case SubscriptionManager.CB_EXTREME_THREAT_ALERT: 2081 case SubscriptionManager.CB_SEVERE_THREAT_ALERT: 2082 case SubscriptionManager.CB_AMBER_ALERT: 2083 case SubscriptionManager.CB_EMERGENCY_ALERT: 2084 case SubscriptionManager.CB_ALERT_SOUND_DURATION: 2085 case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL: 2086 case SubscriptionManager.CB_ALERT_VIBRATE: 2087 case SubscriptionManager.CB_ALERT_SPEECH: 2088 case SubscriptionManager.CB_ETWS_TEST_ALERT: 2089 case SubscriptionManager.CB_CHANNEL_50_ALERT: 2090 case SubscriptionManager.CB_CMAS_TEST_ALERT: 2091 case SubscriptionManager.CB_OPT_OUT_DIALOG: 2092 case SubscriptionManager.ENHANCED_4G_MODE_ENABLED: 2093 case SubscriptionManager.VT_IMS_ENABLED: 2094 case SubscriptionManager.WFC_IMS_ENABLED: 2095 case SubscriptionManager.WFC_IMS_MODE: 2096 case SubscriptionManager.WFC_IMS_ROAMING_MODE: 2097 case SubscriptionManager.WFC_IMS_ROAMING_ENABLED: 2098 resultValue = cursor.getInt(0) + ""; 2099 break; 2100 default: 2101 if(DBG) logd("Invalid column name"); 2102 break; 2103 } 2104 } else { 2105 if(DBG) logd("Valid row not present in db"); 2106 } 2107 } else { 2108 if(DBG) logd("Query failed"); 2109 } 2110 } finally { 2111 if (cursor != null) { 2112 cursor.close(); 2113 } 2114 } 2115 if (DBG) logd("getSubscriptionProperty Query value = " + resultValue); 2116 return resultValue; 2117 } 2118 2119 private static void printStackTrace(String msg) { 2120 RuntimeException re = new RuntimeException(); 2121 slogd("StackTrace - " + msg); 2122 StackTraceElement[] st = re.getStackTrace(); 2123 boolean first = true; 2124 for (StackTraceElement ste : st) { 2125 if (first) { 2126 first = false; 2127 } else { 2128 slogd(ste.toString()); 2129 } 2130 } 2131 } 2132 2133 @Override 2134 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2135 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, 2136 "Requires DUMP"); 2137 final long token = Binder.clearCallingIdentity(); 2138 try { 2139 pw.println("SubscriptionController:"); 2140 pw.println(" mLastISubServiceRegTime=" + mLastISubServiceRegTime); 2141 pw.println(" defaultSubId=" + getDefaultSubId()); 2142 pw.println(" defaultDataSubId=" + getDefaultDataSubId()); 2143 pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId()); 2144 pw.println(" defaultSmsSubId=" + getDefaultSmsSubId()); 2145 2146 pw.println(" defaultDataPhoneId=" + SubscriptionManager 2147 .from(mContext).getDefaultDataPhoneId()); 2148 pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId()); 2149 pw.println(" defaultSmsPhoneId=" + SubscriptionManager 2150 .from(mContext).getDefaultSmsPhoneId()); 2151 pw.flush(); 2152 2153 for (Entry<Integer, Integer> entry : sSlotIndexToSubId.entrySet()) { 2154 pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subId=" + entry.getValue()); 2155 } 2156 pw.flush(); 2157 pw.println("++++++++++++++++++++++++++++++++"); 2158 2159 List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList( 2160 mContext.getOpPackageName()); 2161 if (sirl != null) { 2162 pw.println(" ActiveSubInfoList:"); 2163 for (SubscriptionInfo entry : sirl) { 2164 pw.println(" " + entry.toString()); 2165 } 2166 } else { 2167 pw.println(" ActiveSubInfoList: is null"); 2168 } 2169 pw.flush(); 2170 pw.println("++++++++++++++++++++++++++++++++"); 2171 2172 sirl = getAllSubInfoList(mContext.getOpPackageName()); 2173 if (sirl != null) { 2174 pw.println(" AllSubInfoList:"); 2175 for (SubscriptionInfo entry : sirl) { 2176 pw.println(" " + entry.toString()); 2177 } 2178 } else { 2179 pw.println(" AllSubInfoList: is null"); 2180 } 2181 pw.flush(); 2182 pw.println("++++++++++++++++++++++++++++++++"); 2183 2184 mLocalLog.dump(fd, pw, args); 2185 pw.flush(); 2186 pw.println("++++++++++++++++++++++++++++++++"); 2187 pw.flush(); 2188 } finally { 2189 Binder.restoreCallingIdentity(token); 2190 } 2191 } 2192 2193 /** 2194 * Migrating Ims settings from global setting to subscription DB, if not already done. 2195 */ 2196 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 2197 public void migrateImsSettings() { 2198 migrateImsSettingHelper( 2199 Settings.Global.ENHANCED_4G_MODE_ENABLED, 2200 SubscriptionManager.ENHANCED_4G_MODE_ENABLED); 2201 migrateImsSettingHelper( 2202 Settings.Global.VT_IMS_ENABLED, 2203 SubscriptionManager.VT_IMS_ENABLED); 2204 migrateImsSettingHelper( 2205 Settings.Global.WFC_IMS_ENABLED, 2206 SubscriptionManager.WFC_IMS_ENABLED); 2207 migrateImsSettingHelper( 2208 Settings.Global.WFC_IMS_MODE, 2209 SubscriptionManager.WFC_IMS_MODE); 2210 migrateImsSettingHelper( 2211 Settings.Global.WFC_IMS_ROAMING_MODE, 2212 SubscriptionManager.WFC_IMS_ROAMING_MODE); 2213 migrateImsSettingHelper( 2214 Settings.Global.WFC_IMS_ROAMING_ENABLED, 2215 SubscriptionManager.WFC_IMS_ROAMING_ENABLED); 2216 } 2217 2218 private void migrateImsSettingHelper(String settingGlobal, String subscriptionProperty) { 2219 ContentResolver resolver = mContext.getContentResolver(); 2220 int defaultSubId = getDefaultVoiceSubId(); 2221 try { 2222 int prevSetting = Settings.Global.getInt(resolver, settingGlobal); 2223 2224 if (prevSetting != DEPRECATED_SETTING) { 2225 // Write previous setting into Subscription DB. 2226 setSubscriptionPropertyIntoContentResolver(defaultSubId, subscriptionProperty, 2227 Integer.toString(prevSetting), resolver); 2228 // Write global setting value with DEPRECATED_SETTING making sure 2229 // migration only happen once. 2230 Settings.Global.putInt(resolver, settingGlobal, DEPRECATED_SETTING); 2231 } 2232 } catch (Settings.SettingNotFoundException e) { 2233 } 2234 } 2235 } 2236