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