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