SubscriptionController.java revision 36d48c696e65a6e927341fb197b18f38d2239806
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 + " 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, we are done. 732 // TODO: This can be removed once SubscriptionController is not running on devices 733 // that don't need it, such as TVs. 734 return false; 735 } 736 String carrierText = ""; 737 if (showPlmn) { 738 carrierText = plmn; 739 if (showSpn) { 740 // Need to show both plmn and spn. 741 String separator = mContext.getString( 742 com.android.internal.R.string.kg_text_message_separator).toString(); 743 carrierText = new StringBuilder().append(carrierText).append(separator) 744 .append(spn).toString(); 745 } 746 } else if (showSpn) { 747 carrierText = spn; 748 } 749 for (int i = 0; i < subIds.length; i++) { 750 setCarrierText(carrierText, subIds[i]); 751 } 752 return true; 753 } 754 } 755 756 /** 757 * Set carrier text by simInfo index 758 * @param text new carrier text 759 * @param subId the unique SubInfoRecord index in database 760 * @return the number of records updated 761 */ 762 private int setCarrierText(String text, int subId) { 763 if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId); 764 enforceSubscriptionPermission(); 765 766 ContentValues value = new ContentValues(1); 767 value.put(SubscriptionManager.CARRIER_NAME, text); 768 769 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 770 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 771 notifySubscriptionInfoChanged(); 772 773 return result; 774 } 775 776 /** 777 * Set SIM color tint by simInfo index 778 * @param tint the tint color of the SIM 779 * @param subId the unique SubInfoRecord index in database 780 * @return the number of records updated 781 */ 782 @Override 783 public int setIconTint(int tint, int subId) { 784 if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); 785 enforceSubscriptionPermission(); 786 787 validateSubId(subId); 788 ContentValues value = new ContentValues(1); 789 value.put(SubscriptionManager.COLOR, tint); 790 if (DBG) logd("[setIconTint]- tint:" + tint + " set"); 791 792 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 793 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 794 notifySubscriptionInfoChanged(); 795 796 return result; 797 } 798 799 /** 800 * Set display name by simInfo index 801 * @param displayName the display name of SIM card 802 * @param subId the unique SubInfoRecord index in database 803 * @return the number of records updated 804 */ 805 @Override 806 public int setDisplayName(String displayName, int subId) { 807 return setDisplayNameUsingSrc(displayName, subId, -1); 808 } 809 810 /** 811 * Set display name by simInfo index with name source 812 * @param displayName the display name of SIM card 813 * @param subId the unique SubInfoRecord index in database 814 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 815 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED 816 * @return the number of records updated 817 */ 818 @Override 819 public int setDisplayNameUsingSrc(String displayName, int subId, long nameSource) { 820 if (DBG) { 821 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId 822 + " nameSource:" + nameSource); 823 } 824 enforceSubscriptionPermission(); 825 826 validateSubId(subId); 827 String nameToSet; 828 if (displayName == null) { 829 nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES); 830 } else { 831 nameToSet = displayName; 832 } 833 ContentValues value = new ContentValues(1); 834 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 835 if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) { 836 if (DBG) logd("Set nameSource=" + nameSource); 837 value.put(SubscriptionManager.NAME_SOURCE, nameSource); 838 } 839 if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); 840 841 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 842 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 843 notifySubscriptionInfoChanged(); 844 845 return result; 846 } 847 848 /** 849 * Set phone number by subId 850 * @param number the phone number of the SIM 851 * @param subId the unique SubInfoRecord index in database 852 * @return the number of records updated 853 */ 854 @Override 855 public int setDisplayNumber(String number, int subId) { 856 if (DBG) logd("[setDisplayNumber]+ subId:" + subId); 857 enforceSubscriptionPermission(); 858 859 validateSubId(subId); 860 int result; 861 int phoneId = getPhoneId(subId); 862 863 if (number == null || phoneId < 0 || 864 phoneId >= mTelephonyManager.getPhoneCount()) { 865 if (DBG) logd("[setDispalyNumber]- fail"); 866 return -1; 867 } 868 ContentValues value = new ContentValues(1); 869 value.put(SubscriptionManager.NUMBER, number); 870 871 // This function had a call to update number on the SIM (Phone.setLine1Number()) but that 872 // was removed as there doesn't seem to be a reason for that. If it is added back, watch out 873 // for deadlocks. 874 875 result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 876 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID 877 + "=" + Long.toString(subId), null); 878 if (DBG) logd("[setDisplayNumber]- update result :" + result); 879 notifySubscriptionInfoChanged(); 880 881 return result; 882 } 883 884 /** 885 * Set data roaming by simInfo index 886 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 887 * @param subId the unique SubInfoRecord index in database 888 * @return the number of records updated 889 */ 890 @Override 891 public int setDataRoaming(int roaming, int subId) { 892 if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 893 enforceSubscriptionPermission(); 894 895 validateSubId(subId); 896 if (roaming < 0) { 897 if (DBG) logd("[setDataRoaming]- fail"); 898 return -1; 899 } 900 ContentValues value = new ContentValues(1); 901 value.put(SubscriptionManager.DATA_ROAMING, roaming); 902 if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set"); 903 904 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 905 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 906 notifySubscriptionInfoChanged(); 907 908 return result; 909 } 910 911 /** 912 * Set MCC/MNC by subscription ID 913 * @param mccMnc MCC/MNC associated with the subscription 914 * @param subId the unique SubInfoRecord index in database 915 * @return the number of records updated 916 */ 917 public int setMccMnc(String mccMnc, int subId) { 918 int mcc = 0; 919 int mnc = 0; 920 try { 921 mcc = Integer.parseInt(mccMnc.substring(0,3)); 922 mnc = Integer.parseInt(mccMnc.substring(3)); 923 } catch (NumberFormatException e) { 924 loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc); 925 } 926 if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId); 927 ContentValues value = new ContentValues(2); 928 value.put(SubscriptionManager.MCC, mcc); 929 value.put(SubscriptionManager.MNC, mnc); 930 931 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 932 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 933 notifySubscriptionInfoChanged(); 934 935 return result; 936 } 937 938 939 @Override 940 public int getSlotId(int subId) { 941 if (VDBG) printStackTrace("[getSlotId] subId=" + subId); 942 943 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 944 subId = getDefaultSubId(); 945 } 946 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 947 if (DBG) logd("[getSlotId]- subId invalid"); 948 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 949 } 950 951 int size = mSlotIdxToSubId.size(); 952 953 if (size == 0) 954 { 955 if (DBG) logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead"); 956 return SubscriptionManager.SIM_NOT_INSERTED; 957 } 958 959 for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) { 960 int sim = entry.getKey(); 961 int sub = entry.getValue(); 962 963 if (subId == sub) 964 { 965 if (VDBG) logv("[getSlotId]- return = " + sim); 966 return sim; 967 } 968 } 969 970 if (DBG) logd("[getSlotId]- return fail"); 971 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 972 } 973 974 /** 975 * Return the subId for specified slot Id. 976 * @deprecated 977 */ 978 @Override 979 @Deprecated 980 public int[] getSubId(int slotIdx) { 981 if (VDBG) printStackTrace("[getSubId]+ slotIdx=" + slotIdx); 982 983 // Map default slotIdx to the current default subId. 984 // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous 985 // as a slot maybe used for multiple different type of "connections" 986 // such as: voice, data and sms. But we're doing the best we can and using 987 // getDefaultSubId which makes a best guess. 988 if (slotIdx == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { 989 slotIdx = getSlotId(getDefaultSubId()); 990 if (DBG) logd("[getSubId] map default slotIdx=" + slotIdx); 991 } 992 993 // Check that we have a valid SlotIdx 994 if (!SubscriptionManager.isValidSlotId(slotIdx)) { 995 if (DBG) logd("[getSubId]- invalid slotIdx=" + slotIdx); 996 return null; 997 } 998 999 // Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate. 1000 int size = mSlotIdxToSubId.size(); 1001 if (size == 0) { 1002 if (DBG) { 1003 logd("[getSubId]- mSlotIdToSubIdMap.size == 0, return DummySubIds slotIdx=" 1004 + slotIdx); 1005 } 1006 return getDummySubIds(slotIdx); 1007 } 1008 1009 // Create an array of subIds that are in this slot? 1010 ArrayList<Integer> subIds = new ArrayList<Integer>(); 1011 for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) { 1012 int slot = entry.getKey(); 1013 int sub = entry.getValue(); 1014 if (slotIdx == slot) { 1015 subIds.add(sub); 1016 } 1017 } 1018 1019 // Convert ArrayList to array 1020 int numSubIds = subIds.size(); 1021 if (numSubIds > 0) { 1022 int[] subIdArr = new int[numSubIds]; 1023 for (int i = 0; i < numSubIds; i++) { 1024 subIdArr[i] = subIds.get(i); 1025 } 1026 if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr); 1027 return subIdArr; 1028 } else { 1029 if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIdx=" + slotIdx); 1030 return getDummySubIds(slotIdx); 1031 } 1032 } 1033 1034 @Override 1035 public int getPhoneId(int subId) { 1036 if (VDBG) printStackTrace("[getPhoneId] subId=" + subId); 1037 int phoneId; 1038 1039 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1040 subId = getDefaultSubId(); 1041 if (DBG) logdl("[getPhoneId] asked for default subId=" + subId); 1042 } 1043 1044 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1045 if (DBG) { 1046 logdl("[getPhoneId]- invalid subId return=" 1047 + SubscriptionManager.INVALID_PHONE_INDEX); 1048 } 1049 return SubscriptionManager.INVALID_PHONE_INDEX; 1050 } 1051 1052 int size = mSlotIdxToSubId.size(); 1053 if (size == 0) { 1054 phoneId = mDefaultPhoneId; 1055 if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId); 1056 return phoneId; 1057 } 1058 1059 // FIXME: Assumes phoneId == slotId 1060 for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) { 1061 int sim = entry.getKey(); 1062 int sub = entry.getValue(); 1063 1064 if (subId == sub) { 1065 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim); 1066 return sim; 1067 } 1068 } 1069 1070 phoneId = mDefaultPhoneId; 1071 if (DBG) { 1072 logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId); 1073 } 1074 return phoneId; 1075 1076 } 1077 1078 private int[] getDummySubIds(int slotIdx) { 1079 // FIXME: Remove notion of Dummy SUBSCRIPTION_ID. 1080 // I tested this returning null as no one appears to care, 1081 // but no connection came up on sprout with two sims. 1082 // We need to figure out why and hopefully remove DummySubsIds!!! 1083 int numSubs = getActiveSubInfoCountMax(); 1084 if (numSubs > 0) { 1085 int[] dummyValues = new int[numSubs]; 1086 for (int i = 0; i < numSubs; i++) { 1087 dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx; 1088 } 1089 if (DBG) { 1090 logd("getDummySubIds: slotIdx=" + slotIdx 1091 + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]); 1092 } 1093 return dummyValues; 1094 } else { 1095 return null; 1096 } 1097 } 1098 1099 /** 1100 * @return the number of records cleared 1101 */ 1102 @Override 1103 public int clearSubInfo() { 1104 enforceSubscriptionPermission(); 1105 if (DBG) logd("[clearSubInfo]+"); 1106 1107 int size = mSlotIdxToSubId.size(); 1108 1109 if (size == 0) { 1110 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size); 1111 return 0; 1112 } 1113 1114 mSlotIdxToSubId.clear(); 1115 if (DBG) logdl("[clearSubInfo]- clear size=" + size); 1116 return size; 1117 } 1118 1119 private void logvl(String msg) { 1120 logv(msg); 1121 mLocalLog.log(msg); 1122 } 1123 1124 private void logv(String msg) { 1125 Rlog.v(LOG_TAG, msg); 1126 } 1127 1128 private void logdl(String msg) { 1129 logd(msg); 1130 mLocalLog.log(msg); 1131 } 1132 1133 private static void slogd(String msg) { 1134 Rlog.d(LOG_TAG, msg); 1135 } 1136 1137 private void logd(String msg) { 1138 Rlog.d(LOG_TAG, msg); 1139 } 1140 1141 private void logel(String msg) { 1142 loge(msg); 1143 mLocalLog.log(msg); 1144 } 1145 1146 private void loge(String msg) { 1147 Rlog.e(LOG_TAG, msg); 1148 } 1149 1150 @Override 1151 public int getDefaultSubId() { 1152 int subId; 1153 boolean isVoiceCapable = mContext.getResources().getBoolean( 1154 com.android.internal.R.bool.config_voice_capable); 1155 if (isVoiceCapable) { 1156 subId = getDefaultVoiceSubId(); 1157 if (VDBG) logdl("[getDefaultSubId] isVoiceCapable subId=" + subId); 1158 } else { 1159 subId = getDefaultDataSubId(); 1160 if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId); 1161 } 1162 if ( ! isActiveSubId(subId)) { 1163 subId = mDefaultFallbackSubId; 1164 if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId); 1165 } 1166 if (VDBG) logv("[getDefaultSubId]- value = " + subId); 1167 return subId; 1168 } 1169 1170 @Override 1171 public void setDefaultSmsSubId(int subId) { 1172 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1173 throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID"); 1174 } 1175 if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId); 1176 Settings.Global.putInt(mContext.getContentResolver(), 1177 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId); 1178 broadcastDefaultSmsSubIdChanged(subId); 1179 } 1180 1181 private void broadcastDefaultSmsSubIdChanged(int subId) { 1182 // Broadcast an Intent for default sms sub change 1183 if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId); 1184 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED); 1185 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1186 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1187 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1188 } 1189 1190 @Override 1191 public int getDefaultSmsSubId() { 1192 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1193 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, 1194 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1195 if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId); 1196 return subId; 1197 } 1198 1199 @Override 1200 public void setDefaultVoiceSubId(int subId) { 1201 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1202 throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID"); 1203 } 1204 if (DBG) logdl("[setDefaultVoiceSubId] subId=" + subId); 1205 Settings.Global.putInt(mContext.getContentResolver(), 1206 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); 1207 broadcastDefaultVoiceSubIdChanged(subId); 1208 } 1209 1210 private void broadcastDefaultVoiceSubIdChanged(int subId) { 1211 // Broadcast an Intent for default voice sub change 1212 if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId); 1213 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 1214 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1215 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1216 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1217 } 1218 1219 @Override 1220 public int getDefaultVoiceSubId() { 1221 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1222 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, 1223 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1224 if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId); 1225 return subId; 1226 } 1227 1228 @Override 1229 public int getDefaultDataSubId() { 1230 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1231 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, 1232 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1233 if (VDBG) logd("[getDefaultDataSubId] subId= " + subId); 1234 return subId; 1235 } 1236 1237 @Override 1238 public void setDefaultDataSubId(int subId) { 1239 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1240 throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID"); 1241 } 1242 if (DBG) logdl("[setDefaultDataSubId] subId=" + subId); 1243 1244 int len = sProxyPhones.length; 1245 logdl("[setDefaultDataSubId] num phones=" + len); 1246 1247 RadioAccessFamily[] rafs = new RadioAccessFamily[len]; 1248 for (int phoneId = 0; phoneId < len; phoneId++) { 1249 PhoneProxy phone = sProxyPhones[phoneId]; 1250 int raf = phone.getRadioAccessFamily(); 1251 int id = phone.getSubId(); 1252 logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf); 1253 raf |= RadioAccessFamily.RAF_GSM; 1254 if (id == subId) { 1255 raf |= RadioAccessFamily.RAF_UMTS; 1256 } else { 1257 raf &= ~RadioAccessFamily.RAF_UMTS; 1258 } 1259 logdl("[setDefaultDataSubId] reqRAF=" + raf); 1260 1261 // Set the raf to the maximum of the requested and the user's preferred. 1262 int networkType = PhoneFactory.calculatePreferredNetworkType(mContext, id); 1263 logdl("[setDefaultDataSubId] networkType=" + networkType); 1264 raf &= RadioAccessFamily.getRafFromNetworkType(networkType); 1265 1266 logdl("[setDefaultDataSubId] newRAF=" + raf); 1267 rafs[phoneId] = new RadioAccessFamily(phoneId, raf); 1268 } 1269 ProxyController.getInstance().setRadioCapability(rafs); 1270 1271 // FIXME is this still needed? 1272 updateAllDataConnectionTrackers(); 1273 1274 Settings.Global.putInt(mContext.getContentResolver(), 1275 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); 1276 broadcastDefaultDataSubIdChanged(subId); 1277 } 1278 1279 private void updateAllDataConnectionTrackers() { 1280 // Tell Phone Proxies to update data connection tracker 1281 int len = sProxyPhones.length; 1282 if (DBG) logdl("[updateAllDataConnectionTrackers] sProxyPhones.length=" + len); 1283 for (int phoneId = 0; phoneId < len; phoneId++) { 1284 if (DBG) logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId); 1285 sProxyPhones[phoneId].updateDataConnectionTracker(); 1286 } 1287 } 1288 1289 private void broadcastDefaultDataSubIdChanged(int subId) { 1290 // Broadcast an Intent for default data sub change 1291 if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId); 1292 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 1293 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1294 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1295 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1296 } 1297 1298 /* Sets the default subscription. If only one sub is active that 1299 * sub is set as default subId. If two or more sub's are active 1300 * the first sub is set as default subscription 1301 */ 1302 private void setDefaultFallbackSubId(int subId) { 1303 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1304 throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID"); 1305 } 1306 if (DBG) logdl("[setDefaultFallbackSubId] subId=" + subId); 1307 if (SubscriptionManager.isValidSubscriptionId(subId)) { 1308 int phoneId = getPhoneId(subId); 1309 if (phoneId >= 0 && (phoneId < mTelephonyManager.getPhoneCount() 1310 || mTelephonyManager.getSimCount() == 1)) { 1311 if (DBG) logdl("[setDefaultFallbackSubId] set mDefaultFallbackSubId=" + subId); 1312 mDefaultFallbackSubId = subId; 1313 // Update MCC MNC device configuration information 1314 String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId); 1315 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); 1316 1317 // Broadcast an Intent for default sub change 1318 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); 1319 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1320 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId); 1321 if (DBG) { 1322 logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" + phoneId 1323 + " subId=" + subId); 1324 } 1325 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1326 } else { 1327 if (DBG) { 1328 logdl("[setDefaultFallbackSubId] not set invalid phoneId=" + phoneId 1329 + " subId=" + subId); 1330 } 1331 } 1332 } 1333 } 1334 1335 @Override 1336 public void clearDefaultsForInactiveSubIds() { 1337 final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(); 1338 if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records); 1339 if (shouldDefaultBeCleared(records, getDefaultDataSubId())) { 1340 if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id"); 1341 setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1342 } 1343 if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) { 1344 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id"); 1345 setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1346 } 1347 if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) { 1348 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id"); 1349 setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1350 } 1351 } 1352 1353 private boolean shouldDefaultBeCleared(List<SubscriptionInfo> records, int subId) { 1354 if (DBG) logdl("[shouldDefaultBeCleared: subId] " + subId); 1355 if (records == null) { 1356 if (DBG) logdl("[shouldDefaultBeCleared] return true no records subId=" + subId); 1357 return true; 1358 } 1359 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1360 // If the subId parameter is not valid its already cleared so return false. 1361 if (DBG) logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId); 1362 return false; 1363 } 1364 for (SubscriptionInfo record : records) { 1365 int id = record.getSubscriptionId(); 1366 if (DBG) logdl("[shouldDefaultBeCleared] Record.id: " + id); 1367 if (id == subId) { 1368 logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId); 1369 return false; 1370 } 1371 } 1372 if (DBG) logdl("[shouldDefaultBeCleared] return true not active subId=" + subId); 1373 return true; 1374 } 1375 1376 // FIXME: We need we should not be assuming phoneId == slotId as it will not be true 1377 // when there are multiple subscriptions per sim and probably for other reasons. 1378 public int getSubIdUsingPhoneId(int phoneId) { 1379 int[] subIds = getSubId(phoneId); 1380 if (subIds == null || subIds.length == 0) { 1381 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 1382 } 1383 return subIds[0]; 1384 } 1385 1386 public int[] getSubIdUsingSlotId(int slotId) { 1387 return getSubId(slotId); 1388 } 1389 1390 public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) { 1391 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId); 1392 enforceSubscriptionPermission(); 1393 1394 if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { 1395 slotId = getSlotId(getDefaultSubId()); 1396 } 1397 if (!SubscriptionManager.isValidSlotId(slotId)) { 1398 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId"); 1399 return null; 1400 } 1401 1402 if (needCheck && !isSubInfoReady()) { 1403 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready"); 1404 return null; 1405 } 1406 1407 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 1408 null, SubscriptionManager.SIM_SLOT_INDEX + "=?", 1409 new String[] {String.valueOf(slotId)}, null); 1410 ArrayList<SubscriptionInfo> subList = null; 1411 try { 1412 if (cursor != null) { 1413 while (cursor.moveToNext()) { 1414 SubscriptionInfo subInfo = getSubInfoRecord(cursor); 1415 if (subInfo != null) 1416 { 1417 if (subList == null) 1418 { 1419 subList = new ArrayList<SubscriptionInfo>(); 1420 } 1421 subList.add(subInfo); 1422 } 1423 } 1424 } 1425 } finally { 1426 if (cursor != null) { 1427 cursor.close(); 1428 } 1429 } 1430 if (DBG) logd("[getSubInfoUsingSlotId]- null info return"); 1431 1432 return subList; 1433 } 1434 1435 private void validateSubId(int subId) { 1436 if (DBG) logd("validateSubId subId: " + subId); 1437 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 1438 throw new RuntimeException("Invalid sub id passed as parameter"); 1439 } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1440 throw new RuntimeException("Default sub id passed as parameter"); 1441 } 1442 } 1443 1444 public void updatePhonesAvailability(PhoneProxy[] phones) { 1445 sProxyPhones = phones; 1446 } 1447 1448 /** 1449 * @return the list of subId's that are active, is never null but the length maybe 0. 1450 */ 1451 @Override 1452 public int[] getActiveSubIdList() { 1453 Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet(); 1454 if (DBG) logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet); 1455 1456 int[] subIdArr = new int[simInfoSet.size()]; 1457 int i = 0; 1458 for (Entry<Integer, Integer> entry: simInfoSet) { 1459 int sub = entry.getValue(); 1460 subIdArr[i] = sub; 1461 i++; 1462 } 1463 1464 if (DBG) logdl("[getActiveSubIdList] X subIdArr.length=" + subIdArr.length); 1465 return subIdArr; 1466 } 1467 1468 private boolean isActiveSubId(int subId) { 1469 boolean retVal = false; 1470 1471 if (SubscriptionManager.isValidSubscriptionId(subId)) { 1472 Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet(); 1473 if (VDBG) logdl("[isActiveSubId] simInfoSet=" + simInfoSet); 1474 1475 for (Entry<Integer, Integer> entry: simInfoSet) { 1476 if (subId == entry.getValue()) { 1477 retVal = true; 1478 break; 1479 } 1480 } 1481 } 1482 1483 if (VDBG) logdl("[isActiveSubId]- " + retVal); 1484 return retVal; 1485 } 1486 1487 /** 1488 * Get the SIM state for the subscriber 1489 * @return SIM state as the ordinal of {@See IccCardConstants.State} 1490 */ 1491 @Override 1492 public int getSimStateForSubscriber(int subId) { 1493 State simState; 1494 String err; 1495 int phoneIdx = getPhoneId(subId); 1496 if (phoneIdx < 0) { 1497 simState = IccCardConstants.State.UNKNOWN; 1498 err = "invalid PhoneIdx"; 1499 } else { 1500 Phone phone = PhoneFactory.getPhone(phoneIdx); 1501 if (phone == null) { 1502 simState = IccCardConstants.State.UNKNOWN; 1503 err = "phone == null"; 1504 } else { 1505 IccCard icc = phone.getIccCard(); 1506 if (icc == null) { 1507 simState = IccCardConstants.State.UNKNOWN; 1508 err = "icc == null"; 1509 } else { 1510 simState = icc.getState(); 1511 err = ""; 1512 } 1513 } 1514 } 1515 if (DBG) logd("getSimStateForSubscriber: " + err + " simState=" + simState 1516 + " ordinal=" + simState.ordinal()); 1517 return simState.ordinal(); 1518 } 1519 1520 private static void printStackTrace(String msg) { 1521 RuntimeException re = new RuntimeException(); 1522 slogd("StackTrace - " + msg); 1523 StackTraceElement[] st = re.getStackTrace(); 1524 boolean first = true; 1525 for (StackTraceElement ste : st) { 1526 if (first) { 1527 first = false; 1528 } else { 1529 slogd(ste.toString()); 1530 } 1531 } 1532 } 1533 1534 @Override 1535 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1536 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, 1537 "Requires DUMP"); 1538 final long token = Binder.clearCallingIdentity(); 1539 try { 1540 pw.println("SubscriptionController:"); 1541 pw.println(" defaultSubId=" + getDefaultSubId()); 1542 pw.println(" defaultDataSubId=" + getDefaultDataSubId()); 1543 pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId()); 1544 pw.println(" defaultSmsSubId=" + getDefaultSmsSubId()); 1545 1546 pw.println(" defaultDataPhoneId=" + SubscriptionManager 1547 .from(mContext).getDefaultDataPhoneId()); 1548 pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId()); 1549 pw.println(" defaultSmsPhoneId=" + SubscriptionManager 1550 .from(mContext).getDefaultSmsPhoneId()); 1551 pw.flush(); 1552 1553 for (Entry<Integer, Integer> entry : mSlotIdxToSubId.entrySet()) { 1554 pw.println(" mSlotIdToSubIdMap[" + entry.getKey() + "]: subId=" + entry.getValue()); 1555 } 1556 pw.flush(); 1557 pw.println("++++++++++++++++++++++++++++++++"); 1558 1559 List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(); 1560 if (sirl != null) { 1561 pw.println(" ActiveSubInfoList:"); 1562 for (SubscriptionInfo entry : sirl) { 1563 pw.println(" " + entry.toString()); 1564 } 1565 } else { 1566 pw.println(" ActiveSubInfoList: is null"); 1567 } 1568 pw.flush(); 1569 pw.println("++++++++++++++++++++++++++++++++"); 1570 1571 sirl = getAllSubInfoList(); 1572 if (sirl != null) { 1573 pw.println(" AllSubInfoList:"); 1574 for (SubscriptionInfo entry : sirl) { 1575 pw.println(" " + entry.toString()); 1576 } 1577 } else { 1578 pw.println(" AllSubInfoList: is null"); 1579 } 1580 pw.flush(); 1581 pw.println("++++++++++++++++++++++++++++++++"); 1582 1583 mLocalLog.dump(fd, pw, args); 1584 pw.flush(); 1585 pw.println("++++++++++++++++++++++++++++++++"); 1586 pw.flush(); 1587 } finally { 1588 Binder.restoreCallingIdentity(token); 1589 } 1590 } 1591} 1592