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