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