SubscriptionController.java revision b66469cd64077d31fc67087708638a985d9cb449
1/* 2* Copyright (C) 2011-2014 MediaTek Inc. 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.Context; 20import android.os.AsyncResult; 21import android.os.Handler; 22import android.os.Message; 23import android.os.RemoteException; 24import android.os.ServiceManager; 25import android.os.UserHandle; 26import android.telephony.Rlog; 27import android.util.Log; 28import android.net.Uri; 29import android.database.Cursor; 30import android.content.Intent; 31import android.provider.BaseColumns; 32import android.provider.Settings; 33import android.content.ContentResolver; 34import android.content.ContentValues; 35 36import com.android.internal.telephony.ISub; 37import com.android.internal.telephony.uicc.SpnOverride; 38 39import android.telephony.SubscriptionManager; 40import android.telephony.SubInfoRecord; 41import android.telephony.TelephonyManager; 42 43import java.util.ArrayList; 44import java.util.List; 45import java.util.HashMap; 46import java.util.Map.Entry; 47import java.util.Set; 48/** 49 * SubscriptionController to provide an inter-process communication to 50 * access Sms in Icc. 51 * 52 * Any setters which take subId, slotId or phoneId as a parameter will throw an exception if the 53 * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID. 54 * 55 * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling 56 * getPhoneId(DEFAULT_SUB_ID) will return the same as getPhoneId(getDefaultSubId()). 57 * 58 * Finally, any getters which perform the mapping between subscriptions, slots and phones will 59 * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters 60 * will fail and return the appropriate error value. Ie calling getSlotId(INVALID_SUB_ID) will 61 * return INVALID_SLOT_ID and calling getSubInfoForSubscriber(INVALID_SUB_ID) will return null. 62 * 63 */ 64public class SubscriptionController extends ISub.Stub { 65 static final String LOG_TAG = "SUB"; 66 static final boolean DBG = true; 67 static final boolean VDBG = false; 68 69 protected final Object mLock = new Object(); 70 protected boolean mSuccess; 71 72 /** The singleton instance. */ 73 private static SubscriptionController sInstance = null; 74 protected static PhoneProxy[] sProxyPhones; 75 protected static Context mContext; 76 protected static CallManager mCM; 77 78 private static final int RES_TYPE_BACKGROUND_DARK = 0; 79 private static final int RES_TYPE_BACKGROUND_LIGHT = 1; 80 81 private static final int[] sSimBackgroundDarkRes = setSimResource(RES_TYPE_BACKGROUND_DARK); 82 private static final int[] sSimBackgroundLightRes = setSimResource(RES_TYPE_BACKGROUND_LIGHT); 83 84 //FIXME this does not allow for multiple subs in a slot 85 private static HashMap<Integer, Long> mSimInfo = new HashMap<Integer, Long>(); 86 private static long mDefaultVoiceSubId = SubscriptionManager.INVALID_SUB_ID; 87 private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_ID; 88 89 private static final int EVENT_WRITE_MSISDN_DONE = 1; 90 91 protected Handler mHandler = new Handler() { 92 @Override 93 public void handleMessage(Message msg) { 94 AsyncResult ar; 95 96 switch (msg.what) { 97 case EVENT_WRITE_MSISDN_DONE: 98 ar = (AsyncResult) msg.obj; 99 synchronized (mLock) { 100 mSuccess = (ar.exception == null); 101 logd("EVENT_WRITE_MSISDN_DONE, mSuccess = "+mSuccess); 102 mLock.notifyAll(); 103 } 104 break; 105 } 106 } 107 }; 108 109 110 public static SubscriptionController init(Phone phone) { 111 synchronized (SubscriptionController.class) { 112 if (sInstance == null) { 113 sInstance = new SubscriptionController(phone); 114 } else { 115 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 116 } 117 return sInstance; 118 } 119 } 120 121 public static SubscriptionController init(Context c, CommandsInterface[] ci) { 122 synchronized (SubscriptionController.class) { 123 if (sInstance == null) { 124 sInstance = new SubscriptionController(c); 125 } else { 126 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 127 } 128 return sInstance; 129 } 130 } 131 132 public static SubscriptionController getInstance() { 133 if (sInstance == null) 134 { 135 Log.wtf(LOG_TAG, "getInstance null"); 136 } 137 138 return sInstance; 139 } 140 141 private SubscriptionController(Context c) { 142 mContext = c; 143 mCM = CallManager.getInstance(); 144 145 if(ServiceManager.getService("isub") == null) { 146 ServiceManager.addService("isub", this); 147 } 148 149 logd("SubscriptionController init by Context"); 150 } 151 152 private boolean isSubInfoReady() { 153 return mSimInfo.size() > 0; 154 } 155 156 private SubscriptionController(Phone phone) { 157 mContext = phone.getContext(); 158 mCM = CallManager.getInstance(); 159 160 if(ServiceManager.getService("isub") == null) { 161 ServiceManager.addService("isub", this); 162 } 163 164 logd("SubscriptionController init by Phone"); 165 } 166 167 /** 168 * Make sure the caller has the READ_PHONE_STATE permission. 169 * 170 * @throws SecurityException if the caller does not have the required permission 171 */ 172 private void enforceSubscriptionPermission() { 173 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, 174 "Requires READ_PHONE_STATE"); 175 } 176 177 /** 178 * Broadcast when subinfo settings has chanded 179 * @SubId The unique SubInfoRecord index in database 180 * @param columnName The column that is updated 181 * @param intContent The updated integer value 182 * @param stringContent The updated string value 183 */ 184 private void broadcastSimInfoContentChanged(long subId, 185 String columnName, int intContent, String stringContent) { 186 Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE); 187 intent.putExtra(BaseColumns._ID, subId); 188 intent.putExtra(TelephonyIntents.EXTRA_COLUMN_NAME, columnName); 189 intent.putExtra(TelephonyIntents.EXTRA_INT_CONTENT, intContent); 190 intent.putExtra(TelephonyIntents.EXTRA_STRING_CONTENT, stringContent); 191 if (intContent != SubscriptionManager.DEFAULT_INT_VALUE) { 192 logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + intContent); 193 } else { 194 logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + stringContent); 195 } 196 mContext.sendBroadcast(intent); 197 } 198 199 200 /** 201 * New SubInfoRecord instance and fill in detail info 202 * @param cursor 203 * @return the query result of desired SubInfoRecord 204 */ 205 private SubInfoRecord getSubInfoRecord(Cursor cursor) { 206 SubInfoRecord info = new SubInfoRecord(); 207 info.subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); 208 info.iccId = cursor.getString(cursor.getColumnIndexOrThrow( 209 SubscriptionManager.ICC_ID)); 210 info.slotId = cursor.getInt(cursor.getColumnIndexOrThrow( 211 SubscriptionManager.SIM_ID)); 212 info.displayName = cursor.getString(cursor.getColumnIndexOrThrow( 213 SubscriptionManager.DISPLAY_NAME)); 214 info.nameSource = cursor.getInt(cursor.getColumnIndexOrThrow( 215 SubscriptionManager.NAME_SOURCE)); 216 info.color = cursor.getInt(cursor.getColumnIndexOrThrow( 217 SubscriptionManager.COLOR)); 218 info.number = cursor.getString(cursor.getColumnIndexOrThrow( 219 SubscriptionManager.NUMBER)); 220 info.displayNumberFormat = cursor.getInt(cursor.getColumnIndexOrThrow( 221 SubscriptionManager.DISPLAY_NUMBER_FORMAT)); 222 info.dataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow( 223 SubscriptionManager.DATA_ROAMING)); 224 225 int size = sSimBackgroundDarkRes.length; 226 if (info.color >= 0 && info.color < size) { 227 info.simIconRes[RES_TYPE_BACKGROUND_DARK] = sSimBackgroundDarkRes[info.color]; 228 info.simIconRes[RES_TYPE_BACKGROUND_LIGHT] = sSimBackgroundLightRes[info.color]; 229 } 230 info.mcc = cursor.getInt(cursor.getColumnIndexOrThrow( 231 SubscriptionManager.MCC)); 232 info.mnc = cursor.getInt(cursor.getColumnIndexOrThrow( 233 SubscriptionManager.MNC)); 234 235 logd("[getSubInfoRecord] SubId:" + info.subId + " iccid:" + info.iccId + " slotId:" + 236 info.slotId + " displayName:" + info.displayName + " color:" + info.color + 237 " mcc/mnc:" + info.mcc + "/" + info.mnc); 238 239 return info; 240 } 241 242 /** 243 * Query SubInfoRecord(s) from subinfo database 244 * @param selection A filter declaring which rows to return 245 * @param queryKey query key content 246 * @return Array list of queried result from database 247 */ 248 private List<SubInfoRecord> getSubInfo(String selection, Object queryKey) { 249 logd("selection:" + selection + " " + queryKey); 250 String[] selectionArgs = null; 251 if (queryKey != null) { 252 selectionArgs = new String[] {queryKey.toString()}; 253 } 254 ArrayList<SubInfoRecord> subList = null; 255 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 256 null, selection, selectionArgs, null); 257 try { 258 if (cursor != null) { 259 while (cursor.moveToNext()) { 260 SubInfoRecord subInfo = getSubInfoRecord(cursor); 261 if (subInfo != null) 262 { 263 if (subList == null) 264 { 265 subList = new ArrayList<SubInfoRecord>(); 266 } 267 subList.add(subInfo); 268 } 269 } 270 } else { 271 logd("Query fail"); 272 } 273 } finally { 274 if (cursor != null) { 275 cursor.close(); 276 } 277 } 278 279 return subList; 280 } 281 282 283 284 /** 285 * Get the SubInfoRecord according to an index 286 * @param subId The unique SubInfoRecord index in database 287 * @return SubInfoRecord, maybe null 288 */ 289 @Override 290 public SubInfoRecord getSubInfoForSubscriber(long subId) { 291 logd("[getSubInfoForSubscriberx]+ subId:" + subId); 292 enforceSubscriptionPermission(); 293 294 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 295 subId = getDefaultSubId(); 296 } 297 if (!SubscriptionManager.isValidSubId(subId) || !isSubInfoReady()) { 298 logd("[getSubInfoForSubscriberx]- invalid subId or not ready"); 299 return null; 300 } 301 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 302 null, BaseColumns._ID + "=?", new String[] {Long.toString(subId)}, null); 303 try { 304 if (cursor != null) { 305 if (cursor.moveToFirst()) { 306 logd("[getSubInfoForSubscriberx]- Info detail:"); 307 return getSubInfoRecord(cursor); 308 } 309 } 310 } finally { 311 if (cursor != null) { 312 cursor.close(); 313 } 314 } 315 logd("[getSubInfoForSubscriber]- null info return"); 316 317 return null; 318 } 319 320 /** 321 * Get the SubInfoRecord according to an IccId 322 * @param iccId the IccId of SIM card 323 * @return SubInfoRecord, maybe null 324 */ 325 @Override 326 public List<SubInfoRecord> getSubInfoUsingIccId(String iccId) { 327 logd("[getSubInfoUsingIccId]+ iccId:" + iccId); 328 enforceSubscriptionPermission(); 329 330 if (iccId == null || !isSubInfoReady()) { 331 logd("[getSubInfoUsingIccId]- null iccid or not ready"); 332 return null; 333 } 334 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 335 null, SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null); 336 ArrayList<SubInfoRecord> subList = null; 337 try { 338 if (cursor != null) { 339 while (cursor.moveToNext()) { 340 SubInfoRecord subInfo = getSubInfoRecord(cursor); 341 if (subInfo != null) 342 { 343 if (subList == null) 344 { 345 subList = new ArrayList<SubInfoRecord>(); 346 } 347 subList.add(subInfo); 348 } 349 } 350 } else { 351 logd("Query fail"); 352 } 353 } finally { 354 if (cursor != null) { 355 cursor.close(); 356 } 357 } 358 359 return subList; 360 } 361 362 /** 363 * Get the SubInfoRecord according to slotId 364 * @param slotId the slot which the SIM is inserted 365 * @return SubInfoRecord, maybe null 366 */ 367 @Override 368 public List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) { 369 return getSubInfoUsingSlotIdWithCheck(slotId, true); 370 } 371 372 /** 373 * Get all the SubInfoRecord(s) in subinfo database 374 * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before 375 */ 376 @Override 377 public List<SubInfoRecord> getAllSubInfoList() { 378 logd("[getAllSubInfoList]+"); 379 enforceSubscriptionPermission(); 380 381 List<SubInfoRecord> subList = null; 382 subList = getSubInfo(null, null); 383 if (subList != null) { 384 logd("[getAllSubInfoList]- " + subList.size() + " infos return"); 385 } else { 386 logd("[getAllSubInfoList]- no info return"); 387 } 388 389 return subList; 390 } 391 392 /** 393 * Get the SubInfoRecord(s) of the currently inserted SIM(s) 394 * @return Array list of currently inserted SubInfoRecord(s) 395 */ 396 @Override 397 public List<SubInfoRecord> getActiveSubInfoList() { 398 logd("[getActiveSubInfoList]+"); 399 enforceSubscriptionPermission(); 400 401 List<SubInfoRecord> subList = null; 402 403 if (!isSubInfoReady()) { 404 logd("[getActiveSubInfoList] Sub Controller not ready"); 405 return subList; 406 } 407 408 subList = getSubInfo(SubscriptionManager.SIM_ID 409 + "!=" + SubscriptionManager.INVALID_SLOT_ID, null); 410 if (subList != null) { 411 logd("[getActiveSubInfoList]- " + subList.size() + " infos return"); 412 } else { 413 logd("[getActiveSubInfoList]- no info return"); 414 } 415 416 return subList; 417 } 418 419 /** 420 * Get the SUB count of active SUB(s) 421 * @return active SIM count 422 */ 423 @Override 424 public int getActiveSubInfoCount() { 425 logd("[getActiveSubInfoCount]+"); 426 List<SubInfoRecord> records = getActiveSubInfoList(); 427 if (records == null) { 428 logd("[getActiveSubInfoCount] records null"); 429 return 0; 430 } 431 logd("[getActiveSubInfoCount]- count: " + records.size()); 432 return records.size(); 433 } 434 435 /** 436 * Get the SUB count of all SUB(s) in subinfo database 437 * @return all SIM count in database, include what was inserted before 438 */ 439 @Override 440 public int getAllSubInfoCount() { 441 logd("[getAllSubInfoCount]+"); 442 enforceSubscriptionPermission(); 443 444 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 445 null, null, null, null); 446 try { 447 if (cursor != null) { 448 int count = cursor.getCount(); 449 logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB"); 450 return count; 451 } 452 } finally { 453 if (cursor != null) { 454 cursor.close(); 455 } 456 } 457 logd("[getAllSubInfoCount]- no SUB in DB"); 458 459 return 0; 460 } 461 462 /** 463 * Add a new SubInfoRecord to subinfo database if needed 464 * @param iccId the IccId of the SIM card 465 * @param slotId the slot which the SIM is inserted 466 * @return the URL of the newly created row or the updated row 467 */ 468 @Override 469 public int addSubInfoRecord(String iccId, int slotId) { 470 logd("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId); 471 enforceSubscriptionPermission(); 472 473 if (iccId == null) { 474 logd("[addSubInfoRecord]- null iccId"); 475 } 476 477 long[] subIds = getSubId(slotId); 478 if (subIds == null || subIds.length == 0) { 479 logd("[addSubInfoRecord]- getSubId fail"); 480 return 0; 481 } 482 483 String nameToSet; 484 SpnOverride mSpnOverride = new SpnOverride(); 485 486 String CarrierName = TelephonyManager.getDefault().getSimOperator(subIds[0]); 487 logd("[addSubInfoRecord] CarrierName = " + CarrierName); 488 489 if (mSpnOverride.containsCarrier(CarrierName)) { 490 nameToSet = mSpnOverride.getSpn(CarrierName) + " 0" + Integer.toString(slotId + 1); 491 logd("[addSubInfoRecord] Found, name = " + nameToSet); 492 } else { 493 nameToSet = "SUB 0" + Integer.toString(slotId + 1); 494 logd("[addSubInfoRecord] Not found, name = " + nameToSet); 495 } 496 497 ContentResolver resolver = mContext.getContentResolver(); 498 Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI, 499 new String[] {BaseColumns._ID, SubscriptionManager.SIM_ID, 500 SubscriptionManager.NAME_SOURCE}, SubscriptionManager.ICC_ID + "=?", 501 new String[] {iccId}, null); 502 503 try { 504 if (cursor == null || !cursor.moveToFirst()) { 505 ContentValues value = new ContentValues(); 506 value.put(SubscriptionManager.ICC_ID, iccId); 507 // default SIM color differs between slots 508 value.put(SubscriptionManager.COLOR, slotId); 509 value.put(SubscriptionManager.SIM_ID, slotId); 510 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 511 Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value); 512 logd("[addSubInfoRecord]- New record created: " + uri); 513 } else { 514 long subId = cursor.getLong(0); 515 int oldSimInfoId = cursor.getInt(1); 516 int nameSource = cursor.getInt(2); 517 ContentValues value = new ContentValues(); 518 519 if (slotId != oldSimInfoId) { 520 value.put(SubscriptionManager.SIM_ID, slotId); 521 } 522 523 if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) { 524 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 525 } 526 527 if (value.size() > 0) { 528 resolver.update(SubscriptionManager.CONTENT_URI, value, 529 BaseColumns._ID + "=" + Long.toString(subId), null); 530 } 531 532 logd("[addSubInfoRecord]- Record already exist"); 533 } 534 } finally { 535 if (cursor != null) { 536 cursor.close(); 537 } 538 } 539 540 cursor = resolver.query(SubscriptionManager.CONTENT_URI, null, 541 SubscriptionManager.SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null); 542 543 try { 544 if (cursor != null && cursor.moveToFirst()) { 545 do { 546 long subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); 547 // If mSimInfo already has a valid subId for a slotId/phoneId, 548 // do not add another subId for same slotId/phoneId. 549 Long currentSubId = mSimInfo.get(slotId); 550 if (currentSubId == null || !SubscriptionManager.isValidSubId(currentSubId)) { 551 // TODO While two subs active, if user deactivats first 552 // one, need to update the default subId with second 553 // one. 554 mSimInfo.put(slotId, subId); 555 int simCount = TelephonyManager.getDefault().getSimCount(); 556 long defaultSubId = getDefaultSubId(); 557 logd("[addSubInfoRecord] mSimInfo.size=" + mSimInfo.size() 558 + " slotId=" + slotId + " subId=" + subId 559 + " defaultSubId=" + defaultSubId + " simCount=" + simCount); 560 561 // Set the default sub if not set 562 if (!SubscriptionManager.isValidSubId(defaultSubId)) { 563 setDefaultSubId(subId); 564 } 565 // If single sim device, set this subscription as the default for everything 566 if (simCount == 1) { 567 logd("[addSubInfoRecord] one sim set defaults to subId=" + subId); 568 setDefaultDataSubId(subId); 569 setDefaultSmsSubId(subId); 570 setDefaultVoiceSubId(subId); 571 } 572 } else { 573 logd("[addSubInfoRecord] currentSubId != null && currentSubId is valid, IGNORE"); 574 } 575 logd("[addSubInfoRecord]- hashmap("+slotId+","+subId+")"); 576 } while (cursor.moveToNext()); 577 } 578 } finally { 579 if (cursor != null) { 580 cursor.close(); 581 } 582 } 583 584 int size = mSimInfo.size(); 585 logd("[addSubInfoRecord]- info size="+size); 586 587 // Once the records are loaded, notify DcTracker 588 updateAllDataConnectionTrackers(); 589 590 // FIXME this does not match the javadoc 591 return 1; 592 } 593 594 /** 595 * Set SIM color by simInfo index 596 * @param color the color of the SIM 597 * @param subId the unique SubInfoRecord index in database 598 * @return the number of records updated 599 */ 600 @Override 601 public int setColor(int color, long subId) { 602 logd("[setColor]+ color:" + color + " subId:" + subId); 603 enforceSubscriptionPermission(); 604 605 validateSubId(subId); 606 int size = sSimBackgroundDarkRes.length; 607 if (color < 0 || color >= size) { 608 logd("[setColor]- fail"); 609 return -1; 610 } 611 ContentValues value = new ContentValues(1); 612 value.put(SubscriptionManager.COLOR, color); 613 logd("[setColor]- color:" + color + " set"); 614 615 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 616 BaseColumns._ID + "=" + Long.toString(subId), null); 617 broadcastSimInfoContentChanged(subId, SubscriptionManager.COLOR, 618 color, SubscriptionManager.DEFAULT_STRING_VALUE); 619 620 return result; 621 } 622 623 /** 624 * Set display name by simInfo index 625 * @param displayName the display name of SIM card 626 * @param subId the unique SubInfoRecord index in database 627 * @return the number of records updated 628 */ 629 @Override 630 public int setDisplayName(String displayName, long subId) { 631 return setDisplayNameUsingSrc(displayName, subId, -1); 632 } 633 634 /** 635 * Set display name by simInfo index with name source 636 * @param displayName the display name of SIM card 637 * @param subId the unique SubInfoRecord index in database 638 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 639 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED 640 * @return the number of records updated 641 */ 642 @Override 643 public int setDisplayNameUsingSrc(String displayName, long subId, long nameSource) { 644 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId 645 + " nameSource:" + nameSource); 646 enforceSubscriptionPermission(); 647 648 validateSubId(subId); 649 String nameToSet; 650 if (displayName == null) { 651 nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES); 652 } else { 653 nameToSet = displayName; 654 } 655 ContentValues value = new ContentValues(1); 656 value.put(SubscriptionManager.DISPLAY_NAME, nameToSet); 657 if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) { 658 logd("Set nameSource=" + nameSource); 659 value.put(SubscriptionManager.NAME_SOURCE, nameSource); 660 } 661 logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); 662 663 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 664 BaseColumns._ID + "=" + Long.toString(subId), null); 665 broadcastSimInfoContentChanged(subId, SubscriptionManager.DISPLAY_NAME, 666 SubscriptionManager.DEFAULT_INT_VALUE, nameToSet); 667 668 return result; 669 } 670 671 /** 672 * Set phone number by subId 673 * @param number the phone number of the SIM 674 * @param subId the unique SubInfoRecord index in database 675 * @return the number of records updated 676 */ 677 @Override 678 public int setDisplayNumber(String number, long subId) { 679 logd("[setDisplayNumber]+ number:" + number + " subId:" + subId); 680 enforceSubscriptionPermission(); 681 682 validateSubId(subId); 683 int result = 0; 684 int phoneId = getPhoneId(subId); 685 686 if (number == null || phoneId < 0 || 687 phoneId >= TelephonyManager.getDefault().getPhoneCount()) { 688 logd("[setDispalyNumber]- fail"); 689 return -1; 690 } 691 ContentValues value = new ContentValues(1); 692 value.put(SubscriptionManager.NUMBER, number); 693 logd("[setDisplayNumber]- number:" + number + " set"); 694 695 Phone phone = sProxyPhones[phoneId]; 696 String alphaTag = TelephonyManager.getDefault().getLine1AlphaTagForSubscriber(subId); 697 698 synchronized(mLock) { 699 mSuccess = false; 700 Message response = mHandler.obtainMessage(EVENT_WRITE_MSISDN_DONE); 701 702 phone.setLine1Number(alphaTag, number, response); 703 704 try { 705 mLock.wait(); 706 } catch (InterruptedException e) { 707 loge("interrupted while trying to write MSISDN"); 708 } 709 } 710 711 if (mSuccess) { 712 result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 713 BaseColumns._ID + "=" + Long.toString(subId), null); 714 logd("[setDisplayNumber]- update result :" + result); 715 broadcastSimInfoContentChanged(subId, SubscriptionManager.NUMBER, 716 SubscriptionManager.DEFAULT_INT_VALUE, number); 717 } 718 719 return result; 720 } 721 722 /** 723 * Set number display format. 0: none, 1: the first four digits, 2: the last four digits 724 * @param format the display format of phone number 725 * @param subId the unique SubInfoRecord index in database 726 * @return the number of records updated 727 */ 728 @Override 729 public int setDisplayNumberFormat(int format, long subId) { 730 logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId); 731 enforceSubscriptionPermission(); 732 733 validateSubId(subId); 734 if (format < 0) { 735 logd("[setDisplayNumberFormat]- fail, return -1"); 736 return -1; 737 } 738 ContentValues value = new ContentValues(1); 739 value.put(SubscriptionManager.DISPLAY_NUMBER_FORMAT, format); 740 logd("[setDisplayNumberFormat]- format:" + format + " set"); 741 742 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 743 BaseColumns._ID + "=" + Long.toString(subId), null); 744 broadcastSimInfoContentChanged(subId, SubscriptionManager.DISPLAY_NUMBER_FORMAT, 745 format, SubscriptionManager.DEFAULT_STRING_VALUE); 746 747 return result; 748 } 749 750 /** 751 * Set data roaming by simInfo index 752 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 753 * @param subId the unique SubInfoRecord index in database 754 * @return the number of records updated 755 */ 756 @Override 757 public int setDataRoaming(int roaming, long subId) { 758 logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 759 enforceSubscriptionPermission(); 760 761 validateSubId(subId); 762 if (roaming < 0) { 763 logd("[setDataRoaming]- fail"); 764 return -1; 765 } 766 ContentValues value = new ContentValues(1); 767 value.put(SubscriptionManager.DATA_ROAMING, roaming); 768 logd("[setDataRoaming]- roaming:" + roaming + " set"); 769 770 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 771 BaseColumns._ID + "=" + Long.toString(subId), null); 772 broadcastSimInfoContentChanged(subId, SubscriptionManager.DATA_ROAMING, 773 roaming, SubscriptionManager.DEFAULT_STRING_VALUE); 774 775 return result; 776 } 777 778 /** 779 * Set MCC/MNC by subscription ID 780 * @param mccMnc MCC/MNC associated with the subscription 781 * @param subId the unique SubInfoRecord index in database 782 * @return the number of records updated 783 */ 784 public int setMccMnc(String mccMnc, long subId) { 785 int mcc = 0; 786 int mnc = 0; 787 try { 788 mcc = Integer.parseInt(mccMnc.substring(0,3)); 789 mnc = Integer.parseInt(mccMnc.substring(3)); 790 } catch (NumberFormatException e) { 791 logd("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc); 792 } 793 logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId); 794 ContentValues value = new ContentValues(2); 795 value.put(SubscriptionManager.MCC, mcc); 796 value.put(SubscriptionManager.MNC, mnc); 797 798 int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value, 799 BaseColumns._ID + "=" + Long.toString(subId), null); 800 broadcastSimInfoContentChanged(subId, SubscriptionManager.MCC, mcc, null); 801 802 return result; 803 } 804 805 806 @Override 807 public int getSlotId(long subId) { 808 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 809 subId = getDefaultSubId(); 810 } 811 if (!SubscriptionManager.isValidSubId(subId)) { 812 logd("[getSlotId]- subId invalid"); 813 return SubscriptionManager.INVALID_SLOT_ID; 814 } 815 816 int size = mSimInfo.size(); 817 818 if (size == 0) 819 { 820 logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead"); 821 return SubscriptionManager.SIM_NOT_INSERTED; 822 } 823 824 for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { 825 int sim = entry.getKey(); 826 long sub = entry.getValue(); 827 828 if (subId == sub) 829 { 830 if (VDBG) logv("[getSlotId]- return = " + sim); 831 return sim; 832 } 833 } 834 835 logd("[getSlotId]- return fail"); 836 return SubscriptionManager.INVALID_SLOT_ID; 837 } 838 839 /** 840 * Return the subId for specified slot Id. 841 * @deprecated 842 */ 843 @Override 844 @Deprecated 845 public long[] getSubId(int slotId) { 846 if (slotId == SubscriptionManager.DEFAULT_SLOT_ID) { 847 logd("[getSubId]- default slotId"); 848 slotId = getSlotId(getDefaultSubId()); 849 } 850 851 //FIXME remove this 852 final long[] DUMMY_VALUES = {-1 - slotId, -1 - slotId}; 853 854 if (!SubscriptionManager.isValidSlotId(slotId)) { 855 logd("[getSubId]- invalid slotId"); 856 return null; 857 } 858 859 //FIXME remove this 860 if (slotId < 0) { 861 logd("[getSubId]- slotId < 0, return dummy instead"); 862 return DUMMY_VALUES; 863 } 864 865 int size = mSimInfo.size(); 866 867 if (size == 0) { 868 logd("[getSubId]- size == 0, return dummy instead"); 869 //FIXME return null 870 return DUMMY_VALUES; 871 } 872 873 ArrayList<Long> subIds = new ArrayList<Long>(); 874 for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { 875 int slot = entry.getKey(); 876 long sub = entry.getValue(); 877 if (slotId == slot) { 878 subIds.add(sub); 879 } 880 } 881 882 logd("[getSubId]-, subIds = " + subIds); 883 int numSubIds = subIds.size(); 884 885 if (numSubIds == 0) { 886 logd("[getSubId]- numSubIds == 0, return dummy instead"); 887 return DUMMY_VALUES; 888 } 889 890 long[] subIdArr = new long[numSubIds]; 891 for (int i = 0; i < numSubIds; i++) { 892 subIdArr[i] = subIds.get(i); 893 } 894 895 return subIdArr; 896 } 897 898 @Override 899 public int getPhoneId(long subId) { 900 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 901 if (VDBG) logv("[getPhoneId]- default subId"); 902 subId = getDefaultSubId(); 903 } 904 905 if (!SubscriptionManager.isValidSubId(subId)) { 906 logd("[getPhoneId]- invalid subId"); 907 return SubscriptionManager.INVALID_PHONE_ID; 908 } 909 910 //FIXME remove this 911 if (subId == -1) { 912 logd("[getPhoneId]- subId == -1, return dummy data"); 913 return 0; 914 } else if (subId == -2) { 915 logd("[getPhoneId]- subId == -2, return dummy data"); 916 return 1; 917 } 918 919 int size = mSimInfo.size(); 920 921 if (size == 0) { 922 logd("getPhoneId, returning defaultPhoneId "); 923 return mDefaultPhoneId; 924 } 925 926 for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { 927 int sim = entry.getKey(); 928 long sub = entry.getValue(); 929 930 if (subId == sub) { 931 if (VDBG) logv("[getPhoneId]- return = " + sim); 932 return sim; 933 } 934 } 935 936 logd("[getPhoneId]- return fail"); 937 return mDefaultPhoneId; 938 939 } 940 941 /** 942 * @return the number of records cleared 943 */ 944 @Override 945 public int clearSubInfo() { 946 enforceSubscriptionPermission(); 947 logd("[clearSubInfo]+"); 948 949 int size = mSimInfo.size(); 950 logd("[clearSubInfo]- info size="+size); 951 952 if (size == 0) { 953 return 0; 954 } 955 956 mSimInfo.clear(); 957 logd("[clearSubInfo]-"); 958 return size; 959 } 960 961 private static int[] setSimResource(int type) { 962 int[] simResource = null; 963 964 switch (type) { 965 case RES_TYPE_BACKGROUND_DARK: 966 simResource = new int[] { 967 com.android.internal.R.drawable.sim_dark_blue, 968 com.android.internal.R.drawable.sim_dark_orange, 969 com.android.internal.R.drawable.sim_dark_green, 970 com.android.internal.R.drawable.sim_dark_purple 971 }; 972 break; 973 case RES_TYPE_BACKGROUND_LIGHT: 974 simResource = new int[] { 975 com.android.internal.R.drawable.sim_light_blue, 976 com.android.internal.R.drawable.sim_light_orange, 977 com.android.internal.R.drawable.sim_light_green, 978 com.android.internal.R.drawable.sim_light_purple 979 }; 980 break; 981 } 982 983 return simResource; 984 } 985 986 private void logv(String msg) { 987 Rlog.v(LOG_TAG, "[SubController]" + msg); 988 } 989 990 private void logd(String msg) { 991 Rlog.d(LOG_TAG, "[SubController]" + msg); 992 } 993 994 private void loge(String msg) { 995 Rlog.e(LOG_TAG, "[SubController]" + msg); 996 } 997 998 @Override 999 @Deprecated 1000 public long getDefaultSubId() { 1001 //FIXME To remove this api, All clients should be using getDefaultVoiceSubId 1002 if (VDBG) logv("getDefaultSubId, value = " + mDefaultVoiceSubId); 1003 return mDefaultVoiceSubId; 1004 } 1005 1006 @Override 1007 public void setDefaultSmsSubId(long subId) { 1008 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 1009 throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID"); 1010 } 1011 logd(" setDefaultSmsSubId subId: " + subId); 1012 Settings.Global.putLong(mContext.getContentResolver(), 1013 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, subId); 1014 broadcastDefaultSmsSubIdChanged(subId); 1015 } 1016 1017 private static void broadcastDefaultSmsSubIdChanged(long subId) { 1018 // Broadcast an Intent for default sms sub change 1019 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED); 1020 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1021 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1022 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1023 } 1024 1025 @Override 1026 public long getDefaultSmsSubId() { 1027 long subId = Settings.Global.getLong(mContext.getContentResolver(), 1028 Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION, 1029 SubscriptionManager.INVALID_SUB_ID); 1030 if (VDBG) logd("getDefaultSmsSubId, value = " + subId); 1031 return subId; 1032 } 1033 1034 @Override 1035 public void setDefaultVoiceSubId(long subId) { 1036 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 1037 throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID"); 1038 } 1039 logd(" setDefaultVoiceSubId subId: " + subId); 1040 Settings.Global.putLong(mContext.getContentResolver(), 1041 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); 1042 broadcastDefaultVoiceSubIdChanged(subId); 1043 } 1044 1045 private static void broadcastDefaultVoiceSubIdChanged(long subId) { 1046 // Broadcast an Intent for default voice sub change 1047 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 1048 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1049 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1050 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1051 } 1052 1053 @Override 1054 public long getDefaultVoiceSubId() { 1055 long subId = Settings.Global.getLong(mContext.getContentResolver(), 1056 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, 1057 SubscriptionManager.INVALID_SUB_ID); 1058 if (VDBG) logd("getDefaultVoiceSubId, value = " + subId); 1059 return subId; 1060 } 1061 1062 @Override 1063 public long getDefaultDataSubId() { 1064 long subId = Settings.Global.getLong(mContext.getContentResolver(), 1065 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, 1066 SubscriptionManager.INVALID_SUB_ID); 1067 if (VDBG) logd("getDefaultDataSubId, value = " + subId); 1068 return subId; 1069 } 1070 1071 @Override 1072 public void setDefaultDataSubId(long subId) { 1073 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 1074 throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID"); 1075 } 1076 logd("setDefaultDataSubId: subId=" + subId); 1077 1078 Settings.Global.putLong(mContext.getContentResolver(), 1079 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); 1080 broadcastDefaultDataSubIdChanged(subId); 1081 1082 // FIXME is this still needed? 1083 updateAllDataConnectionTrackers(); 1084 } 1085 1086 private void updateAllDataConnectionTrackers() { 1087 // Tell Phone Proxies to update data connection tracker 1088 for (int phoneId = 0; phoneId < sProxyPhones.length; phoneId++) { 1089 sProxyPhones[phoneId].updateDataConnectionTracker(); 1090 } 1091 } 1092 1093 private static void broadcastDefaultDataSubIdChanged(long subId) { 1094 // Broadcast an Intent for default data sub change 1095 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 1096 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1097 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1098 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1099 } 1100 1101 /* Sets the default subscription. If only one sub is active that 1102 * sub is set as default subId. If two or more sub's are active 1103 * the first sub is set as default subscription 1104 */ 1105 // FIXME 1106 public void setDefaultSubId(long subId) { 1107 if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 1108 throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID"); 1109 } 1110 logd("[setDefaultSubId] subId=" + subId); 1111 if (SubscriptionManager.isValidSubId(subId)) { 1112 int phoneId = getPhoneId(subId); 1113 if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) { 1114 mDefaultVoiceSubId = subId; 1115 // Update MCC MNC device configuration information 1116 String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId); 1117 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); 1118 1119 // Broadcast an Intent for default sub change 1120 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); 1121 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1122 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId); 1123 if (VDBG) { 1124 logd("setDefaultSubId: broadcast default subId changed phoneId=" + phoneId 1125 + " subId=" + subId); 1126 } 1127 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1128 } else { 1129 if (VDBG) { 1130 logd("setDefaultSubId: not set invalid phoneId=" + phoneId + " subId=" + subId); 1131 } 1132 } 1133 } 1134 } 1135 1136 @Override 1137 public void clearDefaultsForInactiveSubIds() { 1138 final List<SubInfoRecord> records = getActiveSubInfoList(); 1139 logd("[clearDefaultsForInactiveSubIds] records: " + records); 1140 if (shouldDefaultBeCleared(records, getDefaultDataSubId())) { 1141 logd("[clearDefaultsForInactiveSubIds]: clearing default data sub id"); 1142 setDefaultDataSubId(SubscriptionManager.INVALID_SUB_ID); 1143 } 1144 if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) { 1145 logd("[clearDefaultsForInactiveSubIds]: clearing default sms sub id"); 1146 setDefaultSmsSubId(SubscriptionManager.INVALID_SUB_ID); 1147 } 1148 if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) { 1149 logd("[clearDefaultsForInactiveSubIds]: clearing default voice sub id"); 1150 setDefaultVoiceSubId(SubscriptionManager.INVALID_SUB_ID); 1151 } 1152 } 1153 1154 private boolean shouldDefaultBeCleared(List<SubInfoRecord> records, long subId) { 1155 logd("[shouldDefaultBeCleared] subId: " + subId); 1156 if (records == null) { 1157 return true; 1158 } 1159 if (subId == SubscriptionManager.ASK_USER_SUB_ID && records.size() > 1) { 1160 // Only allow ASK_USER_SUB_ID if there is more than 1 subscription. 1161 return false; 1162 } 1163 for (SubInfoRecord record : records) { 1164 logd("[shouldDefaultBeCleared] Record.subId: " + record.subId); 1165 if (record.subId == subId) { 1166 return false; 1167 } 1168 } 1169 return true; 1170 } 1171 1172 /* This should return long and not long [] since each phone has 1173 * exactly 1 sub id for now, it could return the 0th element 1174 * returned from getSubId() 1175 */ 1176 // FIXME will design a mechanism to manage the relationship between PhoneId/SlotId/SubId 1177 // since phoneId = SlotId is not always true 1178 public long getSubIdUsingPhoneId(int phoneId) { 1179 long[] subIds = getSubId(phoneId); 1180 if (subIds == null || subIds.length == 0) { 1181 return SubscriptionManager.INVALID_SUB_ID; 1182 } 1183 return subIds[0]; 1184 } 1185 1186 public long[] getSubIdUsingSlotId(int slotId) { 1187 return getSubId(slotId); 1188 } 1189 1190 public List<SubInfoRecord> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) { 1191 logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId); 1192 enforceSubscriptionPermission(); 1193 1194 if (slotId == SubscriptionManager.DEFAULT_SLOT_ID) { 1195 slotId = getSlotId(getDefaultSubId()); 1196 } 1197 if (!SubscriptionManager.isValidSlotId(slotId)) { 1198 logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId"); 1199 return null; 1200 } 1201 1202 if (needCheck && !isSubInfoReady()) { 1203 logd("[getSubInfoUsingSlotIdWithCheck]- not ready"); 1204 return null; 1205 } 1206 1207 Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI, 1208 null, SubscriptionManager.SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null); 1209 ArrayList<SubInfoRecord> subList = null; 1210 try { 1211 if (cursor != null) { 1212 while (cursor.moveToNext()) { 1213 SubInfoRecord subInfo = getSubInfoRecord(cursor); 1214 if (subInfo != null) 1215 { 1216 if (subList == null) 1217 { 1218 subList = new ArrayList<SubInfoRecord>(); 1219 } 1220 subList.add(subInfo); 1221 } 1222 } 1223 } 1224 } finally { 1225 if (cursor != null) { 1226 cursor.close(); 1227 } 1228 } 1229 logd("[getSubInfoUsingSlotId]- null info return"); 1230 1231 return subList; 1232 } 1233 1234 private void validateSubId(long subId) { 1235 logd("validateSubId subId: " + subId); 1236 if (!SubscriptionManager.isValidSubId(subId)) { 1237 throw new RuntimeException("Invalid sub id passed as parameter"); 1238 } else if (subId == SubscriptionManager.DEFAULT_SUB_ID) { 1239 throw new RuntimeException("Default sub id passed as parameter"); 1240 } 1241 } 1242 1243 public void updatePhonesAvailability(PhoneProxy[] phones) { 1244 sProxyPhones = phones; 1245 } 1246 1247 /** 1248 * @return the list of subId's that are active, is never null but the length maybe 0. 1249 */ 1250 @Override 1251 public long[] getActiveSubIdList() { 1252 Set<Entry<Integer, Long>> simInfoSet = mSimInfo.entrySet(); 1253 logd("getActiveSubIdList: simInfoSet=" + simInfoSet); 1254 1255 long[] subIdArr = new long[simInfoSet.size()]; 1256 int i = 0; 1257 for (Entry<Integer, Long> entry: simInfoSet) { 1258 long sub = entry.getValue(); 1259 subIdArr[i] = sub; 1260 i++; 1261 } 1262 1263 logd("getActiveSubIdList: X subIdArr.length=" + subIdArr.length); 1264 return subIdArr; 1265 } 1266 1267} 1268