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