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.net.Uri; 25 import android.telephony.Rlog; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.ServiceManager; 29 import android.os.RemoteException; 30 31 import com.android.internal.telephony.ISub; 32 import com.android.internal.telephony.IOnSubscriptionsChangedListener; 33 import com.android.internal.telephony.ITelephonyRegistry; 34 import com.android.internal.telephony.PhoneConstants; 35 import com.android.internal.telephony.TelephonyProperties; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 40 /** 41 * SubscriptionManager is the application interface to SubscriptionController 42 * and provides information about the current Telephony Subscriptions. 43 * * <p> 44 * You do not instantiate this class directly; instead, you retrieve 45 * a reference to an instance through {@link #from}. 46 * <p> 47 * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE. 48 */ 49 public class SubscriptionManager { 50 private static final String LOG_TAG = "SubscriptionManager"; 51 private static final boolean DBG = false; 52 private static final boolean VDBG = false; 53 54 /** An invalid subscription identifier */ 55 /** @hide */ 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 PhoneBase 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 * Broadcast Action: The user has changed one of the default subs related to 254 * data, phone calls, or sms</p> 255 * 256 * TODO: Change to a listener 257 * @hide 258 */ 259 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 260 public static final String SUB_DEFAULT_CHANGED_ACTION = 261 "android.intent.action.SUB_DEFAULT_CHANGED"; 262 263 private final Context mContext; 264 265 /** 266 * A listener class for monitoring changes to {@link SubscriptionInfo} records. 267 * <p> 268 * Override the onSubscriptionsChanged method in the object that extends this 269 * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)} 270 * to register your listener and to unregister invoke 271 * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)} 272 * <p> 273 * Permissions android.Manifest.permission.READ_PHONE_STATE is required 274 * for #onSubscriptionsChanged to be invoked. 275 */ 276 public static class OnSubscriptionsChangedListener { 277 /** @hide */ 278 public static final String PERMISSION_ON_SUBSCRIPTIONS_CHANGED = 279 android.Manifest.permission.READ_PHONE_STATE; 280 281 private final Handler mHandler = new Handler() { 282 @Override 283 public void handleMessage(Message msg) { 284 if (DBG) { 285 log("handleMessage: invoke the overriden onSubscriptionsChanged()"); 286 } 287 OnSubscriptionsChangedListener.this.onSubscriptionsChanged(); 288 } 289 }; 290 291 /** 292 * Callback invoked when there is any change to any SubscriptionInfo. Typically 293 * this method would invoke {@link #getActiveSubscriptionInfoList} 294 */ 295 public void onSubscriptionsChanged() { 296 if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN"); 297 } 298 299 /** 300 * The callback methods need to be called on the handler thread where 301 * this object was created. If the binder did that for us it'd be nice. 302 */ 303 IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() { 304 @Override 305 public void onSubscriptionsChanged() { 306 if (DBG) log("callback: received, sendEmptyMessage(0) to handler"); 307 mHandler.sendEmptyMessage(0); 308 } 309 }; 310 311 private void log(String s) { 312 Rlog.d(LOG_TAG, s); 313 } 314 } 315 316 /** @hide */ 317 public SubscriptionManager(Context context) { 318 if (DBG) logd("SubscriptionManager created"); 319 mContext = context; 320 } 321 322 /** 323 * Get an instance of the SubscriptionManager from the Context. 324 * This invokes {@link android.content.Context#getSystemService 325 * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}. 326 * 327 * @param context to use. 328 * @return SubscriptionManager instance 329 */ 330 public static SubscriptionManager from(Context context) { 331 return (SubscriptionManager) context.getSystemService( 332 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 333 } 334 335 /** 336 * Register for changes to the list of active {@link SubscriptionInfo} records or to the 337 * individual records themselves. When a change occurs the onSubscriptionsChanged method of 338 * the listener will be invoked immediately if there has been a notification. 339 * 340 * @param listener an instance of {@link OnSubscriptionsChangedListener} with 341 * onSubscriptionsChanged overridden. 342 */ 343 public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 344 String pkgForDebug = mContext != null ? mContext.getPackageName() : "<unknown>"; 345 if (DBG) { 346 logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug 347 + " listener=" + listener); 348 } 349 try { 350 // We use the TelephonyRegistry as it runs in the system and thus is always 351 // available. Where as SubscriptionController could crash and not be available 352 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( 353 "telephony.registry")); 354 if (tr != null) { 355 tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback); 356 } 357 } catch (RemoteException ex) { 358 // Should not happen 359 } 360 } 361 362 /** 363 * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary 364 * as the listener will automatically be unregistered if an attempt to invoke the listener 365 * fails. 366 * 367 * @param listener that is to be unregistered. 368 */ 369 public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 370 String pkgForDebug = mContext != null ? mContext.getPackageName() : "<unknown>"; 371 if (DBG) { 372 logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug 373 + " listener=" + listener); 374 } 375 try { 376 // We use the TelephonyRegistry as its runs in the system and thus is always 377 // available where as SubscriptionController could crash and not be available 378 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( 379 "telephony.registry")); 380 if (tr != null) { 381 tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback); 382 } 383 } catch (RemoteException ex) { 384 // Should not happen 385 } 386 } 387 388 /** 389 * Get the active SubscriptionInfo with the subId key 390 * @param subId The unique SubscriptionInfo key in database 391 * @return SubscriptionInfo, maybe null if its not active. 392 */ 393 public SubscriptionInfo getActiveSubscriptionInfo(int subId) { 394 if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId); 395 if (!isValidSubscriptionId(subId)) { 396 logd("[getActiveSubscriptionInfo]- invalid subId"); 397 return null; 398 } 399 400 SubscriptionInfo subInfo = null; 401 402 try { 403 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 404 if (iSub != null) { 405 subInfo = iSub.getActiveSubscriptionInfo(subId); 406 } 407 } catch (RemoteException ex) { 408 // ignore it 409 } 410 411 return subInfo; 412 413 } 414 415 /** 416 * Get the active SubscriptionInfo associated with the iccId 417 * @param iccId the IccId of SIM card 418 * @return SubscriptionInfo, maybe null if its not active 419 * @hide 420 */ 421 public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) { 422 if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId); 423 if (iccId == null) { 424 logd("[getActiveSubscriptionInfoForIccIndex]- null iccid"); 425 return null; 426 } 427 428 SubscriptionInfo result = null; 429 430 try { 431 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 432 if (iSub != null) { 433 result = iSub.getActiveSubscriptionInfoForIccId(iccId); 434 } 435 } catch (RemoteException ex) { 436 // ignore it 437 } 438 439 return result; 440 } 441 442 /** 443 * Get the active SubscriptionInfo associated with the slotIdx 444 * @param slotIdx the slot which the subscription is inserted 445 * @return SubscriptionInfo, maybe null if its not active 446 */ 447 public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx) { 448 if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx); 449 if (!isValidSlotId(slotIdx)) { 450 logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIdx"); 451 return null; 452 } 453 454 SubscriptionInfo result = null; 455 456 try { 457 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 458 if (iSub != null) { 459 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIdx); 460 } 461 } catch (RemoteException ex) { 462 // ignore it 463 } 464 465 return result; 466 } 467 468 /** 469 * @return List of all SubscriptionInfo records in database, 470 * include those that were inserted before, maybe empty but not null. 471 * @hide 472 */ 473 public List<SubscriptionInfo> getAllSubscriptionInfoList() { 474 if (VDBG) logd("[getAllSubscriptionInfoList]+"); 475 476 List<SubscriptionInfo> result = null; 477 478 try { 479 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 480 if (iSub != null) { 481 result = iSub.getAllSubInfoList(); 482 } 483 } catch (RemoteException ex) { 484 // ignore it 485 } 486 487 if (result == null) { 488 result = new ArrayList<SubscriptionInfo>(); 489 } 490 return result; 491 } 492 493 /** 494 * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted 495 * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}. 496 * 497 * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device. 498 * <ul> 499 * <li> 500 * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener} 501 * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be 502 * invoked in the future. 503 * </li> 504 * <li> 505 * If the list is empty then there are no {@link SubscriptionInfo} records currently available. 506 * </li> 507 * <li> 508 * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex} 509 * then by {@link SubscriptionInfo#getSubscriptionId}. 510 * </li> 511 * </ul> 512 */ 513 public List<SubscriptionInfo> getActiveSubscriptionInfoList() { 514 List<SubscriptionInfo> result = null; 515 516 try { 517 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 518 if (iSub != null) { 519 result = iSub.getActiveSubscriptionInfoList(); 520 } 521 } catch (RemoteException ex) { 522 // ignore it 523 } 524 return result; 525 } 526 527 /** 528 * @return the count of all subscriptions in the database, this includes 529 * all subscriptions that have been seen. 530 * @hide 531 */ 532 public int getAllSubscriptionInfoCount() { 533 if (VDBG) logd("[getAllSubscriptionInfoCount]+"); 534 535 int result = 0; 536 537 try { 538 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 539 if (iSub != null) { 540 result = iSub.getAllSubInfoCount(); 541 } 542 } catch (RemoteException ex) { 543 // ignore it 544 } 545 546 return result; 547 } 548 549 /** 550 * @return the current number of active subscriptions. There is no guarantee the value 551 * returned by this method will be the same as the length of the list returned by 552 * {@link #getActiveSubscriptionInfoList}. 553 */ 554 public int getActiveSubscriptionInfoCount() { 555 int result = 0; 556 557 try { 558 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 559 if (iSub != null) { 560 result = iSub.getActiveSubInfoCount(); 561 } 562 } catch (RemoteException ex) { 563 // ignore it 564 } 565 566 return result; 567 } 568 569 /** 570 * @return the maximum number of active subscriptions that will be returned by 571 * {@link #getActiveSubscriptionInfoList} and the value returned by 572 * {@link #getActiveSubscriptionInfoCount}. 573 */ 574 public int getActiveSubscriptionInfoCountMax() { 575 int result = 0; 576 577 try { 578 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 579 if (iSub != null) { 580 result = iSub.getActiveSubInfoCountMax(); 581 } 582 } catch (RemoteException ex) { 583 // ignore it 584 } 585 586 return result; 587 } 588 589 /** 590 * Add a new SubscriptionInfo to SubscriptionInfo database if needed 591 * @param iccId the IccId of the SIM card 592 * @param slotId the slot which the SIM is inserted 593 * @return the URL of the newly created row or the updated row 594 * @hide 595 */ 596 public Uri addSubscriptionInfoRecord(String iccId, int slotId) { 597 if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotId:" + slotId); 598 if (iccId == null) { 599 logd("[addSubscriptionInfoRecord]- null iccId"); 600 } 601 if (!isValidSlotId(slotId)) { 602 logd("[addSubscriptionInfoRecord]- invalid slotId"); 603 } 604 605 try { 606 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 607 if (iSub != null) { 608 // FIXME: This returns 1 on success, 0 on error should should we return it? 609 iSub.addSubInfoRecord(iccId, slotId); 610 } 611 } catch (RemoteException ex) { 612 // ignore it 613 } 614 615 // FIXME: Always returns null? 616 return null; 617 618 } 619 620 /** 621 * Set SIM icon tint color by simInfo index 622 * @param tint the RGB value of icon tint color of the SIM 623 * @param subId the unique SubInfoRecord index in database 624 * @return the number of records updated 625 * @hide 626 */ 627 public int setIconTint(int tint, int subId) { 628 if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); 629 if (!isValidSubscriptionId(subId)) { 630 logd("[setIconTint]- fail"); 631 return -1; 632 } 633 634 int result = 0; 635 636 try { 637 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 638 if (iSub != null) { 639 result = iSub.setIconTint(tint, subId); 640 } 641 } catch (RemoteException ex) { 642 // ignore it 643 } 644 645 return result; 646 647 } 648 649 /** 650 * Set display name by simInfo index 651 * @param displayName the display name of SIM card 652 * @param subId the unique SubscriptionInfo index in database 653 * @return the number of records updated 654 * @hide 655 */ 656 public int setDisplayName(String displayName, int subId) { 657 return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED); 658 } 659 660 /** 661 * Set display name by simInfo index with name source 662 * @param displayName the display name of SIM card 663 * @param subId the unique SubscriptionInfo index in database 664 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, 665 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED 666 * @return the number of records updated or < 0 if invalid subId 667 * @hide 668 */ 669 public int setDisplayName(String displayName, int subId, long nameSource) { 670 if (VDBG) { 671 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId 672 + " nameSource:" + nameSource); 673 } 674 if (!isValidSubscriptionId(subId)) { 675 logd("[setDisplayName]- fail"); 676 return -1; 677 } 678 679 int result = 0; 680 681 try { 682 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 683 if (iSub != null) { 684 result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource); 685 } 686 } catch (RemoteException ex) { 687 // ignore it 688 } 689 690 return result; 691 692 } 693 694 /** 695 * Set phone number by subId 696 * @param number the phone number of the SIM 697 * @param subId the unique SubscriptionInfo index in database 698 * @return the number of records updated 699 * @hide 700 */ 701 public int setDisplayNumber(String number, int subId) { 702 if (number == null || !isValidSubscriptionId(subId)) { 703 logd("[setDisplayNumber]- fail"); 704 return -1; 705 } 706 707 int result = 0; 708 709 try { 710 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 711 if (iSub != null) { 712 result = iSub.setDisplayNumber(number, subId); 713 } 714 } catch (RemoteException ex) { 715 // ignore it 716 } 717 718 return result; 719 720 } 721 722 /** 723 * Set data roaming by simInfo index 724 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming 725 * @param subId the unique SubscriptionInfo index in database 726 * @return the number of records updated 727 * @hide 728 */ 729 public int setDataRoaming(int roaming, int subId) { 730 if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); 731 if (roaming < 0 || !isValidSubscriptionId(subId)) { 732 logd("[setDataRoaming]- fail"); 733 return -1; 734 } 735 736 int result = 0; 737 738 try { 739 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 740 if (iSub != null) { 741 result = iSub.setDataRoaming(roaming, subId); 742 } 743 } catch (RemoteException ex) { 744 // ignore it 745 } 746 747 return result; 748 } 749 750 /** 751 * Get slotId associated with the subscription. 752 * @return slotId as a positive integer or a negative value if an error either 753 * SIM_NOT_INSERTED or < 0 if an invalid slot index 754 * @hide 755 */ 756 public static int getSlotId(int subId) { 757 if (!isValidSubscriptionId(subId)) { 758 logd("[getSlotId]- fail"); 759 } 760 761 int result = INVALID_SIM_SLOT_INDEX; 762 763 try { 764 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 765 if (iSub != null) { 766 result = iSub.getSlotId(subId); 767 } 768 } catch (RemoteException ex) { 769 // ignore it 770 } 771 772 return result; 773 774 } 775 776 /** @hide */ 777 public static int[] getSubId(int slotId) { 778 if (!isValidSlotId(slotId)) { 779 logd("[getSubId]- fail"); 780 return null; 781 } 782 783 int[] subId = null; 784 785 try { 786 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 787 if (iSub != null) { 788 subId = iSub.getSubId(slotId); 789 } 790 } catch (RemoteException ex) { 791 // ignore it 792 } 793 794 return subId; 795 } 796 797 /** @hide */ 798 public static int getPhoneId(int subId) { 799 if (!isValidSubscriptionId(subId)) { 800 logd("[getPhoneId]- fail"); 801 return INVALID_PHONE_INDEX; 802 } 803 804 int result = INVALID_PHONE_INDEX; 805 806 try { 807 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 808 if (iSub != null) { 809 result = iSub.getPhoneId(subId); 810 } 811 } catch (RemoteException ex) { 812 // ignore it 813 } 814 815 if (VDBG) logd("[getPhoneId]- phoneId=" + result); 816 return result; 817 818 } 819 820 private static void logd(String msg) { 821 Rlog.d(LOG_TAG, msg); 822 } 823 824 /** 825 * @return the "system" defaultSubId on a voice capable device this 826 * will be getDefaultVoiceSubId() and on a data only device it will be 827 * getDefaultDataSubId(). 828 * @hide 829 */ 830 public static int getDefaultSubId() { 831 int subId = INVALID_SUBSCRIPTION_ID; 832 833 try { 834 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 835 if (iSub != null) { 836 subId = iSub.getDefaultSubId(); 837 } 838 } catch (RemoteException ex) { 839 // ignore it 840 } 841 842 if (VDBG) logd("getDefaultSubId=" + subId); 843 return subId; 844 } 845 846 /** @hide */ 847 public static int getDefaultVoiceSubId() { 848 int subId = INVALID_SUBSCRIPTION_ID; 849 850 try { 851 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 852 if (iSub != null) { 853 subId = iSub.getDefaultVoiceSubId(); 854 } 855 } catch (RemoteException ex) { 856 // ignore it 857 } 858 859 if (VDBG) logd("getDefaultVoiceSubId, sub id = " + subId); 860 return subId; 861 } 862 863 /** @hide */ 864 public void setDefaultVoiceSubId(int subId) { 865 if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId); 866 try { 867 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 868 if (iSub != null) { 869 iSub.setDefaultVoiceSubId(subId); 870 } 871 } catch (RemoteException ex) { 872 // ignore it 873 } 874 } 875 876 /** @hide */ 877 public SubscriptionInfo getDefaultVoiceSubscriptionInfo() { 878 return getActiveSubscriptionInfo(getDefaultVoiceSubId()); 879 } 880 881 /** @hide */ 882 public static int getDefaultVoicePhoneId() { 883 return getPhoneId(getDefaultVoiceSubId()); 884 } 885 886 /** 887 * @return subId of the DefaultSms subscription or 888 * a value < 0 if an error. 889 * 890 * @hide 891 */ 892 public static int getDefaultSmsSubId() { 893 int subId = INVALID_SUBSCRIPTION_ID; 894 895 try { 896 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 897 if (iSub != null) { 898 subId = iSub.getDefaultSmsSubId(); 899 } 900 } catch (RemoteException ex) { 901 // ignore it 902 } 903 904 if (VDBG) logd("getDefaultSmsSubId, sub id = " + subId); 905 return subId; 906 } 907 908 /** @hide */ 909 public void setDefaultSmsSubId(int subId) { 910 if (VDBG) logd("setDefaultSmsSubId sub id = " + subId); 911 try { 912 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 913 if (iSub != null) { 914 iSub.setDefaultSmsSubId(subId); 915 } 916 } catch (RemoteException ex) { 917 // ignore it 918 } 919 } 920 921 /** @hide */ 922 public SubscriptionInfo getDefaultSmsSubscriptionInfo() { 923 return getActiveSubscriptionInfo(getDefaultSmsSubId()); 924 } 925 926 /** @hide */ 927 public int getDefaultSmsPhoneId() { 928 return getPhoneId(getDefaultSmsSubId()); 929 } 930 931 /** @hide */ 932 public static int getDefaultDataSubId() { 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.getDefaultDataSubId(); 939 } 940 } catch (RemoteException ex) { 941 // ignore it 942 } 943 944 if (VDBG) logd("getDefaultDataSubId, sub id = " + subId); 945 return subId; 946 } 947 948 /** @hide */ 949 public void setDefaultDataSubId(int subId) { 950 if (VDBG) logd("setDataSubscription sub id = " + subId); 951 try { 952 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 953 if (iSub != null) { 954 iSub.setDefaultDataSubId(subId); 955 } 956 } catch (RemoteException ex) { 957 // ignore it 958 } 959 } 960 961 /** @hide */ 962 public SubscriptionInfo getDefaultDataSubscriptionInfo() { 963 return getActiveSubscriptionInfo(getDefaultDataSubId()); 964 } 965 966 /** @hide */ 967 public int getDefaultDataPhoneId() { 968 return getPhoneId(getDefaultDataSubId()); 969 } 970 971 /** @hide */ 972 public void clearSubscriptionInfo() { 973 try { 974 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 975 if (iSub != null) { 976 iSub.clearSubInfo(); 977 } 978 } catch (RemoteException ex) { 979 // ignore it 980 } 981 982 return; 983 } 984 985 //FIXME this is vulnerable to race conditions 986 /** @hide */ 987 public boolean allDefaultsSelected() { 988 if (!isValidSubscriptionId(getDefaultDataSubId())) { 989 return false; 990 } 991 if (!isValidSubscriptionId(getDefaultSmsSubId())) { 992 return false; 993 } 994 if (!isValidSubscriptionId(getDefaultVoiceSubId())) { 995 return false; 996 } 997 return true; 998 } 999 1000 /** 1001 * If a default is set to subscription which is not active, this will reset that default back to 1002 * an invalid subscription id, i.e. < 0. 1003 * @hide 1004 */ 1005 public void clearDefaultsForInactiveSubIds() { 1006 if (VDBG) logd("clearDefaultsForInactiveSubIds"); 1007 try { 1008 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1009 if (iSub != null) { 1010 iSub.clearDefaultsForInactiveSubIds(); 1011 } 1012 } catch (RemoteException ex) { 1013 // ignore it 1014 } 1015 } 1016 1017 /** 1018 * @return true if a valid subId else false 1019 * @hide 1020 */ 1021 public static boolean isValidSubscriptionId(int subId) { 1022 return subId > INVALID_SUBSCRIPTION_ID ; 1023 } 1024 1025 /** 1026 * @return true if subId is an usable subId value else false. A 1027 * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID. 1028 * @hide 1029 */ 1030 public static boolean isUsableSubIdValue(int subId) { 1031 return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE; 1032 } 1033 1034 /** @hide */ 1035 public static boolean isValidSlotId(int slotId) { 1036 return slotId >= 0 && slotId < TelephonyManager.getDefault().getSimCount(); 1037 } 1038 1039 /** @hide */ 1040 public static boolean isValidPhoneId(int phoneId) { 1041 return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount(); 1042 } 1043 1044 /** @hide */ 1045 public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) { 1046 int[] subIds = SubscriptionManager.getSubId(phoneId); 1047 if (subIds != null && subIds.length > 0) { 1048 putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]); 1049 } else { 1050 logd("putPhoneIdAndSubIdExtra: no valid subs"); 1051 } 1052 } 1053 1054 /** @hide */ 1055 public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) { 1056 if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId); 1057 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); 1058 intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); 1059 //FIXME this is using phoneId and slotId interchangeably 1060 //Eventually, this should be removed as it is not the slot id 1061 intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); 1062 } 1063 1064 /** 1065 * @return the list of subId's that are active, 1066 * is never null but the length maybe 0. 1067 * @hide 1068 */ 1069 public @NonNull int[] getActiveSubscriptionIdList() { 1070 int[] subId = null; 1071 1072 try { 1073 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1074 if (iSub != null) { 1075 subId = iSub.getActiveSubIdList(); 1076 } 1077 } catch (RemoteException ex) { 1078 // ignore it 1079 } 1080 1081 if (subId == null) { 1082 subId = new int[0]; 1083 } 1084 1085 return subId; 1086 1087 } 1088 1089 /** 1090 * Returns true if the device is considered roaming on the current 1091 * network for a subscription. 1092 * <p> 1093 * Availability: Only when user registered to a network. 1094 * 1095 * @param subId The subscription ID 1096 * @return true if the network for the subscription is roaming, false otherwise 1097 */ 1098 public boolean isNetworkRoaming(int subId) { 1099 final int phoneId = getPhoneId(subId); 1100 if (phoneId < 0) { 1101 // What else can we do? 1102 return false; 1103 } 1104 return TelephonyManager.getDefault().isNetworkRoaming(subId); 1105 } 1106 1107 /** 1108 * Returns a constant indicating the state of sim for the subscription. 1109 * 1110 * @param subId 1111 * 1112 * {@See TelephonyManager#SIM_STATE_UNKNOWN} 1113 * {@See TelephonyManager#SIM_STATE_ABSENT} 1114 * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} 1115 * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} 1116 * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} 1117 * {@See TelephonyManager#SIM_STATE_READY} 1118 * {@See TelephonyManager#SIM_STATE_NOT_READY} 1119 * {@See TelephonyManager#SIM_STATE_PERM_DISABLED} 1120 * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} 1121 * 1122 * {@hide} 1123 */ 1124 public static int getSimStateForSubscriber(int subId) { 1125 int simState; 1126 1127 try { 1128 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); 1129 simState = iSub.getSimStateForSubscriber(subId); 1130 } catch (RemoteException ex) { 1131 simState = TelephonyManager.SIM_STATE_UNKNOWN; 1132 } 1133 logd("getSimStateForSubscriber: simState=" + simState + " subId=" + subId); 1134 return simState; 1135 } 1136 } 1137 1138