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