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