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