SubscriptionController.java revision 374fb1f013fcddac3421720a7f2123e8f025c52a
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.Manifest; 20import android.app.AppOpsManager; 21import android.app.PendingIntent; 22import android.content.Context; 23import android.os.AsyncResult; 24import android.os.Binder; 25import android.os.Handler; 26import android.os.Message; 27import android.os.ServiceManager; 28import android.os.UserHandle; 29import android.telephony.Rlog; 30import android.util.Log; 31import android.net.Uri; 32import android.database.Cursor; 33import android.content.Intent; 34import android.provider.BaseColumns; 35import android.provider.Settings; 36import android.provider.Settings.SettingNotFoundException; 37import android.content.ContentResolver; 38import android.content.ContentValues; 39 40import com.android.internal.telephony.ISub; 41import com.android.internal.telephony.dataconnection.DctController; 42import com.android.internal.telephony.uicc.IccConstants; 43import com.android.internal.telephony.uicc.IccFileHandler; 44import android.telephony.SubscriptionManager; 45import android.telephony.SubInfoRecord; 46import android.telephony.TelephonyManager; 47 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.List; 51import java.util.HashMap; 52import java.util.Iterator; 53import java.util.Map.Entry; 54/** 55 * SubscriptionController to provide an inter-process communication to 56 * access Sms in Icc. 57 */ 58public class SubscriptionController extends ISub.Stub { 59 static final String LOG_TAG = "SUB"; 60 static final boolean DBG = true; 61 static final boolean VDBG = false; 62 63 protected final Object mLock = new Object(); 64 protected boolean mSuccess; 65 66 /** The singleton instance. */ 67 private static SubscriptionController sInstance = null; 68 protected static Phone mPhone; 69 protected static Context mContext; 70 71 private DataConnectionHandler mDataConnectionHandler; 72 73 public static final Uri CONTENT_URI = 74 Uri.parse("content://telephony/siminfo"); 75 76 public static final int DEFAULT_INT_VALUE = -100; 77 78 public static final String DEFAULT_STRING_VALUE = "N/A"; 79 80 private static final int EVENT_SET_DEFAULT_DATA_DONE = 1; 81 82 public static final int EXTRA_VALUE_NEW_SIM = 1; 83 public static final int EXTRA_VALUE_REMOVE_SIM = 2; 84 public static final int EXTRA_VALUE_REPOSITION_SIM = 3; 85 public static final int EXTRA_VALUE_NOCHANGE = 4; 86 87 public static final String INTENT_KEY_DETECT_STATUS = "simDetectStatus"; 88 public static final String INTENT_KEY_SIM_COUNT = "simCount"; 89 public static final String INTENT_KEY_NEW_SIM_SLOT = "newSIMSlot"; 90 public static final String INTENT_KEY_NEW_SIM_STATUS = "newSIMStatus"; 91 92 /** 93 * The ICC ID of a SIM. 94 * <P>Type: TEXT (String)</P> 95 */ 96 public static final String ICC_ID = "icc_id"; 97 98 /** 99 * <P>Type: INTEGER (int)</P> 100 */ 101 public static final String SIM_ID = "sim_id"; 102 103 public static final int SIM_NOT_INSERTED = -1; 104 105 /** 106 * The display name of a SIM. 107 * <P>Type: TEXT (String)</P> 108 */ 109 public static final String DISPLAY_NAME = "display_name"; 110 111 public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName; 112 113 /** 114 * The display name source of a SIM. 115 * <P>Type: INT (int)</P> 116 */ 117 public static final String NAME_SOURCE = "name_source"; 118 119 public static final int DEFAULT_SOURCE = 0; 120 121 public static final int SIM_SOURCE = 1; 122 123 public static final int USER_INPUT = 2; 124 125 /** 126 * The color of a SIM. 127 * <P>Type: INTEGER (int)</P> 128 */ 129 public static final String COLOR = "color"; 130 131 public static final int COLOR_1 = 0; 132 133 public static final int COLOR_2 = 1; 134 135 public static final int COLOR_3 = 2; 136 137 public static final int COLOR_4 = 3; 138 139 public static final int COLOR_DEFAULT = COLOR_1; 140 141 /** 142 * The phone number of a SIM. 143 * <P>Type: TEXT (String)</P> 144 */ 145 public static final String NUMBER = "number"; 146 147 /** 148 * The number display format of a SIM. 149 * <P>Type: INTEGER (int)</P> 150 */ 151 public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; 152 153 public static final int DISPALY_NUMBER_NONE = 0; 154 155 public static final int DISPLAY_NUMBER_FIRST = 1; 156 157 public static final int DISPLAY_NUMBER_LAST = 2; 158 159 public static final int DISLPAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST; 160 161 /** 162 * Permission for data roaming of a SIM. 163 * <P>Type: INTEGER (int)</P> 164 */ 165 public static final String DATA_ROAMING = "data_roaming"; 166 167 public static final int DATA_ROAMING_ENABLE = 1; 168 169 public static final int DATA_ROAMING_DISABLE = 0; 170 171 public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; 172 173 private static final int RES_TYPE_BACKGROUND_DARK = 0; 174 175 private static final int RES_TYPE_BACKGROUND_LIGHT = 1; 176 177 private static final int[] sSimBackgroundDarkRes = setSimResource(RES_TYPE_BACKGROUND_DARK); 178 179 private static final int[] sSimBackgroundLightRes = setSimResource(RES_TYPE_BACKGROUND_LIGHT); 180 181 private static HashMap<Integer, Long> mSimInfo = new HashMap<Integer, Long>(); 182 // FIXME define an invalid SUB_ID, and use that below instead of "1". 183 private static long mDefaultVoiceSubId = 1; 184 private static int mDefaultPhoneId = 0; 185 186 public static SubscriptionController init(Phone phone) { 187 synchronized (SubscriptionController.class) { 188 if (sInstance == null) { 189 sInstance = new SubscriptionController(phone); 190 } else { 191 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 192 } 193 return sInstance; 194 } 195 } 196 197 public static SubscriptionController init(Context c, CommandsInterface[] ci) { 198 synchronized (SubscriptionController.class) { 199 if (sInstance == null) { 200 sInstance = new SubscriptionController(c); 201 } else { 202 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 203 } 204 return sInstance; 205 } 206 } 207 208 public static SubscriptionController getInstance() { 209 if (sInstance == null) 210 { 211 Log.wtf(LOG_TAG, "getInstance null"); 212 } 213 214 return sInstance; 215 } 216 217 private SubscriptionController(Context c) { 218 mContext = c; 219 220 if(ServiceManager.getService("isub") == null) { 221 ServiceManager.addService("isub", this); 222 } 223 224 mDataConnectionHandler = new DataConnectionHandler(); 225 logd("SubscriptionController init by Context"); 226 } 227 228 private boolean isSubInfoReady() { 229 return (mSimInfo.size() > 0) ? true : false; 230 } 231 232 private SubscriptionController(Phone phone) { 233 mContext = phone.getContext(); 234 235 if(ServiceManager.getService("isub") == null) { 236 ServiceManager.addService("isub", this); 237 } 238 239 logd("SubscriptionController init by Phone"); 240 } 241 242 /** 243 * Broadcast when subinfo settings has chanded 244 * @SubId The unique SubInfoRecord index in database 245 * @param columnName The column that is updated 246 * @param intContent The updated integer value 247 * @param stringContent The updated string value 248 */ 249 private void broadcastSimInfoContentChanged(long subId, 250 String columnName, int intContent, String stringContent) { 251 Intent intent = new Intent(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE); 252 intent.putExtra(BaseColumns._ID, subId); 253 intent.putExtra(TelephonyIntents.EXTRA_COLUMN_NAME, columnName); 254 intent.putExtra(TelephonyIntents.EXTRA_INT_CONTENT, intContent); 255 intent.putExtra(TelephonyIntents.EXTRA_STRING_CONTENT, stringContent); 256 if (intContent != DEFAULT_INT_VALUE) { 257 logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + intContent); 258 } else { 259 logd("SubInfoRecord" + subId + " changed, " + columnName + " -> " + stringContent); 260 } 261 mContext.sendBroadcast(intent); 262 } 263 264 265 /** 266 * New SubInfoRecord instance and fill in detail info 267 * @param cursor 268 * @return the query result of desired SubInfoRecord 269 */ 270 private SubInfoRecord getSubInfoRecord(Cursor cursor) { 271 SubInfoRecord info = new SubInfoRecord(); 272 info.mSubId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); 273 info.mIccId = cursor.getString(cursor.getColumnIndexOrThrow(ICC_ID)); 274 info.mSlotId = cursor.getInt(cursor.getColumnIndexOrThrow(SIM_ID)); 275 info.mDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME)); 276 info.mNameSource = cursor.getInt(cursor.getColumnIndexOrThrow(NAME_SOURCE)); 277 info.mColor = cursor.getInt(cursor.getColumnIndexOrThrow(COLOR)); 278 info.mNumber = cursor.getString(cursor.getColumnIndexOrThrow(NUMBER)); 279 info.mDispalyNumberFormat = cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_NUMBER_FORMAT)); 280 info.mDataRoaming = cursor.getInt(cursor.getColumnIndexOrThrow(DATA_ROAMING)); 281 282 int size = sSimBackgroundDarkRes.length; 283 if (info.mColor >= 0 && info.mColor < size) { 284 info.mSimIconRes[RES_TYPE_BACKGROUND_DARK] = sSimBackgroundDarkRes[info.mColor]; 285 info.mSimIconRes[RES_TYPE_BACKGROUND_LIGHT] = sSimBackgroundLightRes[info.mColor]; 286 } 287 logd("[getSubInfoRecord] SubId:" + info.mSubId + " iccid:" + info.mIccId + " slotId:" + info.mSlotId 288 + " displayName:" + info.mDisplayName + " color:" + info.mColor); 289 290 return info; 291 } 292 293 /** 294 * Query SubInfoRecord(s) from subinfo database 295 * @param selection A filter declaring which rows to return 296 * @param queryKey query key content 297 * @return Array list of queried result from database 298 */ 299 private List<SubInfoRecord> getSubInfo(String selection, Object queryKey) { 300 logd("selection:" + selection + " " + queryKey); 301 String[] selectionArgs = null; 302 if (queryKey != null) { 303 selectionArgs = new String[] {queryKey.toString()}; 304 } 305 ArrayList<SubInfoRecord> subList = null; 306 Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, 307 null, selection, selectionArgs, null); 308 try { 309 if (cursor != null) { 310 while (cursor.moveToNext()) { 311 SubInfoRecord subInfo = getSubInfoRecord(cursor); 312 if (subInfo != null) 313 { 314 if (subList == null) 315 { 316 subList = new ArrayList<SubInfoRecord>(); 317 } 318 subList.add(subInfo); 319 } 320 } 321 } else { 322 logd("Query fail"); 323 } 324 } finally { 325 if (cursor != null) { 326 cursor.close(); 327 } 328 } 329 330 return subList; 331 } 332 333 334 335 /** 336 * Get the SubInfoRecord according to an index 337 * @param subId The unique SubInfoRecord index in database 338 * @return SubInfoRecord, maybe null 339 */ 340 @Override 341 public SubInfoRecord getSubInfoUsingSubId(long subId) { 342 logd("[getSubInfoUsingSubIdx]+ subId:" + subId); 343 if (subId <= 0 || !isSubInfoReady()) { 344 logd("[getSubInfoUsingSubIdx]- subId <= 0 or not ready"); 345 return null; 346 } 347 Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, 348 null, BaseColumns._ID + "=?", new String[] {Long.toString(subId)}, null); 349 try { 350 if (cursor != null) { 351 if (cursor.moveToFirst()) { 352 logd("[getSubInfoUsingSubIdx]- Info detail:"); 353 return getSubInfoRecord(cursor); 354 } 355 } 356 } finally { 357 if (cursor != null) { 358 cursor.close(); 359 } 360 } 361 logd("[getSubInfoUsingSubIdx]- null info return"); 362 363 return null; 364 } 365 366 /** 367 * Get the SubInfoRecord according to an IccId 368 * @param iccId the IccId of SIM card 369 * @return SubInfoRecord, maybe null 370 */ 371 @Override 372 public List<SubInfoRecord> getSubInfoUsingIccId(String iccId) { 373 logd("[getSubInfoUsingIccId]+ iccId:" + iccId); 374 if (iccId == null || !isSubInfoReady()) { 375 logd("[getSubInfoUsingIccId]- null iccid or not ready"); 376 return null; 377 } 378 Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, 379 null, ICC_ID + "=?", new String[] {iccId}, null); 380 ArrayList<SubInfoRecord> subList = null; 381 try { 382 if (cursor != null) { 383 while (cursor.moveToNext()) { 384 SubInfoRecord subInfo = getSubInfoRecord(cursor); 385 if (subInfo != null) 386 { 387 if (subList == null) 388 { 389 subList = new ArrayList<SubInfoRecord>(); 390 } 391 subList.add(subInfo); 392 } 393 } 394 } else { 395 logd("Query fail"); 396 } 397 } finally { 398 if (cursor != null) { 399 cursor.close(); 400 } 401 } 402 403 return subList; 404 } 405 406 /** 407 * Get the SubInfoRecord according to slotId 408 * @param slotId the slot which the SIM is inserted 409 * @return SubInfoRecord, maybe null 410 */ 411 @Override 412 public List<SubInfoRecord> getSubInfoUsingSlotId(int slotId) { 413 return getSubInfoUsingSlotIdWithCheck(slotId, true); 414 } 415 416 /** 417 * Get all the SubInfoRecord(s) in subinfo database 418 * @return Array list of all SubInfoRecords in database, include thsoe that were inserted before 419 */ 420 @Override 421 public List<SubInfoRecord> getAllSubInfoList() { 422 logd("[getAllSubInfoList]+"); 423 List<SubInfoRecord> subList = null; 424 subList = getSubInfo(null, null); 425 if (subList != null) { 426 logd("[getAllSubInfoList]- " + subList.size() + " infos return"); 427 } else { 428 logd("[getAllSubInfoList]- no info return"); 429 } 430 431 return subList; 432 } 433 434 /** 435 * Get the SubInfoRecord(s) of the currently inserted SIM(s) 436 * @return Array list of currently inserted SubInfoRecord(s) 437 */ 438 @Override 439 public List<SubInfoRecord> getActivatedSubInfoList() { 440 logd("[getActivatedSubInfoList]+"); 441 List<SubInfoRecord> subList = null; 442 443 if (!isSubInfoReady()) { 444 logd("[getActivatedSubInfoList] Sub Controller not ready"); 445 return subList; 446 } 447 448 subList = getSubInfo(SIM_ID + "!=" + SIM_NOT_INSERTED, null); 449 if (subList != null) { 450 logd("[getActivatedSubInfoList]- " + subList.size() + " infos return"); 451 } else { 452 logd("[getActivatedSubInfoList]- no info return"); 453 } 454 455 return subList; 456 } 457 458 /** 459 * Get the SUB count of all SUB(s) in subinfo database 460 * @return all SIM count in database, include what was inserted before 461 */ 462 @Override 463 public int getAllSubInfoCount() { 464 logd("[getAllSubInfoCount]+"); 465 Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, 466 null, null, null, null); 467 try { 468 if (cursor != null) { 469 int count = cursor.getCount(); 470 logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB"); 471 return count; 472 } 473 } finally { 474 if (cursor != null) { 475 cursor.close(); 476 } 477 } 478 logd("[getAllSubInfoCount]- no SUB in DB"); 479 480 return 0; 481 } 482 483 /** 484 * Add a new SubInfoRecord to subinfo database if needed 485 * @param iccId the IccId of the SIM card 486 * @param slotId the slot which the SIM is inserted 487 * @return the URL of the newly created row or the updated row 488 */ 489 @Override 490 public int addSubInfoRecord(String iccId, int slotId) { 491 logd("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId); 492 if (iccId == null) { 493 logd("[addSubInfoRecord]- null iccId"); 494 } 495 Uri uri = null; 496 String nameToSet; 497 nameToSet = "SUB 0"+Integer.toString(slotId+1); 498 ContentResolver resolver = mContext.getContentResolver(); 499 Cursor cursor = resolver.query(CONTENT_URI, new String[] {BaseColumns._ID, SIM_ID, NAME_SOURCE}, 500 ICC_ID + "=?", new String[] {iccId}, null); 501 try { 502 if (cursor == null || !cursor.moveToFirst()) { 503 ContentValues value = new ContentValues(); 504 value.put(ICC_ID, iccId); 505 // default SIM color differs between slots 506 value.put(COLOR, slotId); 507 value.put(SIM_ID, slotId); 508 value.put(DISPLAY_NAME, nameToSet); 509 uri = resolver.insert(CONTENT_URI, value); 510 logd("[addSubInfoRecord] New record creating values=" + value); 511 logd("[addSubInfoRecord] New record result uri=" + uri); 512 } else { 513 long subId = cursor.getLong(0); 514 int oldSimInfoId = cursor.getInt(1); 515 int nameSource = cursor.getInt(2); 516 ContentValues value = new ContentValues(); 517 518 if (slotId != oldSimInfoId) { 519 value.put(SIM_ID, slotId); 520 } 521 522 if (nameSource != USER_INPUT) { 523 value.put(DISPLAY_NAME, nameToSet); 524 } 525 526 if (value.size() > 0) { 527 String where = BaseColumns._ID + "=" + Long.toString(subId); 528 logd("[addSubInfoRecord] resolver.update value=" + value + " where=" + where); 529 resolver.update(CONTENT_URI, value, where, 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(CONTENT_URI, 541 null, SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null); 542 if (cursor == null) logd("[addSubInfoRecord] 1 cursor is null"); 543 544 try { 545 if (cursor != null && cursor.moveToFirst()) { 546 do { 547 long subId = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); 548 logd("[addSubInfoRecord] subId=" + subId + " mSimInfo.size" + mSimInfo.size()); 549 // If mSinInfo is already having a valid subId for a slotId/phoneId, 550 // do not add another subId for same slotId/phoneId. 551 long[] sub = getSubId(slotId); 552 553 // FIXME: This is only adding records if there isn't any or sub is returning "dummy" values. 554 // this should probably be updating if the recorddoesn't contain the specific slotId/subId mapping. 555 // But that will be for another time and likely there will be other changes in the mean time. 556 if ((mSimInfo.size() == 0) || (sub != null && sub.length > 0 && sub[0] <= 0)) { 557 // set the first entry as default sub 558 // TODO While two subs active, if user deactivats first 559 // one, need to update the default subId with second 560 // one. 561 if (mSimInfo.size() == 0) { 562 logd("[addSubInfoRecord] call setDefaultSubId subId=" + subId); 563 setDefaultSubId(subId); 564 } 565 mSimInfo.put(slotId, subId); 566 logd("[addSubInfoRecord] mSimInfo.size=" + mSimInfo.size() 567 + " slotId = " + slotId + " subId = " + subId); 568 } else { 569 logd("[addSubInfoRecord] size != 0 && sub[] isn't valid, IGNORE"); 570 } 571 } while (cursor.moveToNext()); 572 } else { 573 logd("[addSubInfoRecord] no records for " + BaseColumns._ID); 574 } 575 } finally { 576 if (cursor != null) { 577 cursor.close(); 578 } 579 } 580 581 int size = mSimInfo.size(); 582 logd("[addSubInfoRecord]- info size=" + size); 583 584 return 1; 585 } 586 587 /** 588 * Set SIM color by simInfo index 589 * @param color the color of the SIM 590 * @param subId the unique SubInfoRecord index in database 591 * @return the number of records updated 592 */ 593 @Override 594 public int setColor(int color, long subId) { 595 logd("[setColor]+ color:" + color + " subId:" + subId); 596 int size = sSimBackgroundDarkRes.length; 597 if (subId <= 0 || color < 0 || color >= size) { 598 logd("[setColor]- fail"); 599 return -1; 600 } 601 ContentValues value = new ContentValues(1); 602 value.put(COLOR, color); 603 logd("[setColor]- color:" + color + " set"); 604 605 int result = mContext.getContentResolver().update(CONTENT_URI, value, 606 BaseColumns._ID + "=" + Long.toString(subId), null); 607 broadcastSimInfoContentChanged(subId, COLOR, color, DEFAULT_STRING_VALUE); 608 609 return result; 610 } 611 612 /** 613 * Set display name by simInfo index 614 * @param displayName the display name of SIM card 615 * @param subId the unique SubInfoRecord index in database 616 * @return the number of records updated 617 */ 618 @Override 619 public int setDisplayName(String displayName, long subId) { 620 return setDisplayNameUsingSrc(displayName, subId, -1); 621 } 622 623 /** 624 * Set display name by simInfo index with name source 625 * @param displayName the display name of SIM card 626 * @param subId the unique SubInfoRecord index in database 627 * @param nameSource 0: DEFAULT_SOURCE, 1: SIM_SOURCE, 2: USER_INPUT 628 * @return the number of records updated 629 */ 630 @Override 631 public int setDisplayNameUsingSrc(String displayName, long subId, long nameSource) { 632 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId + " nameSource:" + nameSource); 633 if (subId <= 0) { 634 logd("[setDisplayName]- fail"); 635 return -1; 636 } 637 String nameToSet; 638 if (displayName == null) { 639 nameToSet = mContext.getString(DEFAULT_NAME_RES); 640 } else { 641 nameToSet = displayName; 642 } 643 ContentValues value = new ContentValues(1); 644 value.put(DISPLAY_NAME, nameToSet); 645 if (nameSource >= DEFAULT_SOURCE) { 646 logd("Set nameSource=" + nameSource); 647 value.put(NAME_SOURCE, nameSource); 648 } 649 logd("[setDisplayName]- mDisplayName:" + nameToSet + " set"); 650 651 int result = mContext.getContentResolver().update(CONTENT_URI, value, 652 BaseColumns._ID + "=" + Long.toString(subId), null); 653 broadcastSimInfoContentChanged(subId, DISPLAY_NAME, DEFAULT_INT_VALUE, nameToSet); 654 655 return result; 656 } 657 658 /** 659 * Set phone number by subId 660 * @param number the phone number of the SIM 661 * @param subId the unique SubInfoRecord index in database 662 * @return the number of records updated 663 */ 664 @Override 665 public int setDispalyNumber(String number, long subId) { 666 logd("[setDispalyNumber]+ number:" + number + " subId:" + subId); 667 if (number == null || subId <= 0) { 668 logd("[setDispalyNumber]- fail"); 669 return -1; 670 } 671 ContentValues value = new ContentValues(1); 672 value.put(NUMBER, number); 673 logd("[setDispalyNumber]- number:" + number + " set"); 674 675 int result = mContext.getContentResolver().update(CONTENT_URI, value, 676 BaseColumns._ID + "=" + Long.toString(subId), null); 677 broadcastSimInfoContentChanged(subId, NUMBER, DEFAULT_INT_VALUE, number); 678 679 return result; 680 } 681 682 /** 683 * Set number display format. 0: none, 1: the first four digits, 2: the last four digits 684 * @param format the display format of phone number 685 * @param subId the unique SubInfoRecord index in database 686 * @return the number of records updated 687 */ 688 @Override 689 public int setDisplayNumberFormat(int format, long subId) { 690 logd("[setDisplayNumberFormat]+ format:" + format + " subId:" + subId); 691 if (format < 0 || subId <= 0) { 692 logd("[setDisplayNumberFormat]- fail, return -1"); 693 return -1; 694 } 695 ContentValues value = new ContentValues(1); 696 value.put(DISPLAY_NUMBER_FORMAT, format); 697 logd("[setDisplayNumberFormat]- format:" + format + " set"); 698 699 int result = mContext.getContentResolver().update(CONTENT_URI, value, 700 BaseColumns._ID + "=" + Long.toString(subId), null); 701 broadcastSimInfoContentChanged(subId, DISPLAY_NUMBER_FORMAT, format, DEFAULT_STRING_VALUE); 702 703 return result; 704 } 705 706 /** 707 * Set data roaming by simInfo index 708 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 709 * @param subId the unique SubInfoRecord index in database 710 * @return the number of records updated 711 */ 712 @Override 713 public int setDataRoaming(int roaming, long subId) { 714 logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 715 if (roaming < 0 || subId <= 0) { 716 logd("[setDataRoaming]- fail"); 717 return -1; 718 } 719 ContentValues value = new ContentValues(1); 720 value.put(DATA_ROAMING, roaming); 721 logd("[setDataRoaming]- roaming:" + roaming + " set"); 722 723 int result = mContext.getContentResolver().update(CONTENT_URI, value, 724 BaseColumns._ID + "=" + Long.toString(subId), null); 725 broadcastSimInfoContentChanged(subId, DATA_ROAMING, roaming, DEFAULT_STRING_VALUE); 726 727 return result; 728 } 729 730 @Override 731 public int getSlotId(long subId) { 732 logd("[getSlotId]+ subId:" + subId); 733 if (subId <= 0) { 734 logd("[getSlotId]- subId <= 0"); 735 return SIM_NOT_INSERTED; 736 } 737 738 int size = mSimInfo.size(); 739 logd("[getSlotId]- info size="+size); 740 741 if (size == 0) 742 { 743 return SIM_NOT_INSERTED; 744 } 745 746 for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { 747 int sim = entry.getKey(); 748 long sub = entry.getValue(); 749 logd("[getSlotId]- entry, sim ="+sim+", sub="+ sub); 750 751 if (subId == sub) 752 { 753 logd("[getSlotId]- return ="+sim); 754 return sim; 755 } 756 } 757 758 logd("[getSlotId]- return fail"); 759 return (int)(subId-1); 760 761 } 762 763 /** 764 * Return the subId for specified sim Id. 765 * @deprecated 766 */ 767 @Override 768 @Deprecated 769 public long[] getSubId(int slotId) { 770 if (VDBG) logd("[getSubId]+ slotId:" + slotId); 771 772 // FIXME this should return the subIds associated with the PhoneIds 773 // using {1, 2} for now, to workaround problem with data classes 774 // not being able to find a subscription that has value set to 775 // default data during bootup, since SubscriptionController has not 776 // discovered all the subs when this query is made. 777 778 long[] subId = new long[] {-1-slotId, -1-slotId}; 779 780 if (slotId < 0) { 781 logd("[getSubId]- slotId < 0, return dummy instead"); 782 return subId; 783 } 784 785 int size = mSimInfo.size(); 786 if (VDBG) logd("[getSubId]- info size="+size); 787 788 if (size == 0) 789 { 790 logd("[getSubId]- size == 0, return dummy instead"); 791 return subId; 792 } 793 794 int subIdx = 0; 795 796 for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { 797 int sim = entry.getKey(); 798 long sub = entry.getValue(); 799 800 if (VDBG) logd("[getSubId]- entry, sim ="+sim+", sub="+ sub); 801 if (slotId == sim) 802 { 803 subId[subIdx] = sub; 804 if (VDBG) logd("[getSubId]- subId["+subIdx+"] = "+subId[subIdx]); 805 subIdx++; 806 } 807 } 808 809 if (VDBG) logd("[getSubId]-, subId = "+subId[0]); 810 return subId; 811 } 812 813 @Override 814 public int getPhoneId(long subId) { 815 if (VDBG) logd("[getPhoneId]+ subId:" + subId); 816 if (subId <= 0) { 817 // FIXME Do not auto map subId to phoneId 818 // May be we shoud add dummy subId's during 819 // initialization of subscription controller ?? 820 if (subId == -1) { 821 logd("[getPhoneId]- subId == -1 return =" + 0); 822 return 0; 823 } else if (subId == -2) { 824 logd("[getPhoneId]- subId == -2 return =" + 1); 825 return 1; 826 } 827 } 828 829 int size = mSimInfo.size(); 830 831 if (size == 0 || subId == SubscriptionManager.DEFAULT_SUB_ID) { 832 if (VDBG) logd("[getPhoneId]- subId == DEFAULT_SUB_ID returning defaultPhoneId=" + 833 mDefaultPhoneId); 834 return mDefaultPhoneId; 835 } 836 837 for (Entry<Integer, Long> entry: mSimInfo.entrySet()) { 838 int sim = entry.getKey(); 839 long sub = entry.getValue(); 840 if (VDBG) logd("[getPhoneId]- entry, sim="+sim+", sub="+ sub); 841 842 if (subId == sub) 843 { 844 if (VDBG) logd("[getPhoneId]- return="+sim); 845 return sim; 846 } 847 } 848 849 if (VDBG) logd("[getPhoneId]- return=" + (int)(subId-1)); 850 return (int)(subId-1); 851 852 } 853 854 @Override 855 public int clearSubInfo() 856 { 857 if (VDBG) logd("[clearSubInfo]+"); 858 859 int size = mSimInfo.size(); 860 if (VDBG) logd("[getSubId]- info size="+size); 861 862 if (size == 0) 863 { 864 return 0; 865 } 866 867 mSimInfo.clear(); 868 if (VDBG) logd("[clearSubInfo]-"); 869 return 0; 870 871 } 872 873 private static int[] setSimResource(int type) { 874 int[] simResource = null; 875 876 switch (type) { 877 case RES_TYPE_BACKGROUND_DARK: 878 simResource = new int[] { 879 com.android.internal.R.drawable.sim_dark_blue, 880 com.android.internal.R.drawable.sim_dark_orange, 881 com.android.internal.R.drawable.sim_dark_green, 882 com.android.internal.R.drawable.sim_dark_purple 883 }; 884 break; 885 case RES_TYPE_BACKGROUND_LIGHT: 886 simResource = new int[] { 887 com.android.internal.R.drawable.sim_light_blue, 888 com.android.internal.R.drawable.sim_light_orange, 889 com.android.internal.R.drawable.sim_light_green, 890 com.android.internal.R.drawable.sim_light_purple 891 }; 892 break; 893 } 894 895 return simResource; 896 } 897 898 private void logd(String msg) { 899 Rlog.d(LOG_TAG, "[SubController]" + msg); 900 } 901 902 private void loge(String msg) { 903 Rlog.e(LOG_TAG, "[SubController]" + msg); 904 } 905 906 @Override 907 public long getDefaultSubId() { 908 if (VDBG) logd("getDefaultSubId: value=" + mDefaultVoiceSubId); 909 return mDefaultVoiceSubId; 910 } 911 912 @Override 913 public void setDefaultVoiceSubId(long subId) { 914 Settings.Global.putLong(mContext.getContentResolver(), 915 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, subId); 916 broadcastDefaultVoiceSubIdChanged(subId); 917 if (VDBG) logd("setDefaultVoiceSubId, subId=" + subId); 918 } 919 920 private static void broadcastDefaultVoiceSubIdChanged(long subId) { 921 // Broadcast an Intent for default voice sub change 922 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); 923 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 924 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 925 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 926 } 927 928 @Override 929 public long getDefaultVoiceSubId() { 930 long subId = SubscriptionManager.INVALID_SUB_ID; 931 932 try { 933 subId = Settings.Global.getLong(mContext.getContentResolver(), 934 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION); 935 } catch (SettingNotFoundException snfe) { 936 loge("Settings Exception Reading Dual Sim Voice Call Values"); 937 } 938 939 if (VDBG) logd("getDefaultVoiceSubId, value = " + subId); 940 return subId; 941 } 942 943 @Override 944 public long getDefaultDataSubId() { 945 long subId = SubscriptionManager.INVALID_SUB_ID; 946 try { 947 subId = Settings.Global.getLong(mContext.getContentResolver(), 948 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION); 949 } catch (SettingNotFoundException snfe) { 950 loge("Settings Exception Reading Dual Sim Data Call Values"); 951 } 952 953 return subId; 954 } 955 956 public void setDefaultDataSubId(long subId) { 957 loge("setDataSubId: subId=" + subId + " FIXME NOP right now"); 958 DctController dctController = DctController.getInstance(); 959 dctController.setDataSubId(subId); 960 dctController.registerForDataSwitchInfo(mDataConnectionHandler, 961 EVENT_SET_DEFAULT_DATA_DONE, null); 962 } 963 964 private void updateDataSubId(long subId) { 965 if (VDBG) logd(" updateDataSubId, subId=" + subId); 966 Settings.Global.putLong(mContext.getContentResolver(), 967 Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION, subId); 968 broadcastDefaultDataSubIdChanged(subId); 969 } 970 971 private static void broadcastDefaultDataSubIdChanged(long subId) { 972 // Broadcast an Intent for default data sub change 973 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); 974 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 975 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 976 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 977 } 978 979 /* Sets the default subscription. If only one sub is active that 980 * sub is set as default subId. If two or more sub's are active 981 * the first sub is set as default subscription 982 */ 983 // FIXME Modify/rename this method name as part of 984 // refactoring other subscription changes 985 public void setDefaultSubId(long subId) { 986 if (subId > 0 && subId != SubscriptionManager.INVALID_SUB_ID 987 && subId != SubscriptionManager.DEFAULT_SUB_ID) { 988 int phoneId = getPhoneId(subId); 989 if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount()) { 990 mDefaultVoiceSubId = subId; // returned by getFirstActiveSubId() 991 if (VDBG) logd("setDefaultSubId: mDefaultVoiceSubId=subId=" + subId); 992 // Update MCC MNC device configuration information 993 String defaultMccMnc = TelephonyManager.getDefault().getSimOperator(phoneId); 994 if (VDBG) logd("setDefaultSubId: call update mccmnc=" + defaultMccMnc); 995 MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false); 996 997 // Broadcast an Intent for default sub change 998 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED); 999 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 1000 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId); 1001 if (VDBG) { 1002 logd("setDefaultSubId: broadcast default subId changed phoneId=" + phoneId 1003 + " subId=" + subId); 1004 } 1005 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1006 } else { 1007 if (VDBG) { 1008 logd("setDefaultSubId: not set invalid phoneId=" + phoneId + " subId=" + subId); 1009 } 1010 } 1011 } else { 1012 if (VDBG) logd("setDefaultSubId: not set invalid subId=" + subId); 1013 } 1014 } 1015 1016 // FIXME SubscriptionController should register (perhaps using Registrants) 1017 // with SimInfoUpdater for any updates to the subscriptions. When it is 1018 // notified of any updates, this API should be called. Rather than 1019 // SimInfoUpdater calling into updateDefaultSubId. 1020 public void updateDefaultSubId() { 1021 if (!isSubActive(getDefaultDataSubId())) { 1022 updateDataSubId(getFirstActiveSubId()); 1023 } 1024 1025 if (!isSubActive(getDefaultVoiceSubId())) { 1026 setDefaultVoiceSubId(getFirstActiveSubId()); 1027 } 1028 } 1029 1030 // FIXME As part of getDefaultSubId method cleanup, modify 1031 // the mDefaultVoiceSubId to mFirstActiveSubId. 1032 private long getFirstActiveSubId() { 1033 if (VDBG) logd("getFirstActiveSubId, value = " + mDefaultVoiceSubId); 1034 return mDefaultVoiceSubId; 1035 } 1036 1037 private boolean isSubActive(long subId) { 1038 boolean subActive = false; 1039 List<SubInfoRecord> activeSubList = getActivatedSubInfoList(); 1040 1041 if (activeSubList != null) { 1042 for (SubInfoRecord subInfoRecord : activeSubList) { 1043 if (subInfoRecord.mSubId == subId) { 1044 if (VDBG) logd("isSubActive, found active sub " + subId); 1045 subActive = true; 1046 break; 1047 } 1048 } 1049 } 1050 return subActive; 1051 } 1052 1053 private class DataConnectionHandler extends Handler { 1054 @Override 1055 public void handleMessage(Message msg) { 1056 AsyncResult ar = (AsyncResult) msg.obj; 1057 switch (msg.what) { 1058 case EVENT_SET_DEFAULT_DATA_DONE: 1059 Long subId = (Long)ar.result; 1060 if (VDBG) logd("EVENT_SET_DEFAULT_DATA_DONE subId:" + subId); 1061 updateDataSubId(subId); 1062 break; 1063 } 1064 } 1065 } 1066 1067 /* This should return long and not long [] since each phone has 1068 * exactly 1 sub id for now, it could return the 0th element 1069 * returned from getSubId() 1070 */ 1071 // FIXME will design a mechanism to manage the relationship between PhoneId/SlotId/SubId 1072 // since phoneId = SlotId is not always true 1073 public long getSubIdUsingPhoneId(int phoneId) { 1074 long[] subId = getSubId(phoneId); 1075 return subId[0]; 1076 } 1077 1078 public long[] getSubIdUsingSlotId(int slotId) { 1079 return getSubId(slotId); 1080 } 1081 1082 public List<SubInfoRecord> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) { 1083 if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId=" + slotId); 1084 if (slotId < 0 ) { 1085 if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]- return null, slotId < 0"); 1086 return null; 1087 } 1088 1089 if (needCheck && !isSubInfoReady()) { 1090 if (VDBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready return null"); 1091 return null; 1092 } 1093 1094 Cursor cursor = mContext.getContentResolver().query(CONTENT_URI, 1095 null, SIM_ID + "=?", new String[] {String.valueOf(slotId)}, null); 1096 ArrayList<SubInfoRecord> subList = null; 1097 try { 1098 if (cursor != null) { 1099 while (cursor.moveToNext()) { 1100 SubInfoRecord subInfo = getSubInfoRecord(cursor); 1101 if (subInfo != null) 1102 { 1103 if (subList == null) 1104 { 1105 subList = new ArrayList<SubInfoRecord>(); 1106 } 1107 subList.add(subInfo); 1108 } 1109 } 1110 } 1111 } finally { 1112 if (cursor != null) { 1113 cursor.close(); 1114 } 1115 } 1116 if (VDBG) logd("[getSubInfoUsingSlotId]- subList=" + subList); 1117 return subList; 1118 1119 } 1120} 1121