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