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