SubscriptionManager.java revision e1da5a3964f9d5487011ae73082053d172cf5dec
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.telephony; 18 19import android.annotation.NonNull; 20import android.annotation.SdkConstant; 21import android.annotation.SdkConstant.SdkConstantType; 22import android.content.Context; 23import android.content.Intent; 24import android.content.res.Configuration; 25import android.content.res.Resources; 26import android.net.Uri; 27import android.telephony.Rlog; 28import android.os.Handler; 29import android.os.Message; 30import android.os.ServiceManager; 31import android.os.RemoteException; 32import android.util.DisplayMetrics; 33 34import com.android.internal.telephony.ISub; 35import com.android.internal.telephony.IOnSubscriptionsChangedListener; 36import com.android.internal.telephony.ITelephonyRegistry; 37import com.android.internal.telephony.PhoneConstants; 38import java.util.ArrayList; 39import java.util.List; 40 41/** 42 * SubscriptionManager is the application interface to SubscriptionController 43 * and provides information about the current Telephony Subscriptions. 44 * * <p> 45 * You do not instantiate this class directly; instead, you retrieve 46 * a reference to an instance through {@link #from}. 47 * <p> 48 * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE. 49 */ 50public class SubscriptionManager { 51 private static final String LOG_TAG = "SubscriptionManager"; 52 private static final boolean DBG = false; 53 private static final boolean VDBG = false; 54 55 /** An invalid subscription identifier */ 56 public static final int INVALID_SUBSCRIPTION_ID = -1; 57 58 /** Base value for Dummy SUBSCRIPTION_ID's. */ 59 /** FIXME: Remove DummySubId's, but for now have them map just below INVALID_SUBSCRIPTION_ID 60 /** @hide */ 61 public static final int DUMMY_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1; 62 63 /** An invalid phone identifier */ 64 /** @hide */ 65 public static final int INVALID_PHONE_INDEX = -1; 66 67 /** An invalid slot identifier */ 68 /** @hide */ 69 public static final int INVALID_SIM_SLOT_INDEX = -1; 70 71 /** Indicates the caller wants the default sub id. */ 72 /** @hide */ 73 public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE; 74 75 /** 76 * Indicates the caller wants the default phone id. 77 * Used in SubscriptionController and Phone but do we really need it??? 78 * @hide 79 */ 80 public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE; 81 82 /** Indicates the caller wants the default slot id. NOT used remove? */ 83 /** @hide */ 84 public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE; 85 86 /** Minimum possible subid that represents a subscription */ 87 /** @hide */ 88 public static final int MIN_SUBSCRIPTION_ID_VALUE = 0; 89 90 /** Maximum possible subid that represents a subscription */ 91 /** @hide */ 92 public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1; 93 94 /** @hide */ 95 public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo"); 96 97 /** 98 * TelephonyProvider unique key column name is the subscription id. 99 * <P>Type: TEXT (String)</P> 100 */ 101 /** @hide */ 102 public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; 103 104 /** 105 * TelephonyProvider column name for SIM ICC Identifier 106 * <P>Type: TEXT (String)</P> 107 */ 108 /** @hide */ 109 public static final String ICC_ID = "icc_id"; 110 111 /** 112 * TelephonyProvider column name for user SIM_SlOT_INDEX 113 * <P>Type: INTEGER (int)</P> 114 */ 115 /** @hide */ 116 public static final String SIM_SLOT_INDEX = "sim_id"; 117 118 /** SIM is not inserted */ 119 /** @hide */ 120 public static final int SIM_NOT_INSERTED = -1; 121 122 /** 123 * TelephonyProvider column name for user displayed name. 124 * <P>Type: TEXT (String)</P> 125 */ 126 /** @hide */ 127 public static final String DISPLAY_NAME = "display_name"; 128 129 /** 130 * TelephonyProvider column name for the service provider name for the SIM. 131 * <P>Type: TEXT (String)</P> 132 */ 133 /** @hide */ 134 public static final String CARRIER_NAME = "carrier_name"; 135 136 /** 137 * Default name resource 138 * @hide 139 */ 140 public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName; 141 142 /** 143 * TelephonyProvider column name for source of the user displayed name. 144 * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below 145 * 146 * @hide 147 */ 148 public static final String NAME_SOURCE = "name_source"; 149 150 /** 151 * The name_source is undefined 152 * @hide 153 */ 154 public static final int NAME_SOURCE_UNDEFINDED = -1; 155 156 /** 157 * The name_source is the default 158 * @hide 159 */ 160 public static final int NAME_SOURCE_DEFAULT_SOURCE = 0; 161 162 /** 163 * The name_source is from the SIM 164 * @hide 165 */ 166 public static final int NAME_SOURCE_SIM_SOURCE = 1; 167 168 /** 169 * The name_source is from the user 170 * @hide 171 */ 172 public static final int NAME_SOURCE_USER_INPUT = 2; 173 174 /** 175 * TelephonyProvider column name for the color of a SIM. 176 * <P>Type: INTEGER (int)</P> 177 */ 178 /** @hide */ 179 public static final String COLOR = "color"; 180 181 /** @hide */ 182 public static final int COLOR_1 = 0; 183 184 /** @hide */ 185 public static final int COLOR_2 = 1; 186 187 /** @hide */ 188 public static final int COLOR_3 = 2; 189 190 /** @hide */ 191 public static final int COLOR_4 = 3; 192 193 /** @hide */ 194 public static final int COLOR_DEFAULT = COLOR_1; 195 196 /** 197 * TelephonyProvider column name for the phone number of a SIM. 198 * <P>Type: TEXT (String)</P> 199 */ 200 /** @hide */ 201 public static final String NUMBER = "number"; 202 203 /** 204 * TelephonyProvider column name for the number display format of a SIM. 205 * <P>Type: INTEGER (int)</P> 206 */ 207 /** @hide */ 208 public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; 209 210 /** @hide */ 211 public static final int DISPLAY_NUMBER_NONE = 0; 212 213 /** @hide */ 214 public static final int DISPLAY_NUMBER_FIRST = 1; 215 216 /** @hide */ 217 public static final int DISPLAY_NUMBER_LAST = 2; 218 219 /** @hide */ 220 public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST; 221 222 /** 223 * TelephonyProvider column name for permission for data roaming of a SIM. 224 * <P>Type: INTEGER (int)</P> 225 */ 226 /** @hide */ 227 public static final String DATA_ROAMING = "data_roaming"; 228 229 /** Indicates that data roaming is enabled for a subscription */ 230 public static final int DATA_ROAMING_ENABLE = 1; 231 232 /** Indicates that data roaming is disabled for a subscription */ 233 public static final int DATA_ROAMING_DISABLE = 0; 234 235 /** @hide */ 236 public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; 237 238 /** 239 * TelephonyProvider column name for the MCC associated with a SIM. 240 * <P>Type: INTEGER (int)</P> 241 * @hide 242 */ 243 public static final String MCC = "mcc"; 244 245 /** 246 * TelephonyProvider column name for the MNC associated with a SIM. 247 * <P>Type: INTEGER (int)</P> 248 * @hide 249 */ 250 public static final String MNC = "mnc"; 251 252 /** 253 * TelephonyProvider column name for extreme threat in CB settings 254 * @hide 255 */ 256 public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; 257 258 /** 259 * TelephonyProvider column name for severe threat in CB settings 260 *@hide 261 */ 262 public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; 263 264 /** 265 * TelephonyProvider column name for amber alert in CB settings 266 *@hide 267 */ 268 public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; 269 270 /** 271 * TelephonyProvider column name for emergency alert in CB settings 272 *@hide 273 */ 274 public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; 275 276 /** 277 * TelephonyProvider column name for alert sound duration in CB settings 278 *@hide 279 */ 280 public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; 281 282 /** 283 * TelephonyProvider column name for alert reminder interval in CB settings 284 *@hide 285 */ 286 public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; 287 288 /** 289 * TelephonyProvider column name for enabling vibrate in CB settings 290 *@hide 291 */ 292 public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; 293 294 /** 295 * TelephonyProvider column name for enabling alert speech in CB settings 296 *@hide 297 */ 298 public static final String CB_ALERT_SPEECH = "enable_alert_speech"; 299 300 /** 301 * TelephonyProvider column name for ETWS test alert in CB settings 302 *@hide 303 */ 304 public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; 305 306 /** 307 * TelephonyProvider column name for enable channel50 alert in CB settings 308 *@hide 309 */ 310 public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; 311 312 /** 313 * TelephonyProvider column name for CMAS test alert in CB settings 314 *@hide 315 */ 316 public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts"; 317 318 /** 319 * TelephonyProvider column name for Opt out dialog in CB settings 320 *@hide 321 */ 322 public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; 323 324 /** 325 * Broadcast Action: The user has changed one of the default subs related to 326 * data, phone calls, or sms</p> 327 * 328 * TODO: Change to a listener 329 * @hide 330 */ 331 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 332 public static final String SUB_DEFAULT_CHANGED_ACTION = 333 "android.intent.action.SUB_DEFAULT_CHANGED"; 334 335 private final Context mContext; 336 337 /** 338 * A listener class for monitoring changes to {@link SubscriptionInfo} records. 339 * <p> 340 * Override the onSubscriptionsChanged method in the object that extends this 341 * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)} 342 * to register your listener and to unregister invoke 343 * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)} 344 * <p> 345 * Permissions android.Manifest.permission.READ_PHONE_STATE is required 346 * for #onSubscriptionsChanged to be invoked. 347 */ 348 public static class OnSubscriptionsChangedListener { 349 private final Handler mHandler = new Handler() { 350 @Override 351 public void handleMessage(Message msg) { 352 if (DBG) { 353 log("handleMessage: invoke the overriden onSubscriptionsChanged()"); 354 } 355 OnSubscriptionsChangedListener.this.onSubscriptionsChanged(); 356 } 357 }; 358 359 /** 360 * Callback invoked when there is any change to any SubscriptionInfo. Typically 361 * this method would invoke {@link #getActiveSubscriptionInfoList} 362 */ 363 public void onSubscriptionsChanged() { 364 if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN"); 365 } 366 367 /** 368 * The callback methods need to be called on the handler thread where 369 * this object was created. If the binder did that for us it'd be nice. 370 */ 371 IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() { 372 @Override 373 public void onSubscriptionsChanged() { 374 if (DBG) log("callback: received, sendEmptyMessage(0) to handler"); 375 mHandler.sendEmptyMessage(0); 376 } 377 }; 378 379 private void log(String s) { 380 Rlog.d(LOG_TAG, s); 381 } 382 } 383 384 /** @hide */ 385 public SubscriptionManager(Context context) { 386 if (DBG) logd("SubscriptionManager created"); 387 mContext = context; 388 } 389 390 /** 391 * Get an instance of the SubscriptionManager from the Context. 392 * This invokes {@link android.content.Context#getSystemService 393 * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}. 394 * 395 * @param context to use. 396 * @return SubscriptionManager instance 397 */ 398 public static SubscriptionManager from(Context context) { 399 return (SubscriptionManager) context.getSystemService( 400 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 401 } 402 403 /** 404 * Register for changes to the list of active {@link SubscriptionInfo} records or to the 405 * individual records themselves. When a change occurs the onSubscriptionsChanged method of 406 * the listener will be invoked immediately if there has been a notification. 407 * 408 * @param listener an instance of {@link OnSubscriptionsChangedListener} with 409 * onSubscriptionsChanged overridden. 410 */ 411 public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 412 String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; 413 if (DBG) { 414 logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug 415 + " listener=" + listener); 416 } 417 try { 418 // We use the TelephonyRegistry as it runs in the system and thus is always 419 // available. Where as SubscriptionController could crash and not be available 420 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( 421 "telephony.registry")); 422 if (tr != null) { 423 tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback); 424 } 425 } catch (RemoteException ex) { 426 // Should not happen 427 } 428 } 429 430 /** 431 * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary 432 * as the listener will automatically be unregistered if an attempt to invoke the listener 433 * fails. 434 * 435 * @param listener that is to be unregistered. 436 */ 437 public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 438 String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; 439 if (DBG) { 440 logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug 441 + " listener=" + listener); 442 } 443 try { 444 // We use the TelephonyRegistry as its runs in the system and thus is always 445 // available where as SubscriptionController could crash and not be available 446 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( 447 "telephony.registry")); 448 if (tr != null) { 449 tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback); 450 } 451 } catch (RemoteException ex) { 452 // Should not happen 453 } 454 } 455 456 /** 457 * Get the active SubscriptionInfo with the input subId. 458 * 459 * @param subId The unique SubscriptionInfo key in database. 460 * @return SubscriptionInfo, maybe null if its not active. 461 */ 462 public SubscriptionInfo getActiveSubscriptionInfo(int subId) { 463 if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId); 464 if (!isValidSubscriptionId(subId)) { 465 if (DBG) { 466 logd("[getActiveSubscriptionInfo]- invalid subId"); 467 } 468 return null; 469 } 470 471 SubscriptionInfo subInfo = null; 472 473 try { 474 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 475 if (iSub != null) { 476 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName()); 477 } 478 } catch (RemoteException ex) { 479 // ignore it 480 } 481 482 return subInfo; 483 484 } 485 486 /** 487 * Get the active SubscriptionInfo associated with the iccId 488 * @param iccId the IccId of SIM card 489 * @return SubscriptionInfo, maybe null if its not active 490 * @hide 491 */ 492 public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) { 493 if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId); 494 if (iccId == null) { 495 logd("[getActiveSubscriptionInfoForIccIndex]- null iccid"); 496 return null; 497 } 498 499 SubscriptionInfo result = null; 500 501 try { 502 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 503 if (iSub != null) { 504 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName()); 505 } 506 } catch (RemoteException ex) { 507 // ignore it 508 } 509 510 return result; 511 } 512 513 /** 514 * Get the active SubscriptionInfo associated with the slotIdx 515 * @param slotIdx the slot which the subscription is inserted 516 * @return SubscriptionInfo, maybe null if its not active 517 */ 518 public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx) { 519 if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx); 520 if (!isValidSlotId(slotIdx)) { 521 logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIdx"); 522 return null; 523 } 524 525 SubscriptionInfo result = null; 526 527 try { 528 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 529 if (iSub != null) { 530 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIdx, 531 mContext.getOpPackageName()); 532 } 533 } catch (RemoteException ex) { 534 // ignore it 535 } 536 537 return result; 538 } 539 540 /** 541 * @return List of all SubscriptionInfo records in database, 542 * include those that were inserted before, maybe empty but not null. 543 * @hide 544 */ 545 public List<SubscriptionInfo> getAllSubscriptionInfoList() { 546 if (VDBG) logd("[getAllSubscriptionInfoList]+"); 547 548 List<SubscriptionInfo> result = null; 549 550 try { 551 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 552 if (iSub != null) { 553 result = iSub.getAllSubInfoList(mContext.getOpPackageName()); 554 } 555 } catch (RemoteException ex) { 556 // ignore it 557 } 558 559 if (result == null) { 560 result = new ArrayList<SubscriptionInfo>(); 561 } 562 return result; 563 } 564 565 /** 566 * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted 567 * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}. 568 * 569 * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device. 570 * <ul> 571 * <li> 572 * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener} 573 * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be 574 * invoked in the future. 575 * </li> 576 * <li> 577 * If the list is empty then there are no {@link SubscriptionInfo} records currently available. 578 * </li> 579 * <li> 580 * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex} 581 * then by {@link SubscriptionInfo#getSubscriptionId}. 582 * </li> 583 * </ul> 584 */ 585 public List<SubscriptionInfo> getActiveSubscriptionInfoList() { 586 List<SubscriptionInfo> result = null; 587 588 try { 589 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 590 if (iSub != null) { 591 result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName()); 592 } 593 } catch (RemoteException ex) { 594 // ignore it 595 } 596 return result; 597 } 598 599 /** 600 * @return the count of all subscriptions in the database, this includes 601 * all subscriptions that have been seen. 602 * @hide 603 */ 604 public int getAllSubscriptionInfoCount() { 605 if (VDBG) logd("[getAllSubscriptionInfoCount]+"); 606 607 int result = 0; 608 609 try { 610 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 611 if (iSub != null) { 612 result = iSub.getAllSubInfoCount(mContext.getOpPackageName()); 613 } 614 } catch (RemoteException ex) { 615 // ignore it 616 } 617 618 return result; 619 } 620 621 /** 622 * @return the current number of active subscriptions. There is no guarantee the value 623 * returned by this method will be the same as the length of the list returned by 624 * {@link #getActiveSubscriptionInfoList}. 625 */ 626 public int getActiveSubscriptionInfoCount() { 627 int result = 0; 628 629 try { 630 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 631 if (iSub != null) { 632 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName()); 633 } 634 } catch (RemoteException ex) { 635 // ignore it 636 } 637 638 return result; 639 } 640 641 /** 642 * @return the maximum number of active subscriptions that will be returned by 643 * {@link #getActiveSubscriptionInfoList} and the value returned by 644 * {@link #getActiveSubscriptionInfoCount}. 645 */ 646 public int getActiveSubscriptionInfoCountMax() { 647 int result = 0; 648 649 try { 650 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 651 if (iSub != null) { 652 result = iSub.getActiveSubInfoCountMax(); 653 } 654 } catch (RemoteException ex) { 655 // ignore it 656 } 657 658 return result; 659 } 660 661 /** 662 * Add a new SubscriptionInfo to SubscriptionInfo database if needed 663 * @param iccId the IccId of the SIM card 664 * @param slotId the slot which the SIM is inserted 665 * @return the URL of the newly created row or the updated row 666 * @hide 667 */ 668 public Uri addSubscriptionInfoRecord(String iccId, int slotId) { 669 if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotId:" + slotId); 670 if (iccId == null) { 671 logd("[addSubscriptionInfoRecord]- null iccId"); 672 } 673 if (!isValidSlotId(slotId)) { 674 logd("[addSubscriptionInfoRecord]- invalid slotId"); 675 } 676 677 try { 678 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 679 if (iSub != null) { 680 // FIXME: This returns 1 on success, 0 on error should should we return it? 681 iSub.addSubInfoRecord(iccId, slotId); 682 } 683 } catch (RemoteException ex) { 684 // ignore it 685 } 686 687 // FIXME: Always returns null? 688 return null; 689 690 } 691 692 /** 693 * Set SIM icon tint color by simInfo index 694 * @param tint the RGB value of icon tint color of the SIM 695 * @param subId the unique SubInfoRecord index in database 696 * @return the number of records updated 697 * @hide 698 */ 699 public int setIconTint(int tint, int subId) { 700 if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); 701 if (!isValidSubscriptionId(subId)) { 702 logd("[setIconTint]- fail"); 703 return -1; 704 } 705 706 int result = 0; 707 708 try { 709 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 710 if (iSub != null) { 711 result = iSub.setIconTint(tint, subId); 712 } 713 } catch (RemoteException ex) { 714 // ignore it 715 } 716 717 return result; 718 719 } 720 721 /** 722 * Set display name by simInfo index 723 * @param displayName the display name of SIM card 724 * @param subId the unique SubscriptionInfo index in database 725 * @return the number of records updated 726 * @hide 727 */ 728 public int setDisplayName(String displayName, int subId) { 729 return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED); 730 } 731 732 /** 733 * Set display name by simInfo index with name source 734 * @param displayName the display name of SIM card 735 * @param subId the unique SubscriptionInfo index in database 736 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 737 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED 738 * @return the number of records updated or < 0 if invalid subId 739 * @hide 740 */ 741 public int setDisplayName(String displayName, int subId, long nameSource) { 742 if (VDBG) { 743 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId 744 + " nameSource:" + nameSource); 745 } 746 if (!isValidSubscriptionId(subId)) { 747 logd("[setDisplayName]- fail"); 748 return -1; 749 } 750 751 int result = 0; 752 753 try { 754 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 755 if (iSub != null) { 756 result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource); 757 } 758 } catch (RemoteException ex) { 759 // ignore it 760 } 761 762 return result; 763 764 } 765 766 /** 767 * Set phone number by subId 768 * @param number the phone number of the SIM 769 * @param subId the unique SubscriptionInfo index in database 770 * @return the number of records updated 771 * @hide 772 */ 773 public int setDisplayNumber(String number, int subId) { 774 if (number == null || !isValidSubscriptionId(subId)) { 775 logd("[setDisplayNumber]- fail"); 776 return -1; 777 } 778 779 int result = 0; 780 781 try { 782 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 783 if (iSub != null) { 784 result = iSub.setDisplayNumber(number, subId); 785 } 786 } catch (RemoteException ex) { 787 // ignore it 788 } 789 790 return result; 791 792 } 793 794 /** 795 * Set data roaming by simInfo index 796 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 797 * @param subId the unique SubscriptionInfo index in database 798 * @return the number of records updated 799 * @hide 800 */ 801 public int setDataRoaming(int roaming, int subId) { 802 if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 803 if (roaming < 0 || !isValidSubscriptionId(subId)) { 804 logd("[setDataRoaming]- fail"); 805 return -1; 806 } 807 808 int result = 0; 809 810 try { 811 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 812 if (iSub != null) { 813 result = iSub.setDataRoaming(roaming, subId); 814 } 815 } catch (RemoteException ex) { 816 // ignore it 817 } 818 819 return result; 820 } 821 822 /** 823 * Get slotId associated with the subscription. 824 * @return slotId as a positive integer or a negative value if an error either 825 * SIM_NOT_INSERTED or < 0 if an invalid slot index 826 * @hide 827 */ 828 public static int getSlotId(int subId) { 829 if (!isValidSubscriptionId(subId)) { 830 if (DBG) { 831 logd("[getSlotId]- fail"); 832 } 833 } 834 835 int result = INVALID_SIM_SLOT_INDEX; 836 837 try { 838 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 839 if (iSub != null) { 840 result = iSub.getSlotId(subId); 841 } 842 } catch (RemoteException ex) { 843 // ignore it 844 } 845 846 return result; 847 848 } 849 850 /** @hide */ 851 public static int[] getSubId(int slotId) { 852 if (!isValidSlotId(slotId)) { 853 logd("[getSubId]- fail"); 854 return null; 855 } 856 857 int[] subId = null; 858 859 try { 860 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 861 if (iSub != null) { 862 subId = iSub.getSubId(slotId); 863 } 864 } catch (RemoteException ex) { 865 // ignore it 866 } 867 868 return subId; 869 } 870 871 /** @hide */ 872 public static int getPhoneId(int subId) { 873 if (!isValidSubscriptionId(subId)) { 874 if (DBG) { 875 logd("[getPhoneId]- fail"); 876 } 877 return INVALID_PHONE_INDEX; 878 } 879 880 int result = INVALID_PHONE_INDEX; 881 882 try { 883 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 884 if (iSub != null) { 885 result = iSub.getPhoneId(subId); 886 } 887 } catch (RemoteException ex) { 888 // ignore it 889 } 890 891 if (VDBG) logd("[getPhoneId]- phoneId=" + result); 892 return result; 893 894 } 895 896 private static void logd(String msg) { 897 Rlog.d(LOG_TAG, msg); 898 } 899 900 /** 901 * Returns the system's default subscription id. 902 * 903 * For a voice capable device, it will return getDefaultVoiceSubscriptionId. 904 * For a data only device, it will return the getDefaultDataSubscriptionId. 905 * May return an INVALID_SUBSCRIPTION_ID on error. 906 * 907 * @return the "system" default subscription id. 908 */ 909 public static int getDefaultSubscriptionId() { 910 int subId = INVALID_SUBSCRIPTION_ID; 911 912 try { 913 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 914 if (iSub != null) { 915 subId = iSub.getDefaultSubId(); 916 } 917 } catch (RemoteException ex) { 918 // ignore it 919 } 920 921 if (VDBG) logd("getDefaultSubId=" + subId); 922 return subId; 923 } 924 925 /** 926 * Returns the system's default voice subscription id. 927 * 928 * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID. 929 * 930 * @return the default voice subscription Id. 931 */ 932 public static int getDefaultVoiceSubscriptionId() { 933 int subId = INVALID_SUBSCRIPTION_ID; 934 935 try { 936 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 937 if (iSub != null) { 938 subId = iSub.getDefaultVoiceSubId(); 939 } 940 } catch (RemoteException ex) { 941 // ignore it 942 } 943 944 if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId); 945 return subId; 946 } 947 948 /** @hide */ 949 public void setDefaultVoiceSubId(int subId) { 950 if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId); 951 try { 952 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 953 if (iSub != null) { 954 iSub.setDefaultVoiceSubId(subId); 955 } 956 } catch (RemoteException ex) { 957 // ignore it 958 } 959 } 960 961 /** 962 * Return the SubscriptionInfo for default voice subscription. 963 * 964 * Will return null on data only devices, or on error. 965 * 966 * @return the SubscriptionInfo for the default voice subscription. 967 * @hide 968 */ 969 public SubscriptionInfo getDefaultVoiceSubscriptionInfo() { 970 return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId()); 971 } 972 973 /** @hide */ 974 public static int getDefaultVoicePhoneId() { 975 return getPhoneId(getDefaultVoiceSubscriptionId()); 976 } 977 978 /** 979 * Returns the system's default SMS subscription id. 980 * 981 * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID. 982 * 983 * @return the default SMS subscription Id. 984 */ 985 public static int getDefaultSmsSubscriptionId() { 986 int subId = INVALID_SUBSCRIPTION_ID; 987 988 try { 989 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 990 if (iSub != null) { 991 subId = iSub.getDefaultSmsSubId(); 992 } 993 } catch (RemoteException ex) { 994 // ignore it 995 } 996 997 if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId); 998 return subId; 999 } 1000 1001 /** @hide */ 1002 public void setDefaultSmsSubId(int subId) { 1003 if (VDBG) logd("setDefaultSmsSubId sub id = " + subId); 1004 try { 1005 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1006 if (iSub != null) { 1007 iSub.setDefaultSmsSubId(subId); 1008 } 1009 } catch (RemoteException ex) { 1010 // ignore it 1011 } 1012 } 1013 1014 /** 1015 * Return the SubscriptionInfo for default voice subscription. 1016 * 1017 * Will return null on data only devices, or on error. 1018 * 1019 * @return the SubscriptionInfo for the default SMS subscription. 1020 * @hide 1021 */ 1022 public SubscriptionInfo getDefaultSmsSubscriptionInfo() { 1023 return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId()); 1024 } 1025 1026 /** @hide */ 1027 public int getDefaultSmsPhoneId() { 1028 return getPhoneId(getDefaultSmsSubscriptionId()); 1029 } 1030 1031 /** 1032 * Returns the system's default data subscription id. 1033 * 1034 * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID. 1035 * 1036 * @return the default data subscription Id. 1037 */ 1038 public static int getDefaultDataSubscriptionId() { 1039 int subId = INVALID_SUBSCRIPTION_ID; 1040 1041 try { 1042 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1043 if (iSub != null) { 1044 subId = iSub.getDefaultDataSubId(); 1045 } 1046 } catch (RemoteException ex) { 1047 // ignore it 1048 } 1049 1050 if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId); 1051 return subId; 1052 } 1053 1054 /** @hide */ 1055 public void setDefaultDataSubId(int subId) { 1056 if (VDBG) logd("setDataSubscription sub id = " + subId); 1057 try { 1058 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1059 if (iSub != null) { 1060 iSub.setDefaultDataSubId(subId); 1061 } 1062 } catch (RemoteException ex) { 1063 // ignore it 1064 } 1065 } 1066 1067 /** 1068 * Return the SubscriptionInfo for default data subscription. 1069 * 1070 * Will return null on voice only devices, or on error. 1071 * 1072 * @return the SubscriptionInfo for the default data subscription. 1073 * @hide 1074 */ 1075 public SubscriptionInfo getDefaultDataSubscriptionInfo() { 1076 return getActiveSubscriptionInfo(getDefaultDataSubscriptionId()); 1077 } 1078 1079 /** @hide */ 1080 public int getDefaultDataPhoneId() { 1081 return getPhoneId(getDefaultDataSubscriptionId()); 1082 } 1083 1084 /** @hide */ 1085 public void clearSubscriptionInfo() { 1086 try { 1087 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1088 if (iSub != null) { 1089 iSub.clearSubInfo(); 1090 } 1091 } catch (RemoteException ex) { 1092 // ignore it 1093 } 1094 1095 return; 1096 } 1097 1098 //FIXME this is vulnerable to race conditions 1099 /** @hide */ 1100 public boolean allDefaultsSelected() { 1101 if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) { 1102 return false; 1103 } 1104 if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) { 1105 return false; 1106 } 1107 if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) { 1108 return false; 1109 } 1110 return true; 1111 } 1112 1113 /** 1114 * If a default is set to subscription which is not active, this will reset that default back to 1115 * an invalid subscription id, i.e. < 0. 1116 * @hide 1117 */ 1118 public void clearDefaultsForInactiveSubIds() { 1119 if (VDBG) logd("clearDefaultsForInactiveSubIds"); 1120 try { 1121 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1122 if (iSub != null) { 1123 iSub.clearDefaultsForInactiveSubIds(); 1124 } 1125 } catch (RemoteException ex) { 1126 // ignore it 1127 } 1128 } 1129 1130 /** 1131 * @return true if a valid subId else false 1132 * @hide 1133 */ 1134 public static boolean isValidSubscriptionId(int subId) { 1135 return subId > INVALID_SUBSCRIPTION_ID ; 1136 } 1137 1138 /** 1139 * @return true if subId is an usable subId value else false. A 1140 * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID. 1141 * @hide 1142 */ 1143 public static boolean isUsableSubIdValue(int subId) { 1144 return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE; 1145 } 1146 1147 /** @hide */ 1148 public static boolean isValidSlotId(int slotId) { 1149 return slotId >= 0 && slotId < TelephonyManager.getDefault().getSimCount(); 1150 } 1151 1152 /** @hide */ 1153 public static boolean isValidPhoneId(int phoneId) { 1154 return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount(); 1155 } 1156 1157 /** @hide */ 1158 public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) { 1159 int[] subIds = SubscriptionManager.getSubId(phoneId); 1160 if (subIds != null && subIds.length > 0) { 1161 putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]); 1162 } else { 1163 logd("putPhoneIdAndSubIdExtra: no valid subs"); 1164 } 1165 } 1166 1167 /** @hide */ 1168 public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) { 1169 if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId); 1170 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1171 intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); 1172 //FIXME this is using phoneId and slotId interchangeably 1173 //Eventually, this should be removed as it is not the slot id 1174 intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); 1175 } 1176 1177 /** 1178 * @return the list of subId's that are active, 1179 * is never null but the length maybe 0. 1180 * @hide 1181 */ 1182 public @NonNull int[] getActiveSubscriptionIdList() { 1183 int[] subId = null; 1184 1185 try { 1186 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1187 if (iSub != null) { 1188 subId = iSub.getActiveSubIdList(); 1189 } 1190 } catch (RemoteException ex) { 1191 // ignore it 1192 } 1193 1194 if (subId == null) { 1195 subId = new int[0]; 1196 } 1197 1198 return subId; 1199 1200 } 1201 1202 /** 1203 * Returns true if the device is considered roaming on the current 1204 * network for a subscription. 1205 * <p> 1206 * Availability: Only when user registered to a network. 1207 * 1208 * @param subId The subscription ID 1209 * @return true if the network for the subscription is roaming, false otherwise 1210 */ 1211 public boolean isNetworkRoaming(int subId) { 1212 final int phoneId = getPhoneId(subId); 1213 if (phoneId < 0) { 1214 // What else can we do? 1215 return false; 1216 } 1217 return TelephonyManager.getDefault().isNetworkRoaming(subId); 1218 } 1219 1220 /** 1221 * Returns a constant indicating the state of sim for the slot idx. 1222 * 1223 * @param slotIdx 1224 * 1225 * {@See TelephonyManager#SIM_STATE_UNKNOWN} 1226 * {@See TelephonyManager#SIM_STATE_ABSENT} 1227 * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} 1228 * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} 1229 * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} 1230 * {@See TelephonyManager#SIM_STATE_READY} 1231 * {@See TelephonyManager#SIM_STATE_NOT_READY} 1232 * {@See TelephonyManager#SIM_STATE_PERM_DISABLED} 1233 * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} 1234 * 1235 * {@hide} 1236 */ 1237 public static int getSimStateForSlotIdx(int slotIdx) { 1238 int simState = TelephonyManager.SIM_STATE_UNKNOWN; 1239 1240 try { 1241 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1242 if (iSub != null) { 1243 simState = iSub.getSimStateForSlotIdx(slotIdx); 1244 } 1245 } catch (RemoteException ex) { 1246 } 1247 1248 return simState; 1249 } 1250 1251 /** 1252 * Store properties associated with SubscriptionInfo in database 1253 * @param subId Subscription Id of Subscription 1254 * @param propKey Column name in database associated with SubscriptionInfo 1255 * @param propValue Value to store in DB for particular subId & column name 1256 * @hide 1257 */ 1258 public static void setSubscriptionProperty(int subId, String propKey, String propValue) { 1259 try { 1260 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1261 if (iSub != null) { 1262 iSub.setSubscriptionProperty(subId, propKey, propValue); 1263 } 1264 } catch (RemoteException ex) { 1265 // ignore it 1266 } 1267 } 1268 1269 /** 1270 * Store properties associated with SubscriptionInfo in database 1271 * @param subId Subscription Id of Subscription 1272 * @param propKey Column name in SubscriptionInfo database 1273 * @return Value associated with subId and propKey column in database 1274 * @hide 1275 */ 1276 private static String getSubscriptionProperty(int subId, String propKey, 1277 Context context) { 1278 String resultValue = null; 1279 try { 1280 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1281 if (iSub != null) { 1282 resultValue = iSub.getSubscriptionProperty(subId, propKey, 1283 context.getOpPackageName()); 1284 } 1285 } catch (RemoteException ex) { 1286 // ignore it 1287 } 1288 return resultValue; 1289 } 1290 1291 /** 1292 * Returns boolean value corresponding to query result. 1293 * @param subId Subscription Id of Subscription 1294 * @param propKey Column name in SubscriptionInfo database 1295 * @param defValue Default boolean value to be returned 1296 * @return boolean result value to be returned 1297 * @hide 1298 */ 1299 public static boolean getBooleanSubscriptionProperty(int subId, String propKey, 1300 boolean defValue, Context context) { 1301 String result = getSubscriptionProperty(subId, propKey, context); 1302 if (result != null) { 1303 try { 1304 return Integer.parseInt(result) == 1; 1305 } catch (NumberFormatException err) { 1306 logd("getBooleanSubscriptionProperty NumberFormat exception"); 1307 } 1308 } 1309 return defValue; 1310 } 1311 1312 /** 1313 * Returns integer value corresponding to query result. 1314 * @param subId Subscription Id of Subscription 1315 * @param propKey Column name in SubscriptionInfo database 1316 * @param defValue Default integer value to be returned 1317 * @return integer result value to be returned 1318 * @hide 1319 */ 1320 public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue, 1321 Context context) { 1322 String result = getSubscriptionProperty(subId, propKey, context); 1323 if (result != null) { 1324 try { 1325 return Integer.parseInt(result); 1326 } catch (NumberFormatException err) { 1327 logd("getBooleanSubscriptionProperty NumberFormat exception"); 1328 } 1329 } 1330 return defValue; 1331 } 1332 1333 /** 1334 * Returns the resources associated with Subscription. 1335 * @param context Context object 1336 * @param subId Subscription Id of Subscription who's resources are required 1337 * @return Resources associated with Subscription. 1338 * @hide 1339 */ 1340 public static Resources getResourcesForSubId(Context context, int subId) { 1341 final SubscriptionInfo subInfo = 1342 SubscriptionManager.from(context).getActiveSubscriptionInfo(subId); 1343 1344 Configuration config = context.getResources().getConfiguration(); 1345 Configuration newConfig = new Configuration(); 1346 newConfig.setTo(config); 1347 if (subInfo != null) { 1348 newConfig.mcc = subInfo.getMcc(); 1349 newConfig.mnc = subInfo.getMnc(); 1350 if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO; 1351 } 1352 DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 1353 DisplayMetrics newMetrics = new DisplayMetrics(); 1354 newMetrics.setTo(metrics); 1355 return new Resources(context.getResources().getAssets(), newMetrics, newConfig); 1356 } 1357 1358 /** 1359 * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription 1360 * and the SIM providing the subscription is present in a slot and in "LOADED" state. 1361 * @hide 1362 */ 1363 public boolean isActiveSubId(int subId) { 1364 try { 1365 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1366 if (iSub != null) { 1367 return iSub.isActiveSubId(subId); 1368 } 1369 } catch (RemoteException ex) { 1370 } 1371 return false; 1372 } 1373} 1374