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