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