SubscriptionController.java revision bf96bc5d20e42a1c011413c3b1dee8ef5b524b43
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 for (SubscriptionInfo si : subList) { 446 if (si.getSimSlotIndex() == slotIdx) { 447 if (DBG) { 448 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx 449 + " subId=" + si); 450 } 451 return si; 452 } 453 } 454 if (DBG) { 455 logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx + " subId=null"); 456 } 457 return null; 458 } 459 460 /** 461 * @return List of all SubscriptionInfo records in database, 462 * include those that were inserted before, maybe empty but not null. 463 * @hide 464 */ 465 @Override 466 public List<SubscriptionInfo> getAllSubInfoList() { 467 if (DBG) logd("[getAllSubInfoList]+"); 468 enforceSubscriptionPermission(); 469 470 List<SubscriptionInfo> subList = null; 471 subList = getSubInfo(null, null); 472 if (subList != null) { 473 if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return"); 474 } else { 475 if (DBG) logd("[getAllSubInfoList]- no info return"); 476 } 477 478 return subList; 479 } 480 481 /** 482 * Get the SubInfoRecord(s) of the currently inserted SIM(s) 483 * @return Array list of currently inserted SubInfoRecord(s) 484 */ 485 @Override 486 public List<SubscriptionInfo> getActiveSubscriptionInfoList() { 487 enforceSubscriptionPermission(); 488 if (DBG) logdl("[getActiveSubInfoList]+"); 489 490 List<SubscriptionInfo> subList = null; 491 492 if (!isSubInfoReady()) { 493 if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready"); 494 return subList; 495 } 496 497 subList = getSubInfo(SubscriptionManager.SIM_SLOT_INDEX 498 + "!=" + SubscriptionManager.INVALID_SIM_SLOT_INDEX, null); 499 if (subList != null) { 500 // FIXME: Unnecessary when an insertion sort is used! 501 Collections.sort(subList, new Comparator<SubscriptionInfo>() { 502 @Override 503 public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) { 504 // Primary sort key on SimSlotIndex 505 int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex(); 506 if (flag == 0) { 507 // Secondary sort on SubscriptionId 508 return arg0.getSubscriptionId() - arg1.getSubscriptionId(); 509 } 510 return flag; 511 } 512 }); 513 514 if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return"); 515 } else { 516 if (DBG) logdl("[getActiveSubInfoList]- no info return"); 517 } 518 519 return subList; 520 } 521 522 /** 523 * Get the SUB count of active SUB(s) 524 * @return active SIM count 525 */ 526 @Override 527 public int getActiveSubInfoCount() { 528 if (DBG) logd("[getActiveSubInfoCount]+"); 529 List<SubscriptionInfo> records = getActiveSubscriptionInfoList(); 530 if (records == null) { 531 if (DBG) logd("[getActiveSubInfoCount] records null"); 532 return 0; 533 } 534 if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size()); 535 return records.size(); 536 } 537 538 /** 539 * Get the SUB count of all SUB(s) in SubscriptoinInfo database 540 * @return all SIM count in database, include what was inserted before 541 */ 542 @Override 543 public int getAllSubInfoCount() { 544 if (DBG) logd("[getAllSubInfoCount]+"); 545 enforceSubscriptionPermission(); 546 547 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 548 null, null, null, null); 549 try { 550 if (cursor != null) { 551 int count = cursor.getCount(); 552 if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB"); 553 return count; 554 } 555 } finally { 556 if (cursor != null) { 557 cursor.close(); 558 } 559 } 560 if (DBG) logd("[getAllSubInfoCount]- no SUB in DB"); 561 562 return 0; 563 } 564 565 /** 566 * @return the maximum number of subscriptions this device will support at any one time. 567 */ 568 @Override 569 public int getActiveSubInfoCountMax() { 570 // FIXME: This valid now but change to use TelephonyDevController in the future 571 return mTelephonyManager.getSimCount(); 572 } 573 574 /** 575 * Add a new SubInfoRecord to subinfo database if needed 576 * @param iccId the IccId of the SIM card 577 * @param slotId the slot which the SIM is inserted 578 * @return 0 if success, < 0 on error. 579 */ 580 @Override 581 public int addSubInfoRecord(String iccId, int slotId) { 582 if (DBG) logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId); 583 enforceSubscriptionPermission(); 584 585 if (iccId == null) { 586 if (DBG) logdl("[addSubInfoRecord]- null iccId"); 587 return -1; 588 } 589 590 int[] subIds = getSubId(slotId); 591 if (subIds == null || subIds.length == 0) { 592 if (DBG) { 593 logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds=" 594 + subIds); 595 } 596 return -1; 597 } 598 599 String nameToSet; 600 String CarrierName = TelephonyManager.getDefault().getSimOperator(subIds[0]); 601 if (DBG) logdl("[addSubInfoRecord] CarrierName = " + CarrierName); 602 String simCarrierName = 603 TelephonyManager.getDefault().getSimOperatorName(subIds[0]); 604 605 if (!TextUtils.isEmpty(simCarrierName)) { 606 nameToSet = simCarrierName; 607 } else { 608 nameToSet = "CARD " + Integer.toString(slotId + 1); 609 } 610 if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet); 611 if (DBG) logdl("[addSubInfoRecord] carrier name = " + simCarrierName); 612 613 ContentResolver resolver = mContext.getContentResolver(); 614 Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI, 615 new String[] {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, 616 SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE}, 617 SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null); 618 619 int color = getUnusedColor(); 620 621 try { 622 if (cursor == null || !cursor.moveToFirst()) { 623 ContentValues value = new ContentValues(); 624 value.put(SubscriptionManager.ICC_ID, iccId); 625 // default SIM color differs between slots 626 value.put(SubscriptionManager.COLOR, color); 627 value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId); 628 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 629 value.put(SubscriptionManager.CARRIER_NAME, 630 !TextUtils.isEmpty(simCarrierName) ? simCarrierName : 631 mContext.getString(com.android.internal.R.string.unknownName)); 632 Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value); 633 if (DBG) logdl("[addSubInfoRecord] New record created: " + uri); 634 } else { 635 int subId = cursor.getInt(0); 636 int oldSimInfoId = cursor.getInt(1); 637 int nameSource = cursor.getInt(2); 638 ContentValues value = new ContentValues(); 639 640 if (slotId != oldSimInfoId) { 641 value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId); 642 } 643 644 if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) { 645 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 646 } 647 648 if (!TextUtils.isEmpty(simCarrierName)) { 649 value.put(SubscriptionManager.CARRIER_NAME, simCarrierName); 650 } 651 652 if (value.size() > 0) { 653 resolver.update(SubscriptionManager.CONTENT_URI, value, 654 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + 655 "=" + Long.toString(subId), null); 656 } 657 658 if (DBG) logdl("[addSubInfoRecord] Record already exists"); 659 } 660 } finally { 661 if (cursor != null) { 662 cursor.close(); 663 } 664 } 665 666 cursor = resolver.query(SubscriptionManager.CONTENT_URI, null, 667 SubscriptionManager.SIM_SLOT_INDEX + "=?", 668 new String[] {String.valueOf(slotId)}, null); 669 try { 670 if (cursor != null && cursor.moveToFirst()) { 671 do { 672 int subId = cursor.getInt(cursor.getColumnIndexOrThrow( 673 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID)); 674 // If mSlotIdToSubIdMap already has a valid subId for a slotId/phoneId, 675 // do not add another subId for same slotId/phoneId. 676 Integer currentSubId = mSlotIdxToSubId.get(slotId); 677 if (currentSubId == null || !SubscriptionManager.isValidSubId(currentSubId)) { 678 // TODO While two subs active, if user deactivats first 679 // one, need to update the default subId with second one. 680 681 // FIXME: Currently we assume phoneId and slotId may not be true 682 // when we cross map modem or when multiple subs per slot. 683 // But is true at the moment. 684 mSlotIdxToSubId.put(slotId, subId); 685 int subIdCountMax = getActiveSubInfoCountMax(); 686 int defaultSubId = getDefaultSubId(); 687 if (DBG) { 688 logdl("[addSubInfoRecord]" 689 + " mSlotIdxToSubId.size=" + mSlotIdxToSubId.size() 690 + " slotId=" + slotId + " subId=" + subId 691 + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax); 692 } 693 694 // Set the default sub if not set or if single sim device 695 if (!SubscriptionManager.isValidSubId(defaultSubId) || subIdCountMax == 1) { 696 setDefaultSubId(subId); 697 } 698 // If single sim device, set this subscription as the default for everything 699 if (subIdCountMax == 1) { 700 if (DBG) { 701 logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId); 702 } 703 setDefaultDataSubId(subId); 704 setDefaultSmsSubId(subId); 705 setDefaultVoiceSubId(subId); 706 } 707 } else { 708 if (DBG) { 709 logdl("[addSubInfoRecord] currentSubId != null" 710 + " && currentSubId is valid, IGNORE"); 711 } 712 } 713 if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")"); 714 } while (cursor.moveToNext()); 715 } 716 } finally { 717 if (cursor != null) { 718 cursor.close(); 719 } 720 } 721 722 // Once the records are loaded, notify DcTracker 723 updateAllDataConnectionTrackers(); 724 725 if (DBG) logdl("[addSubInfoRecord]- info size=" + mSlotIdxToSubId.size()); 726 return 0; 727 } 728 729 /** 730 * Set SIM color tint by simInfo index 731 * @param tint the tint color of the SIM 732 * @param subId the unique SubInfoRecord index in database 733 * @return the number of records updated 734 */ 735 @Override 736 public int setIconTint(int tint, int subId) { 737 if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); 738 enforceSubscriptionPermission(); 739 740 validateSubId(subId); 741 ContentValues value = new ContentValues(1); 742 value.put(SubscriptionManager.COLOR, tint); 743 if (DBG) logd("[setIconTint]- tint:" + tint + " set"); 744 745 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 746 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 747 notifySubscriptionInfoChanged(); 748 749 return result; 750 } 751 752 /** 753 * Set display name by simInfo index 754 * @param displayName the display name of SIM card 755 * @param subId the unique SubInfoRecord index in database 756 * @return the number of records updated 757 */ 758 @Override 759 public int setDisplayName(String displayName, int subId) { 760 return setDisplayNameUsingSrc(displayName, subId, -1); 761 } 762 763 /** 764 * Set display name by simInfo index with name source 765 * @param displayName the display name of SIM card 766 * @param subId the unique SubInfoRecord index in database 767 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 768 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED 769 * @return the number of records updated 770 */ 771 @Override 772 public int setDisplayNameUsingSrc(String displayName, int subId, long nameSource) { 773 if (DBG) { 774 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId 775 + " nameSource:" + nameSource); 776 } 777 enforceSubscriptionPermission(); 778 779 validateSubId(subId); 780 String nameToSet; 781 if (displayName == null) { 782 nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES); 783 } else { 784 nameToSet = displayName; 785 } 786 ContentValues value = new ContentValues(1); 787 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 788 if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) { 789 if (DBG) logd("Set nameSource=" + nameSource); 790 value.put(SubscriptionManager.NAME_SOURCE, nameSource); 791 } 792 if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); 793 794 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 795 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 796 notifySubscriptionInfoChanged(); 797 798 return result; 799 } 800 801 /** 802 * Set phone number by subId 803 * @param number the phone number of the SIM 804 * @param subId the unique SubInfoRecord index in database 805 * @return the number of records updated 806 */ 807 @Override 808 public int setDisplayNumber(String number, int subId) { 809 if (DBG) logd("[setDisplayNumber]+ number:" + number + " subId:" + subId); 810 enforceSubscriptionPermission(); 811 812 validateSubId(subId); 813 int result = 0; 814 int phoneId = getPhoneId(subId); 815 816 if (number == null || phoneId < 0 || 817 phoneId >= TelephonyManager.getDefault().getPhoneCount()) { 818 if (DBG) logd("[setDispalyNumber]- fail"); 819 return -1; 820 } 821 ContentValues value = new ContentValues(1); 822 value.put(SubscriptionManager.NUMBER, number); 823 if (DBG) logd("[setDisplayNumber]- number:" + number + " set"); 824 825 Phone phone = sProxyPhones[phoneId]; 826 String alphaTag = TelephonyManager.getDefault().getLine1AlphaTagForSubscriber(subId); 827 828 synchronized(mLock) { 829 mSuccess = false; 830 Message response = mHandler.obtainMessage(EVENT_WRITE_MSISDN_DONE); 831 832 phone.setLine1Number(alphaTag, number, response); 833 834 try { 835 mLock.wait(); 836 } catch (InterruptedException e) { 837 loge("interrupted while trying to write MSISDN"); 838 } 839 } 840 841 if (mSuccess) { 842 result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 843 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID 844 + "=" + Long.toString(subId), null); 845 if (DBG) logd("[setDisplayNumber]- update result :" + result); 846 notifySubscriptionInfoChanged(); 847 } 848 849 return result; 850 } 851 852 /** 853 * Set data roaming by simInfo index 854 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 855 * @param subId the unique SubInfoRecord index in database 856 * @return the number of records updated 857 */ 858 @Override 859 public int setDataRoaming(int roaming, int subId) { 860 if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 861 enforceSubscriptionPermission(); 862 863 validateSubId(subId); 864 if (roaming < 0) { 865 if (DBG) logd("[setDataRoaming]- fail"); 866 return -1; 867 } 868 ContentValues value = new ContentValues(1); 869 value.put(SubscriptionManager.DATA_ROAMING, roaming); 870 if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set"); 871 872 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 873 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 874 notifySubscriptionInfoChanged(); 875 876 return result; 877 } 878 879 /** 880 * Set MCC/MNC by subscription ID 881 * @param mccMnc MCC/MNC associated with the subscription 882 * @param subId the unique SubInfoRecord index in database 883 * @return the number of records updated 884 */ 885 public int setMccMnc(String mccMnc, int subId) { 886 int mcc = 0; 887 int mnc = 0; 888 try { 889 mcc = Integer.parseInt(mccMnc.substring(0,3)); 890 mnc = Integer.parseInt(mccMnc.substring(3)); 891 } catch (NumberFormatException e) { 892 loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc); 893 } 894 if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId); 895 ContentValues value = new ContentValues(2); 896 value.put(SubscriptionManager.MCC, mcc); 897 value.put(SubscriptionManager.MNC, mnc); 898 899 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 900 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null); 901 notifySubscriptionInfoChanged(); 902 903 return result; 904 } 905 906 907 @Override 908 public int getSlotId(int subId) { 909 if (VDBG) printStackTrace("[getSlotId] subId=" + subId); 910 911 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 912 subId = getDefaultSubId(); 913 } 914 if (!SubscriptionManager.isValidSubId(subId)) { 915 if (DBG) logd("[getSlotId]- subId invalid"); 916 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 917 } 918 919 int size = mSlotIdxToSubId.size(); 920 921 if (size == 0) 922 { 923 if (DBG) logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead"); 924 return SubscriptionManager.SIM_NOT_INSERTED; 925 } 926 927 for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) { 928 int sim = entry.getKey(); 929 int sub = entry.getValue(); 930 931 if (subId == sub) 932 { 933 if (VDBG) logv("[getSlotId]- return = " + sim); 934 return sim; 935 } 936 } 937 938 if (DBG) logd("[getSlotId]- return fail"); 939 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 940 } 941 942 /** 943 * Return the subId for specified slot Id. 944 * @deprecated 945 */ 946 @Override 947 @Deprecated 948 public int[] getSubId(int slotIdx) { 949 if (VDBG) printStackTrace("[getSubId]+ slotIdx=" + slotIdx); 950 951 // Map default slotIdx to the current default subId. 952 // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous 953 // as a slot maybe used for multiple different type of "connections" 954 // such as: voice, data and sms. But we're doing the best we can and using 955 // getDefaultSubId which makes a best guess. 956 if (slotIdx == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { 957 slotIdx = getSlotId(getDefaultSubId()); 958 if (DBG) logd("[getSubId] map default slotIdx=" + slotIdx); 959 } 960 961 // Check that we have a valid SlotIdx 962 if (!SubscriptionManager.isValidSlotId(slotIdx)) { 963 if (DBG) logd("[getSubId]- invalid slotIdx=" + slotIdx); 964 return null; 965 } 966 967 // Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate. 968 int size = mSlotIdxToSubId.size(); 969 if (size == 0) { 970 if (DBG) { 971 logd("[getSubId]- mSlotIdToSubIdMap.size == 0, return DummySubIds slotIdx=" 972 + slotIdx); 973 } 974 return getDummySubIds(slotIdx); 975 } 976 977 // Create an array of subIds that are in this slot? 978 ArrayList<Integer> subIds = new ArrayList<Integer>(); 979 for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) { 980 int slot = entry.getKey(); 981 int sub = entry.getValue(); 982 if (slotIdx == slot) { 983 subIds.add(sub); 984 } 985 } 986 987 // Convert ArrayList to array 988 int numSubIds = subIds.size(); 989 if (numSubIds > 0) { 990 int[] subIdArr = new int[numSubIds]; 991 for (int i = 0; i < numSubIds; i++) { 992 subIdArr[i] = subIds.get(i); 993 } 994 if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr); 995 return subIdArr; 996 } else { 997 if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIdx=" + slotIdx); 998 return getDummySubIds(slotIdx); 999 } 1000 } 1001 1002 @Override 1003 public int getPhoneId(int subId) { 1004 if (VDBG) printStackTrace("[getPhoneId] subId=" + subId); 1005 int phoneId; 1006 1007 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1008 subId = getDefaultSubId(); 1009 if (DBG) logdl("[getPhoneId] asked for default subId=" + subId); 1010 } 1011 1012 if (!SubscriptionManager.isValidSubId(subId)) { 1013 if (DBG) { 1014 logdl("[getPhoneId]- invalid subId return=" 1015 + SubscriptionManager.INVALID_PHONE_INDEX); 1016 } 1017 return SubscriptionManager.INVALID_PHONE_INDEX; 1018 } 1019 1020 int size = mSlotIdxToSubId.size(); 1021 if (size == 0) { 1022 phoneId = mDefaultPhoneId; 1023 if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId); 1024 return phoneId; 1025 } 1026 1027 // FIXME: Assumes phoneId == slotId 1028 for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) { 1029 int sim = entry.getKey(); 1030 int sub = entry.getValue(); 1031 1032 if (subId == sub) { 1033 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim); 1034 return sim; 1035 } 1036 } 1037 1038 phoneId = mDefaultPhoneId; 1039 if (DBG) { 1040 logdl("[getPhoneId]- subId=" + subId + " not found return default phoneId=" + phoneId); 1041 } 1042 return phoneId; 1043 1044 } 1045 1046 private int[] getDummySubIds(int slotIdx) { 1047 // FIXME: Remove notion of Dummy SUBSCRIPTION_ID. 1048 // I tested this returning null as no one appears to care, 1049 // but no connection came up on sprout with two sims. 1050 // We need to figure out why and hopefully remove DummySubsIds!!! 1051 int numSubs = getActiveSubInfoCountMax(); 1052 if (numSubs > 0) { 1053 int[] dummyValues = new int[numSubs]; 1054 for (int i = 0; i < numSubs; i++) { 1055 dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx; 1056 } 1057 if (DBG) { 1058 logd("getDummySubIds: slotIdx=" + slotIdx 1059 + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]); 1060 } 1061 return dummyValues; 1062 } else { 1063 return null; 1064 } 1065 } 1066 1067 /** 1068 * @return the number of records cleared 1069 */ 1070 @Override 1071 public int clearSubInfo() { 1072 enforceSubscriptionPermission(); 1073 if (DBG) logd("[clearSubInfo]+"); 1074 1075 int size = mSlotIdxToSubId.size(); 1076 1077 if (size == 0) { 1078 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size); 1079 return 0; 1080 } 1081 1082 mSlotIdxToSubId.clear(); 1083 if (DBG) logdl("[clearSubInfo]- clear size=" + size); 1084 return size; 1085 } 1086 1087 private void logvl(String msg) { 1088 logv(msg); 1089 mLocalLog.log(msg); 1090 } 1091 1092 private void logv(String msg) { 1093 Rlog.v(LOG_TAG, msg); 1094 } 1095 1096 private void logdl(String msg) { 1097 logd(msg); 1098 mLocalLog.log(msg); 1099 } 1100 1101 private static void slogd(String msg) { 1102 Rlog.d(LOG_TAG, msg); 1103 } 1104 1105 private void logd(String msg) { 1106 Rlog.d(LOG_TAG, msg); 1107 } 1108 1109 private void logel(String msg) { 1110 loge(msg); 1111 mLocalLog.log(msg); 1112 } 1113 1114 private void loge(String msg) { 1115 Rlog.e(LOG_TAG, msg); 1116 } 1117 1118 @Override 1119 public int getDefaultSubId() { 1120 //FIXME: Make this smarter, need to handle data only and voice devices 1121 int subId = mDefaultVoiceSubId; 1122 if (VDBG) logv("[getDefaultSubId] value = " + subId); 1123 return subId; 1124 } 1125 1126 @Override 1127 public void setDefaultSmsSubId(int subId) { 1128 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1129 throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID"); 1130 } 1131 if (DBG) logdl("[setDefaultSmsSubId] subId=" + subId); 1132 Settings.Global.putInt(mContext.getContentResolver(), 1133 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId); 1134 broadcastDefaultSmsSubIdChanged(subId); 1135 } 1136 1137 private void broadcastDefaultSmsSubIdChanged(int subId) { 1138 // Broadcast an Intent for default sms sub change 1139 if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId); 1140 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED); 1141 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1142 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1143 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1144 } 1145 1146 @Override 1147 public int getDefaultSmsSubId() { 1148 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1149 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, 1150 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1151 if (VDBG) logd("[getDefaultSmsSubId] subId=" + subId); 1152 return subId; 1153 } 1154 1155 @Override 1156 public void setDefaultVoiceSubId(int subId) { 1157 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1158 throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID"); 1159 } 1160 if (DBG) logdl("[setDefaultVoiceSubId] subId=" + subId); 1161 Settings.Global.putInt(mContext.getContentResolver(), 1162 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); 1163 broadcastDefaultVoiceSubIdChanged(subId); 1164 } 1165 1166 private void broadcastDefaultVoiceSubIdChanged(int subId) { 1167 // Broadcast an Intent for default voice sub change 1168 if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId); 1169 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 1170 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1171 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1172 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1173 } 1174 1175 @Override 1176 public int getDefaultVoiceSubId() { 1177 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1178 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, 1179 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1180 if (VDBG) logd("[getDefaultVoiceSubId] subId=" + subId); 1181 return subId; 1182 } 1183 1184 @Override 1185 public int getDefaultDataSubId() { 1186 int subId = Settings.Global.getInt(mContext.getContentResolver(), 1187 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, 1188 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1189 if (VDBG) logd("[getDefaultDataSubId] subId= " + subId); 1190 return subId; 1191 } 1192 1193 @Override 1194 public void setDefaultDataSubId(int subId) { 1195 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1196 throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID"); 1197 } 1198 if (DBG) logdl("[setDefaultDataSubId] subId=" + subId); 1199 1200 int len = sProxyPhones.length; 1201 logdl("[setDefaultDataSubId] num phones=" + len); 1202 1203 RadioAccessFamily[] rafs = new RadioAccessFamily[len]; 1204 for (int phoneId = 0; phoneId < len; phoneId++) { 1205 PhoneProxy phone = sProxyPhones[phoneId]; 1206 int raf = phone.getRadioAccessFamily(); 1207 int id = phone.getSubId(); 1208 logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf); 1209 // TODO(stuartscott): Need to set 3G or 2G depending on user's preference and modem 1210 // supported capabilities 1211 if (id == subId) { 1212 raf |= RadioAccessFamily.RAF_UMTS; 1213 } else { 1214 raf &= ~RadioAccessFamily.RAF_UMTS; 1215 } 1216 logdl("[setDefaultDataSubId] newRAF=" + raf); 1217 rafs[phoneId] = new RadioAccessFamily(phoneId, raf); 1218 } 1219 ProxyController.getInstance().setRadioCapability(rafs); 1220 1221 // FIXME is this still needed? 1222 updateAllDataConnectionTrackers(); 1223 1224 Settings.Global.putInt(mContext.getContentResolver(), 1225 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); 1226 broadcastDefaultDataSubIdChanged(subId); 1227 } 1228 1229 private void updateAllDataConnectionTrackers() { 1230 // Tell Phone Proxies to update data connection tracker 1231 int len = sProxyPhones.length; 1232 if (DBG) logdl("[updateAllDataConnectionTrackers] sProxyPhones.length=" + len); 1233 for (int phoneId = 0; phoneId < len; phoneId++) { 1234 if (DBG) logdl("[updateAllDataConnectionTrackers] phoneId=" + phoneId); 1235 sProxyPhones[phoneId].updateDataConnectionTracker(); 1236 } 1237 } 1238 1239 private void broadcastDefaultDataSubIdChanged(int subId) { 1240 // Broadcast an Intent for default data sub change 1241 if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId); 1242 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 1243 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1244 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1245 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1246 } 1247 1248 /* Sets the default subscription. If only one sub is active that 1249 * sub is set as default subId. If two or more sub's are active 1250 * the first sub is set as default subscription 1251 */ 1252 // FIXME 1253 public void setDefaultSubId(int subId) { 1254 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1255 throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID"); 1256 } 1257 if (DBG) logdl("[setDefaultSubId] subId=" + subId); 1258 if (SubscriptionManager.isValidSubId(subId)) { 1259 int phoneId = getPhoneId(subId); 1260 if (phoneId >= 0 && (phoneId < TelephonyManager.getDefault().getPhoneCount() 1261 || TelephonyManager.getDefault().getSimCount() == 1)) { 1262 if (DBG) logdl("[setDefaultSubId] set mDefaultVoiceSubId=" + subId); 1263 mDefaultVoiceSubId = subId; 1264 // Update MCC MNC device configuration information 1265 String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId); 1266 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); 1267 1268 // Broadcast an Intent for default sub change 1269 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); 1270 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1271 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId); 1272 if (VDBG) { 1273 logdl("[setDefaultSubId] broadcast default subId changed phoneId=" + phoneId 1274 + " subId=" + subId); 1275 } 1276 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1277 } else { 1278 if (VDBG) { 1279 logdl("[setDefaultSubId] not set invalid phoneId=" + phoneId 1280 + " subId=" + subId); 1281 } 1282 } 1283 } 1284 } 1285 1286 @Override 1287 public void clearDefaultsForInactiveSubIds() { 1288 final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(); 1289 if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records); 1290 if (shouldDefaultBeCleared(records, getDefaultDataSubId())) { 1291 if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id"); 1292 setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1293 } 1294 if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) { 1295 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id"); 1296 setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1297 } 1298 if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) { 1299 if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id"); 1300 setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 1301 } 1302 } 1303 1304 private boolean shouldDefaultBeCleared(List<SubscriptionInfo> records, int subId) { 1305 if (DBG) logdl("[shouldDefaultBeCleared: subId] " + subId); 1306 if (records == null) { 1307 if (DBG) logdl("[shouldDefaultBeCleared] return true no records subId=" + subId); 1308 return true; 1309 } 1310 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 1311 // If the subId parameter is INVALID_SUBSCRIPTION_ID its 1312 // already cleared so return false. 1313 if (DBG) logdl("[shouldDefaultBeCleared] return false only one subId, subId=" + subId); 1314 return false; 1315 } 1316 for (SubscriptionInfo record : records) { 1317 int id = record.getSubscriptionId(); 1318 if (DBG) logdl("[shouldDefaultBeCleared] Record.id: " + id); 1319 if (id == subId) { 1320 logdl("[shouldDefaultBeCleared] return false subId is active, subId=" + subId); 1321 return false; 1322 } 1323 } 1324 if (DBG) logdl("[shouldDefaultBeCleared] return true not active subId=" + subId); 1325 return true; 1326 } 1327 1328 // FIXME: We need we should not be assuming phoneId == slotId as it will not be true 1329 // when there are multiple subscriptions per sim and probably for other reasons. 1330 public int getSubIdUsingPhoneId(int phoneId) { 1331 int[] subIds = getSubId(phoneId); 1332 if (subIds == null || subIds.length == 0) { 1333 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 1334 } 1335 return subIds[0]; 1336 } 1337 1338 public int[] getSubIdUsingSlotId(int slotId) { 1339 return getSubId(slotId); 1340 } 1341 1342 public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) { 1343 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId); 1344 enforceSubscriptionPermission(); 1345 1346 if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) { 1347 slotId = getSlotId(getDefaultSubId()); 1348 } 1349 if (!SubscriptionManager.isValidSlotId(slotId)) { 1350 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId"); 1351 return null; 1352 } 1353 1354 if (needCheck && !isSubInfoReady()) { 1355 if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready"); 1356 return null; 1357 } 1358 1359 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 1360 null, SubscriptionManager.SIM_SLOT_INDEX + "=?", 1361 new String[] {String.valueOf(slotId)}, null); 1362 ArrayList<SubscriptionInfo> subList = null; 1363 try { 1364 if (cursor != null) { 1365 while (cursor.moveToNext()) { 1366 SubscriptionInfo subInfo = getSubInfoRecord(cursor); 1367 if (subInfo != null) 1368 { 1369 if (subList == null) 1370 { 1371 subList = new ArrayList<SubscriptionInfo>(); 1372 } 1373 subList.add(subInfo); 1374 } 1375 } 1376 } 1377 } finally { 1378 if (cursor != null) { 1379 cursor.close(); 1380 } 1381 } 1382 if (DBG) logd("[getSubInfoUsingSlotId]- null info return"); 1383 1384 return subList; 1385 } 1386 1387 private void validateSubId(int subId) { 1388 if (DBG) logd("validateSubId subId: " + subId); 1389 if (!SubscriptionManager.isValidSubId(subId)) { 1390 throw new RuntimeException("Invalid sub id passed as parameter"); 1391 } else if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 1392 throw new RuntimeException("Default sub id passed as parameter"); 1393 } 1394 } 1395 1396 public void updatePhonesAvailability(PhoneProxy[] phones) { 1397 sProxyPhones = phones; 1398 } 1399 1400 /** 1401 * @return the list of subId's that are active, is never null but the length maybe 0. 1402 */ 1403 @Override 1404 public int[] getActiveSubIdList() { 1405 Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet(); 1406 if (DBG) logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet); 1407 1408 int[] subIdArr = new int[simInfoSet.size()]; 1409 int i = 0; 1410 for (Entry<Integer, Integer> entry: simInfoSet) { 1411 int sub = entry.getValue(); 1412 subIdArr[i] = sub; 1413 i++; 1414 } 1415 1416 if (DBG) logdl("[getActiveSubIdList] X subIdArr.length=" + subIdArr.length); 1417 return subIdArr; 1418 } 1419 1420 /** 1421 * Get the SIM state for the subscriber 1422 * @return SIM state as the ordinal of {@See IccCardConstants.State} 1423 */ 1424 @Override 1425 public int getSimStateForSubscriber(int subId) { 1426 State simState; 1427 String err; 1428 int phoneIdx = getPhoneId(subId); 1429 if (phoneIdx < 0) { 1430 simState = IccCardConstants.State.UNKNOWN; 1431 err = "invalid PhoneIdx"; 1432 } else { 1433 Phone phone = PhoneFactory.getPhone(phoneIdx); 1434 if (phone == null) { 1435 simState = IccCardConstants.State.UNKNOWN; 1436 err = "phone == null"; 1437 } else { 1438 IccCard icc = phone.getIccCard(); 1439 if (icc == null) { 1440 simState = IccCardConstants.State.UNKNOWN; 1441 err = "icc == null"; 1442 } else { 1443 simState = icc.getState(); 1444 err = ""; 1445 } 1446 } 1447 } 1448 if (DBG) logd("getSimStateForSubscriber: " + err + " simState=" + simState 1449 + " ordinal=" + simState.ordinal()); 1450 return simState.ordinal(); 1451 } 1452 1453 private static void printStackTrace(String msg) { 1454 RuntimeException re = new RuntimeException(); 1455 slogd("StackTrace - " + msg); 1456 StackTraceElement[] st = re.getStackTrace(); 1457 boolean first = true; 1458 for (StackTraceElement ste : st) { 1459 if (first) { 1460 first = false; 1461 } else { 1462 slogd(ste.toString()); 1463 } 1464 } 1465 } 1466 1467 @Override 1468 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1469 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, 1470 "Requires DUMP"); 1471 pw.println("SubscriptionController:"); 1472 pw.println(" defaultSubId=" + getDefaultSubId()); 1473 pw.println(" defaultDataSubId=" + getDefaultDataSubId()); 1474 pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId()); 1475 pw.println(" defaultSmsSubId=" + getDefaultSmsSubId()); 1476 1477 pw.println(" defaultDataPhoneId=" + SubscriptionManager 1478 .from(mContext).getDefaultDataPhoneId()); 1479 pw.println(" defaultVoicePhoneId=" + SubscriptionManager.getDefaultVoicePhoneId()); 1480 pw.println(" defaultSmsPhoneId=" + SubscriptionManager 1481 .from(mContext).getDefaultSmsPhoneId()); 1482 pw.flush(); 1483 1484 for (Entry<Integer, Integer> entry : mSlotIdxToSubId.entrySet()) { 1485 pw.println(" mSlotIdToSubIdMap[" + entry.getKey() + "]: subId=" + entry.getValue()); 1486 } 1487 pw.flush(); 1488 pw.println("++++++++++++++++++++++++++++++++"); 1489 1490 List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(); 1491 if (sirl != null) { 1492 pw.println(" ActiveSubInfoList:"); 1493 for (SubscriptionInfo entry : sirl) { 1494 pw.println(" " + entry.toString()); 1495 } 1496 } else { 1497 pw.println(" ActiveSubInfoList: is null"); 1498 } 1499 pw.flush(); 1500 pw.println("++++++++++++++++++++++++++++++++"); 1501 1502 sirl = getAllSubInfoList(); 1503 if (sirl != null) { 1504 pw.println(" AllSubInfoList:"); 1505 for (SubscriptionInfo entry : sirl) { 1506 pw.println(" " + entry.toString()); 1507 } 1508 } else { 1509 pw.println(" AllSubInfoList: is null"); 1510 } 1511 pw.flush(); 1512 pw.println("++++++++++++++++++++++++++++++++"); 1513 1514 mLocalLog.dump(fd, pw, args); 1515 pw.flush(); 1516 pw.println("++++++++++++++++++++++++++++++++"); 1517 pw.flush(); 1518 } 1519} 1520