summaryrefslogtreecommitdiff
path: root/android/telephony
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/telephony
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/telephony')
-rw-r--r--android/telephony/CarrierConfigManager.java1968
-rw-r--r--android/telephony/CarrierMessagingServiceManager.java99
-rw-r--r--android/telephony/CellBroadcastMessage.java437
-rw-r--r--android/telephony/CellIdentityCdma.java224
-rw-r--r--android/telephony/CellIdentityGsm.java249
-rw-r--r--android/telephony/CellIdentityLte.java235
-rw-r--r--android/telephony/CellIdentityWcdma.java236
-rw-r--r--android/telephony/CellInfo.java216
-rw-r--r--android/telephony/CellInfoCdma.java149
-rw-r--r--android/telephony/CellInfoGsm.java148
-rw-r--r--android/telephony/CellInfoLte.java154
-rw-r--r--android/telephony/CellInfoWcdma.java148
-rw-r--r--android/telephony/CellLocation.java106
-rw-r--r--android/telephony/CellSignalStrength.java83
-rw-r--r--android/telephony/CellSignalStrengthCdma.java396
-rw-r--r--android/telephony/CellSignalStrengthGsm.java268
-rw-r--r--android/telephony/CellSignalStrengthLte.java336
-rw-r--r--android/telephony/CellSignalStrengthWcdma.java235
-rw-r--r--android/telephony/ClientRequestStats.java175
-rw-r--r--android/telephony/DataConnectionRealTimeInfo.java139
-rw-r--r--android/telephony/DisconnectCause.java420
-rw-r--r--android/telephony/IccOpenLogicalChannelResponse.java130
-rw-r--r--android/telephony/ImsiEncryptionInfo.java154
-rw-r--r--android/telephony/JapanesePhoneNumberFormatter.java218
-rw-r--r--android/telephony/MbmsDownloadManager.java586
-rw-r--r--android/telephony/MbmsStreamingManager.java323
-rw-r--r--android/telephony/ModemActivityInfo.java174
-rw-r--r--android/telephony/NeighboringCellInfo.java299
-rw-r--r--android/telephony/NetworkScan.java97
-rw-r--r--android/telephony/NetworkScanRequest.java123
-rw-r--r--android/telephony/PcoData.java87
-rw-r--r--android/telephony/PhoneNumberFormattingTextWatcher.java174
-rw-r--r--android/telephony/PhoneNumberUtils.java3167
-rw-r--r--android/telephony/PhoneStateListener.java694
-rw-r--r--android/telephony/PreciseCallState.java309
-rw-r--r--android/telephony/PreciseDataConnectionState.java273
-rw-r--r--android/telephony/PreciseDisconnectCause.java507
-rw-r--r--android/telephony/RadioAccessFamily.java385
-rw-r--r--android/telephony/RadioAccessSpecifier.java129
-rw-r--r--android/telephony/RadioNetworkConstants.java169
-rw-r--r--android/telephony/Rlog.java153
-rw-r--r--android/telephony/ServiceState.java1330
-rw-r--r--android/telephony/SignalStrength.java1106
-rw-r--r--android/telephony/SmsCbCmasInfo.java310
-rw-r--r--android/telephony/SmsCbEtwsInfo.java222
-rw-r--r--android/telephony/SmsCbLocation.java200
-rw-r--r--android/telephony/SmsCbMessage.java382
-rw-r--r--android/telephony/SmsManager.java1714
-rw-r--r--android/telephony/SmsMessage.java926
-rw-r--r--android/telephony/SubscriptionInfo.java469
-rw-r--r--android/telephony/SubscriptionManager.java1603
-rw-r--r--android/telephony/SubscriptionPlan.java301
-rw-r--r--android/telephony/TelephonyHistogram.java313
-rw-r--r--android/telephony/TelephonyManager.java6853
-rw-r--r--android/telephony/TelephonyScanManager.java198
-rw-r--r--android/telephony/UiccAccessRule.java230
-rw-r--r--android/telephony/UssdResponse.java80
-rw-r--r--android/telephony/VisualVoicemailService.java296
-rw-r--r--android/telephony/VisualVoicemailSms.java149
-rw-r--r--android/telephony/VisualVoicemailSmsFilterSettings.java188
-rw-r--r--android/telephony/VoLteServiceState.java229
-rw-r--r--android/telephony/cdma/CdmaCellLocation.java250
-rw-r--r--android/telephony/cdma/CdmaSmsCbProgramData.java210
-rw-r--r--android/telephony/cdma/CdmaSmsCbProgramResults.java144
-rw-r--r--android/telephony/euicc/DownloadableSubscription.java145
-rw-r--r--android/telephony/euicc/EuiccInfo.java72
-rw-r--r--android/telephony/euicc/EuiccManager.java523
-rw-r--r--android/telephony/gsm/GsmCellLocation.java154
-rw-r--r--android/telephony/gsm/SmsManager.java261
-rw-r--r--android/telephony/gsm/SmsMessage.java625
-rw-r--r--android/telephony/ims/ImsService.java493
-rw-r--r--android/telephony/ims/ImsServiceProxy.java335
-rw-r--r--android/telephony/ims/ImsServiceProxyCompat.java183
-rw-r--r--android/telephony/ims/feature/IMMTelFeature.java187
-rw-r--r--android/telephony/ims/feature/IRcsFeature.java26
-rw-r--r--android/telephony/ims/feature/ImsFeature.java218
-rw-r--r--android/telephony/ims/feature/MMTelFeature.java122
-rw-r--r--android/telephony/ims/feature/RcsFeature.java35
-rw-r--r--android/telephony/ims/stub/ImsCallSessionImplBase.java373
-rw-r--r--android/telephony/ims/stub/ImsCallSessionListenerImplBase.java298
-rw-r--r--android/telephony/ims/stub/ImsConfigImplBase.java150
-rw-r--r--android/telephony/ims/stub/ImsEcbmImplBase.java51
-rw-r--r--android/telephony/ims/stub/ImsMultiEndpointImplBase.java53
-rw-r--r--android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java40
-rw-r--r--android/telephony/ims/stub/ImsUtImplBase.java174
-rw-r--r--android/telephony/ims/stub/ImsUtListenerImplBase.java88
-rw-r--r--android/telephony/mbms/DownloadProgressListener.java48
-rw-r--r--android/telephony/mbms/DownloadRequest.java377
-rw-r--r--android/telephony/mbms/FileInfo.java86
-rw-r--r--android/telephony/mbms/FileServiceInfo.java77
-rw-r--r--android/telephony/mbms/InternalStreamingManagerCallback.java72
-rw-r--r--android/telephony/mbms/InternalStreamingServiceCallback.java81
-rw-r--r--android/telephony/mbms/MbmsDownloadManagerCallback.java64
-rw-r--r--android/telephony/mbms/MbmsDownloadReceiver.java536
-rw-r--r--android/telephony/mbms/MbmsException.java144
-rw-r--r--android/telephony/mbms/MbmsStreamingManagerCallback.java68
-rw-r--r--android/telephony/mbms/MbmsTempFileProvider.java192
-rw-r--r--android/telephony/mbms/MbmsUtils.java94
-rw-r--r--android/telephony/mbms/ServiceInfo.java180
-rw-r--r--android/telephony/mbms/StreamingService.java199
-rw-r--r--android/telephony/mbms/StreamingServiceCallback.java102
-rw-r--r--android/telephony/mbms/StreamingServiceInfo.java75
-rw-r--r--android/telephony/mbms/UriPathPair.java80
-rw-r--r--android/telephony/mbms/vendor/MbmsDownloadServiceBase.java247
-rw-r--r--android/telephony/mbms/vendor/MbmsStreamingServiceBase.java279
-rw-r--r--android/telephony/mbms/vendor/VendorIntents.java166
106 files changed, 40048 insertions, 0 deletions
diff --git a/android/telephony/CarrierConfigManager.java b/android/telephony/CarrierConfigManager.java
new file mode 100644
index 00000000..689ce954
--- /dev/null
+++ b/android/telephony/CarrierConfigManager.java
@@ -0,0 +1,1968 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.carrier.CarrierService;
+
+import com.android.ims.ImsReasonInfo;
+import com.android.internal.telephony.ICarrierConfigLoader;
+
+/**
+ * Provides access to telephony configuration values that are carrier-specific.
+ */
+@SystemService(Context.CARRIER_CONFIG_SERVICE)
+public class CarrierConfigManager {
+ private final static String TAG = "CarrierConfigManager";
+
+ /**
+ * @hide
+ */
+ public CarrierConfigManager() {
+ }
+
+ /**
+ * This intent is broadcast by the system when carrier config changes.
+ */
+ public static final String
+ ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+
+ // Below are the keys used in carrier config bundles. To add a new variable, define the key and
+ // give it a default value in sDefaults. If you need to ship a per-network override in the
+ // system image, that can be added in packages/apps/CarrierConfig.
+
+ /**
+ * This flag specifies whether VoLTE availability is based on provisioning. By default this is
+ * false.
+ */
+ public static final String
+ KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+
+ /**
+ * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
+ * events from the Sim.
+ * If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and
+ * effectively disable the "Sim network lock" feature.
+ */
+ public static final String
+ KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
+
+ /**
+ * When checking if a given number is the voicemail number, if this flag is true
+ * then in addition to comparing the given number to the voicemail number, we also compare it
+ * to the mdn. If this flag is false, the given number is only compared to the voicemail number.
+ * By default this value is false.
+ */
+ public static final String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL =
+ "mdn_is_additional_voicemail_number_bool";
+
+ /**
+ * Flag indicating whether the Phone app should provide a "Dismiss" button on the SIM network
+ * unlock screen. The default value is true. If set to false, there will be *no way* to dismiss
+ * the SIM network unlock screen if you don't enter the correct unlock code. (One important
+ * consequence: there will be no way to make an Emergency Call if your SIM is network-locked and
+ * you don't know the PIN.)
+ */
+ public static final String
+ KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+
+ /** Flag indicating if the phone is a world phone */
+ public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+
+ /**
+ * Flag to require or skip entitlement checks.
+ * If true, entitlement checks will be executed if device has been configured for it,
+ * If false, entitlement checks will be skipped.
+ */
+ public static final String
+ KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
+
+ /**
+ * Flag indicating whether radio is to be restarted on error PDP_FAIL_REGULAR_DEACTIVATION
+ * This is false by default.
+ */
+ public static final String
+ KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL =
+ "restart_radio_on_pdp_fail_regular_deactivation_bool";
+
+ /**
+ * If true, enable vibration (haptic feedback) for key presses in the EmergencyDialer activity.
+ * The pattern is set on a per-platform basis using config_virtualKeyVibePattern. To be
+ * consistent with the regular Dialer, this value should agree with the corresponding values
+ * from config.xml under apps/Contacts.
+ */
+ public static final String
+ KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+
+ /** Flag indicating if dtmf tone type is enabled */
+ public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
+
+ /** Flag indicating if auto retry is enabled */
+ public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+
+ /**
+ * Determine whether we want to play local DTMF tones in a call, or just let the radio/BP handle
+ * playing of the tones.
+ */
+ public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+
+ /**
+ * Determines if the carrier requires converting the destination number before sending out an
+ * SMS. Certain networks and numbering plans require different formats.
+ */
+ public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL=
+ "sms_requires_destination_number_conversion_bool";
+
+ /**
+ * If true, show an onscreen "Dial" button in the dialer. In practice this is used on all
+ * platforms, even the ones with hard SEND/END keys, but for maximum flexibility it's controlled
+ * by a flag here (which can be overridden on a per-product basis.)
+ */
+ public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+
+ /** Determines if device implements a noise suppression device for in call audio. */
+ public static final String
+ KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
+
+ /**
+ * Determines if the current device should allow emergency numbers to be logged in the Call Log.
+ * (Some carriers require that emergency calls *not* be logged, presumably to avoid the risk of
+ * accidental redialing from the call log UI. This is a good idea, so the default here is
+ * false.)
+ */
+ public static final String
+ KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
+
+ /** If true, removes the Voice Privacy option from Call Settings */
+ public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
+
+ /** Control whether users can reach the carrier portions of Cellular Network Settings. */
+ public static final String
+ KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
+
+ /**
+ * Control whether users receive a simplified network settings UI and improved network
+ * selection.
+ */
+ public static final String
+ KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
+
+ /** Control whether users can reach the SIM lock settings. */
+ public static final String
+ KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
+
+ /** Control whether users can edit APNs in Settings. */
+ public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
+
+ /** Control whether users can choose a network operator. */
+ public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+
+ /** Used in Cellular Network Settings for preferred network type. */
+ public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+
+ /** Show cdma network mode choices 1x, 3G, global etc. */
+ public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
+
+ /** CDMA activation goes through HFA */
+ public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
+
+ /**
+ * CDMA activation goes through OTASP.
+ * <p>
+ * TODO: This should be combined with config_use_hfa_for_provisioning and implemented as an enum
+ * (NONE, HFA, OTASP).
+ */
+ public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+
+ /** Display carrier settings menu if true */
+ public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+
+ /** Does not display additional call setting for IMS phone based on GSM Phone */
+ public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
+
+ /** Show APN Settings for some CDMA carriers */
+ public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+
+ /** After a CDMA conference call is merged, the swap button should be displayed. */
+ public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+
+ /**
+ * Since the default voicemail number is empty, if a SIM card does not have a voicemail number
+ * available the user cannot use voicemail. This flag allows the user to edit the voicemail
+ * number in such cases, and is false by default.
+ */
+ public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL= "editable_voicemail_number_bool";
+
+ /**
+ * Determine whether the voicemail notification is persistent in the notification bar. If true,
+ * the voicemail notifications cannot be dismissed from the notification bar.
+ */
+ public static final String
+ KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
+
+ /** For IMS video over LTE calls, determines whether video pause signalling is supported. */
+ public static final String
+ KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
+
+ /**
+ * Disables dialing "*228" (OTASP provisioning) on CDMA carriers where it is not supported or is
+ * potentially harmful by locking the SIM to 3G.
+ */
+ public static final String
+ KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+
+ /**
+ * List of RIL radio technologies (See {@link ServiceState} {@code RIL_RADIO_TECHNOLOGY_*}
+ * constants) which support only a single data connection at a time. Some carriers do not
+ * support multiple pdp on UMTS.
+ */
+ public static final String
+ KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
+
+ /**
+ * Override the platform's notion of a network operator being considered roaming.
+ * Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
+ */
+ public static final String
+ KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
+
+ /**
+ * Override the platform's notion of a network operator being considered not roaming.
+ * Value is string array of MCCMNCs to be considered not roaming for 3GPP RATs.
+ */
+ public static final String
+ KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
+
+ /**
+ * Override the device's configuration for the ImsService to use for this SIM card.
+ */
+ public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING =
+ "config_ims_package_override_string";
+
+ /**
+ * Override the package that will manage {@link SubscriptionPlan}
+ * information instead of the {@link CarrierService} that defines this
+ * value.
+ *
+ * @see SubscriptionManager#getSubscriptionPlans(int)
+ * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING =
+ "config_plans_package_override_string";
+
+ /**
+ * Override the platform's notion of a network operator being considered roaming.
+ * Value is string array of SIDs to be considered roaming for 3GPP2 RATs.
+ */
+ public static final String
+ KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+
+ /**
+ * Override the platform's notion of a network operator being considered non roaming.
+ * Value is string array of SIDs to be considered not roaming for 3GPP2 RATs.
+ */
+ public static final String
+ KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+
+ /**
+ * Override the platform's notion of a network operator being considered non roaming.
+ * If true all networks are considered as home network a.k.a non-roaming. When false,
+ * the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
+ *
+ * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+ */
+ public static final String
+ KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
+
+ /**
+ * Flag specifying whether VoLTE should be available for carrier, independent of carrier
+ * provisioning. If false: hard disabled. If true: then depends on carrier provisioning,
+ * availability, etc.
+ */
+ public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
+
+ /**
+ * Flag specifying whether video telephony is available for carrier. If false: hard disabled.
+ * If true: then depends on carrier provisioning, availability, etc.
+ */
+ public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
+
+ /**
+ * Flag specifying whether the carrier wants to notify the user when a VT call has been handed
+ * over from WIFI to LTE.
+ * <p>
+ * The handover notification is sent as a
+ * {@link TelephonyManager#EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE}
+ * {@link android.telecom.Connection} event, which an {@link android.telecom.InCallService}
+ * should use to trigger the display of a user-facing message.
+ * <p>
+ * The Connection event is sent to the InCallService only once, the first time it occurs.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL =
+ "notify_handover_video_from_wifi_to_lte_bool";
+
+ /**
+ * Flag specifying whether the carrier supports downgrading a video call (tx, rx or tx/rx)
+ * directly to an audio call.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL =
+ "support_downgrade_vt_to_audio_bool";
+
+ /**
+ * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+ * voicemail number.
+ * When empty string, no default voicemail number is specified.
+ */
+ public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+
+ /**
+ * When {@code true}, changes to the mobile data enabled switch will not cause the VT
+ * registration state to change. That is, turning on or off mobile data will not cause VT to be
+ * enabled or disabled.
+ * When {@code false}, disabling mobile data will cause VT to be de-registered.
+ * <p>
+ * See also {@link #KEY_VILTE_DATA_IS_METERED_BOOL}.
+ * @hide
+ */
+ public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS =
+ "ignore_data_enabled_changed_for_video_calls";
+
+ /**
+ * Flag indicating whether data used for a video call over LTE is metered or not.
+ * <p>
+ * When {@code true}, if the device hits the data limit or data is disabled during a ViLTE call,
+ * the call will be downgraded to audio-only (or paused if
+ * {@link #KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} is {@code true}).
+ *
+ * @hide
+ */
+ public static final String KEY_VILTE_DATA_IS_METERED_BOOL = "vilte_data_is_metered_bool";
+
+ /**
+ * Flag specifying whether WFC over IMS should be available for carrier: independent of
+ * carrier provisioning. If false: hard disabled. If true: then depends on carrier
+ * provisioning, availability etc.
+ */
+ public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+
+ /**
+ * Specifies a map from dialstrings to replacements for roaming network service numbers which
+ * cannot be replaced on the carrier side.
+ * <p>
+ * Individual entries have the format:
+ * [dialstring to replace]:[replacement]
+ */
+ public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY =
+ "dial_string_replace_string_array";
+
+ /**
+ * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi
+ * calling settings will not include an option for "wifi only". If true, the wifi calling
+ * settings will include an option for "wifi only"
+ * <p>
+ * By default, it is assumed that WFC supports "wifi only".
+ */
+ public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL =
+ "carrier_wfc_supports_wifi_only_bool";
+
+ /**
+ * Default WFC_IMS_MODE for home network 0: WIFI_ONLY
+ * 1: CELLULAR_PREFERRED
+ * 2: WIFI_PREFERRED
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT =
+ "carrier_default_wfc_ims_mode_int";
+
+ /**
+ * Default WFC_IMS_MODE for roaming
+ * See {@link KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} for valid values.
+ *
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT =
+ "carrier_default_wfc_ims_roaming_mode_int";
+
+ /**
+ * Default WFC_IMS_enabled: true VoWiFi by default is on
+ * false VoWiFi by default is off
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL =
+ "carrier_default_wfc_ims_enabled_bool";
+
+ /**
+ * Default WFC_IMS_roaming_enabled: true VoWiFi roaming by default is on
+ * false VoWiFi roaming by default is off
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL =
+ "carrier_default_wfc_ims_roaming_enabled_bool";
+
+ /**
+ * Flag indicating whether failed calls due to no service should prompt the user to enable
+ * WIFI calling. When {@code true}, if the user attempts to establish a call when there is no
+ * service available, they are connected to WIFI, and WIFI calling is disabled, a different
+ * call failure message will be used to encourage the user to enable WIFI calling.
+ * @hide
+ */
+ public static final String KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL =
+ "carrier_promote_wfc_on_call_fail_bool";
+
+ /** Flag specifying whether provisioning is required for VOLTE. */
+ public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ = "carrier_volte_provisioning_required_bool";
+
+ /**
+ * Flag specifying if WFC provisioning depends on VoLTE provisioning.
+ *
+ * {@code false}: default value; honor actual WFC provisioning state.
+ * {@code true}: when VoLTE is not provisioned, treat WFC as not provisioned; when VoLTE is
+ * provisioned, honor actual WFC provisioning state.
+ *
+ * As of now, Verizon is the only carrier enforcing this dependency in their
+ * WFC awareness and activation requirements.
+ *
+ * @hide
+ * */
+ public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL
+ = "carrier_volte_override_wfc_provisioning_bool";
+
+ /** Flag specifying whether VoLTE TTY is supported. */
+ public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+ = "carrier_volte_tty_supported_bool";
+
+ /**
+ * Flag specifying whether IMS service can be turned off. If false then the service will not be
+ * turned-off completely, but individual features can be disabled.
+ */
+ public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL
+ = "carrier_allow_turnoff_ims_bool";
+
+ /**
+ * Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS.
+ */
+ public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+ = "carrier_ims_gba_required_bool";
+
+ /**
+ * Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if
+ * instant lettering is available for the carrier, {@code false} otherwise.
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL =
+ "carrier_instant_lettering_available_bool";
+
+ /*
+ * Flag specifying whether IMS should be the first phone attempted for E911 even if the
+ * phone is not in service.
+ */
+ public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL
+ = "carrier_use_ims_first_for_emergency_bool";
+
+ /**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the list of characters
+ * which may not be contained in messages. Should be specified as a regular expression suitable
+ * for use with {@link String#matches(String)}.
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING =
+ "carrier_instant_lettering_invalid_chars_string";
+
+ /**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines a list of characters which
+ * must be escaped with a backslash '\' character. Should be specified as a string containing
+ * the characters to be escaped. For example to escape quote and backslash the string would be
+ * a quote and a backslash.
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING =
+ "carrier_instant_lettering_escaped_chars_string";
+
+ /**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the character encoding
+ * which will be used when determining the length of messages. Used in the InCall UI to limit
+ * the number of characters the user may type. If empty-string, the instant lettering
+ * message size limit will be enforced on a 1:1 basis. That is, each character will count
+ * towards the messages size limit as a single bye. If a character encoding is specified, the
+ * message size limit will be based on the number of bytes in the message per the specified
+ * encoding.
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING =
+ "carrier_instant_lettering_encoding_string";
+
+ /**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages. Used
+ * in the InCall UI to ensure the user cannot enter more characters than allowed by the carrier.
+ * See also {@link #KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING} for more information on how
+ * the length of the message is calculated.
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT =
+ "carrier_instant_lettering_length_limit_int";
+
+ /**
+ * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
+ * this is the value that should be used instead. A configuration value of
+ * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
+ * assumption for phone type (GSM) should be used.
+ */
+ public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+
+ /**
+ * The default sim call manager to use when the default dialer doesn't implement one. A sim call
+ * manager can control and route outgoing and incoming phone calls, even if they're placed
+ * using another connection service (PSTN, for example).
+ */
+ public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
+
+ /**
+ * The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in
+ * Settings->More->Emergency broadcasts menu even though developer options is turned on.
+ */
+ public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
+ "carrier_force_disable_etws_cmas_test_bool";
+
+ /**
+ * The default flag specifying whether "Turn on Notifications" option will be always shown in
+ * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not.
+ */
+ public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
+ "always_show_emergency_alert_onoff_bool";
+
+ /**
+ * The flag to disable cell broadcast severe alert when extreme alert is disabled.
+ * @hide
+ */
+ public static final String KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL =
+ "disable_severe_when_extreme_disabled_bool";
+
+ /**
+ * The message expiration time in milliseconds for duplicate detection purposes.
+ * @hide
+ */
+ public static final String KEY_MESSAGE_EXPIRATION_TIME_LONG =
+ "message_expiration_time_long";
+
+ /**
+ * The data call retry configuration for different types of APN.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS =
+ "carrier_data_call_retry_config_strings";
+
+ /**
+ * Delay in milliseconds between trying APN from the pool
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG =
+ "carrier_data_call_apn_delay_default_long";
+
+ /**
+ * Faster delay in milliseconds between trying APN from the pool
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG =
+ "carrier_data_call_apn_delay_faster_long";
+
+ /**
+ * Delay in milliseconds for retrying APN after disconnect
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG =
+ "carrier_data_call_apn_retry_after_disconnect_long";
+
+ /**
+ * Data call setup permanent failure causes by the carrier
+ */
+ public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
+ "carrier_data_call_permanent_failure_strings";
+
+ /**
+ * Default APN types that are metered by the carrier
+ * @hide
+ */
+ public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
+ "carrier_metered_apn_types_strings";
+ /**
+ * Default APN types that are roaming-metered by the carrier
+ * @hide
+ */
+ public static final String KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS =
+ "carrier_metered_roaming_apn_types_strings";
+
+ /**
+ * Default APN types that are metered on IWLAN by the carrier
+ * @hide
+ */
+ public static final String KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS =
+ "carrier_metered_iwlan_apn_types_strings";
+
+ /**
+ * CDMA carrier ERI (Enhanced Roaming Indicator) file name
+ * @hide
+ */
+ public static final String KEY_CARRIER_ERI_FILE_NAME_STRING =
+ "carrier_eri_file_name_string";
+
+ /* The following 3 fields are related to carrier visual voicemail. */
+
+ /**
+ * The carrier number mobile outgoing (MO) sms messages are sent to.
+ */
+ public static final String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
+
+ /**
+ * The port through which the mobile outgoing (MO) sms messages are sent through.
+ */
+ public static final String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
+
+ /**
+ * The type of visual voicemail protocol the carrier adheres to. See {@link TelephonyManager}
+ * for possible values. For example {@link TelephonyManager#VVM_TYPE_OMTP}.
+ */
+ public static final String KEY_VVM_TYPE_STRING = "vvm_type_string";
+
+ /**
+ * Whether cellular data is required to access visual voicemail.
+ */
+ public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL =
+ "vvm_cellular_data_required_bool";
+
+ /**
+ * The default OMTP visual voicemail client prefix to use. Defaulted to "//VVM"
+ */
+ public static final String KEY_VVM_CLIENT_PREFIX_STRING =
+ "vvm_client_prefix_string";
+
+ /**
+ * Whether to use SSL to connect to the visual voicemail IMAP server. Defaulted to false.
+ */
+ public static final String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
+
+ /**
+ * A set of capabilities that should not be used even if it is reported by the visual voicemail
+ * IMAP CAPABILITY command.
+ */
+ public static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY =
+ "vvm_disabled_capabilities_string_array";
+
+ /**
+ * Whether legacy mode should be used when the visual voicemail client is disabled.
+ *
+ * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
+ * the client side all network operations are disabled. SMSs are still monitored so a new
+ * message SYNC SMS will be translated to show a message waiting indicator, like traditional
+ * voicemails.
+ *
+ * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
+ * function without the data cost.
+ */
+ public static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL =
+ "vvm_legacy_mode_enabled_bool";
+
+ /**
+ * Whether to prefetch audio data on new voicemail arrival, defaulted to true.
+ */
+ public static final String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
+
+ /**
+ * The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
+ * and carrier visual voicemail are not active at the same time.
+ *
+ * @deprecated use {@link #KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY}.
+ */
+ @Deprecated
+ public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+
+ /**
+ * A list of the carrier's visual voicemail app package names to ensure that dialer visual
+ * voicemail and carrier visual voicemail are not active at the same time.
+ */
+ public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY =
+ "carrier_vvm_package_name_string_array";
+
+ /**
+ * Flag specifying whether ICCID is showed in SIM Status screen, default to false.
+ */
+ public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
+
+ /**
+ * Flag specifying whether an additional (client initiated) intent needs to be sent on System
+ * update
+ */
+ public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
+
+ /**
+ * Intent to be sent for the additional action on System update
+ */
+ public static final String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING =
+ "ci_action_on_sys_update_intent_string";
+
+ /**
+ * Extra to be included in the intent sent for additional action on System update
+ */
+ public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING =
+ "ci_action_on_sys_update_extra_string";
+
+ /**
+ * Value of extra included in intent sent for additional action on System update
+ */
+ public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING =
+ "ci_action_on_sys_update_extra_val_string";
+
+ /**
+ * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
+ * non-zero value is specified, the UE shall wait for the specified amount of time before it
+ * sends out successive DTMF tones on the network.
+ */
+ public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
+
+ /**
+ * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value
+ * is specified, the UE shall wait for the specified amount of time before it sends out
+ * successive DTMF tones on the network.
+ */
+ public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+
+ /**
+ * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
+ * non-zero value is specified, the UE shall wait for the specified amount of time before it
+ * sends out successive DTMF tones on the network.
+ */
+ public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
+
+ /**
+ * Determines whether conference calls are supported by a carrier. When {@code true},
+ * conference calling is supported, {@code false otherwise}.
+ */
+ public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
+
+ /**
+ * Determines whether a maximum size limit for IMS conference calls is enforced on the device.
+ * When {@code true}, IMS conference calls will be limited to at most
+ * {@link #KEY_IMS_CONFERENCE_SIZE_LIMIT_INT} participants. When {@code false}, no attempt is made
+ * to limit the number of participants in a conference (the carrier will raise an error when an
+ * attempt is made to merge too many participants into a conference).
+ */
+ public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL =
+ "is_ims_conference_size_enforced_bool";
+
+ /**
+ * Determines the maximum number of participants the carrier supports for a conference call.
+ * This number is exclusive of the current device. A conference between 3 devices, for example,
+ * would have a size limit of 2 participants.
+ * Enforced when {@link #KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL} is {@code true}.
+ */
+ public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
+
+ /**
+ * Determines whether High Definition audio property is displayed in the dialer UI.
+ * If {@code false}, remove the HD audio property from the connection so that HD audio related
+ * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
+ */
+ public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL =
+ "display_hd_audio_property_bool";
+
+ /**
+ * Determines whether IMS conference calls are supported by a carrier. When {@code true},
+ * IMS conference calling is supported, {@code false} otherwise.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL =
+ "support_ims_conference_call_bool";
+
+ /**
+ * Determines whether video conference calls are supported by a carrier. When {@code true},
+ * video calls can be merged into conference calls, {@code false} otherwiwse.
+ * <p>
+ * Note: even if video conference calls are not supported, audio calls may be merged into a
+ * conference if {@link #KEY_SUPPORT_CONFERENCE_CALL_BOOL} is {@code true}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL =
+ "support_video_conference_call_bool";
+
+ /**
+ * Determine whether user can toggle Enhanced 4G LTE Mode in Settings.
+ */
+ public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
+
+ /**
+ * Determines whether the Enhanced 4G LTE toggle will be shown in the settings. When this
+ * option is {@code true}, the toggle will be hidden regardless of whether the device and
+ * carrier supports 4G LTE or not.
+ */
+ public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
+
+ /**
+ * Determine whether IMS apn can be shown.
+ */
+ public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
+
+ /**
+ * Determine whether preferred network type can be shown.
+ */
+ public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+
+ /**
+ * String array for package names that need to be enabled for this carrier.
+ * If user has explicitly disabled some packages in the list, won't re-enable.
+ * Other carrier specific apps which are not in this list may be disabled for current carrier,
+ * and only be re-enabled when this config for another carrier includes it.
+ */
+ public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
+
+ /**
+ * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference.
+ * Some operators support Wi-Fi Calling only, not VoLTE.
+ * They don't need "Cellular preferred" option.
+ * In this case, set uneditalbe attribute for preferred preference.
+ * @hide
+ */
+ public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+
+ /**
+ * Flag to indicate if Wi-Fi needs to be disabled in ECBM
+ * @hide
+ **/
+ public static final String
+ KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+
+ /**
+ * List operator-specific error codes and indices of corresponding error strings in
+ * wfcOperatorErrorAlertMessages and wfcOperatorErrorNotificationMessages.
+ *
+ * Example: "REG09|0" specifies error code "REG09" and index "0". This index will be
+ * used to find alert and notification messages in wfcOperatorErrorAlertMessages and
+ * wfcOperatorErrorNotificationMessages.
+ *
+ * @hide
+ */
+ public static final String KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY =
+ "wfc_operator_error_codes_string_array";
+
+ /**
+ * Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats.
+ * @hide
+ */
+ public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
+ /** @hide */
+ public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
+
+ /**
+ * The Component Name of the activity that can setup the emergency addrees for WiFi Calling
+ * as per carrier requirement.
+ * @hide
+ */
+ public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING =
+ "wfc_emergency_address_carrier_app_string";
+
+ /**
+ * Boolean to decide whether to use #KEY_CARRIER_NAME_STRING from CarrierConfig app.
+ * @hide
+ */
+ public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
+
+ /**
+ * String to identify carrier name in CarrierConfig app. This string is used only if
+ * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true
+ * @hide
+ */
+ public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
+
+ /**
+ * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
+ * prevent manual operator selection. If false, this SIM setting will be ignored and manual
+ * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
+ * information
+ */
+ public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+
+ /**
+ * Allow user to add APNs
+ */
+ public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+
+ /**
+ * APN types that user is not allowed to modify
+ * @hide
+ */
+ public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY =
+ "read_only_apn_types_string_array";
+
+ /**
+ * APN fields that user is not allowed to modify
+ * @hide
+ */
+ public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY =
+ "read_only_apn_fields_string_array";
+
+ /**
+ * Boolean indicating if intent for emergency call state changes should be broadcast
+ * @hide
+ */
+ public static final String KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL =
+ "broadcast_emergency_call_state_changes_bool";
+
+ /**
+ * Indicates whether STK LAUNCH_BROWSER command is disabled.
+ * If {@code true}, then the browser will not be launched
+ * on UI for the LAUNCH_BROWSER STK command.
+ * @hide
+ */
+ public static final String KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL =
+ "stk_disable_launch_browser_bool";
+
+
+ // These variables are used by the MMS service and exposed through another API, {@link
+ // SmsManager}. The variable names and string values are copied from there.
+ public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
+ public static final String KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL = "allowAttachAudio";
+ public static final String KEY_MMS_APPEND_TRANSACTION_ID_BOOL = "enabledTransID";
+ public static final String KEY_MMS_GROUP_MMS_ENABLED_BOOL = "enableGroupMms";
+ public static final String KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL = "enableMMSDeliveryReports";
+ public static final String KEY_MMS_MMS_ENABLED_BOOL = "enabledMMS";
+ public static final String KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL = "enableMMSReadReports";
+ public static final String KEY_MMS_MULTIPART_SMS_ENABLED_BOOL = "enableMultipartSMS";
+ public static final String KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL = "enabledNotifyWapMMSC";
+ public static final String KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL = "sendMultipartSmsAsSeparateMessages";
+ public static final String KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL = "config_cellBroadcastAppLinks";
+ public static final String KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL = "enableSMSDeliveryReports";
+ public static final String KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL = "supportHttpCharsetHeader";
+ public static final String KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL = "supportMmsContentDisposition";
+ public static final String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
+ public static final String KEY_MMS_ALIAS_MIN_CHARS_INT = "aliasMinChars";
+ public static final String KEY_MMS_HTTP_SOCKET_TIMEOUT_INT = "httpSocketTimeout";
+ public static final String KEY_MMS_MAX_IMAGE_HEIGHT_INT = "maxImageHeight";
+ public static final String KEY_MMS_MAX_IMAGE_WIDTH_INT = "maxImageWidth";
+ public static final String KEY_MMS_MAX_MESSAGE_SIZE_INT = "maxMessageSize";
+ public static final String KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT = "maxMessageTextSize";
+ public static final String KEY_MMS_RECIPIENT_LIMIT_INT = "recipientLimit";
+ public static final String KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT = "smsToMmsTextLengthThreshold";
+ public static final String KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT = "smsToMmsTextThreshold";
+ public static final String KEY_MMS_SUBJECT_MAX_LENGTH_INT = "maxSubjectLength";
+ public static final String KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING = "emailGatewayNumber";
+ public static final String KEY_MMS_HTTP_PARAMS_STRING = "httpParams";
+ public static final String KEY_MMS_NAI_SUFFIX_STRING = "naiSuffix";
+ public static final String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
+ public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
+ public static final String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ /** @hide */
+ public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
+
+ /**
+ * If carriers require differentiate un-provisioned status: cold sim or out of credit sim
+ * a package name and activity name can be provided to launch a supported carrier application
+ * that check the sim provisioning status
+ * The first element is the package name and the second element is the activity name
+ * of the provisioning app
+ * example:
+ * <item>com.google.android.carrierPackageName</item>
+ * <item>com.google.android.carrierPackageName.CarrierActivityName</item>
+ * The ComponentName of the carrier activity that can setup the device and activate with the
+ * network as part of the Setup Wizard flow.
+ * @hide
+ */
+ public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
+
+ /**
+ * Defines carrier-specific actions which act upon
+ * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the
+ * default carrier app
+ * Format: "CARRIER_ACTION_IDX, ..."
+ * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * Example:
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS
+ * disable_metered_apns}
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY =
+ "carrier_default_actions_on_redirection_string_array";
+
+ /**
+ * Defines carrier-specific actions which act upon
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * and configured signal args:
+ * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType},
+ * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode}
+ * used for customization of the default carrier app
+ * Format:
+ * {
+ * "APN_1, ERROR_CODE_1 : CARRIER_ACTION_IDX_1, CARRIER_ACTION_IDX_2...",
+ * "APN_1, ERROR_CODE_2 : CARRIER_ACTION_IDX_1 "
+ * }
+ * Where {@code APN_1} is a string defined in
+ * {@link com.android.internal.telephony.PhoneConstants PhoneConstants}
+ * Example: "default"
+ *
+ * {@code ERROR_CODE_1} is an integer defined in
+ * {@link com.android.internal.telephony.dataconnection.DcFailCause DcFailure}
+ * Example:
+ * {@link com.android.internal.telephony.dataconnection.DcFailCause#MISSING_UNKNOWN_APN}
+ *
+ * {@code CARRIER_ACTION_IDX_1} is an integer defined in
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * Example:
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS}
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY =
+ "carrier_default_actions_on_dcfailure_string_array";
+
+ /**
+ * Defines carrier-specific actions which act upon
+ * com.android.internal.telephony.CARRIER_SIGNAL_RESET, used for customization of the
+ * default carrier app
+ * Format: "CARRIER_ACTION_IDX, ..."
+ * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * Example:
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils
+ * #CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS clear all notifications on reset}
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET =
+ "carrier_default_actions_on_reset_string_array";
+
+ /**
+ * Defines carrier-specific actions which act upon
+ * com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+ * used for customization of the default carrier app
+ * Format:
+ * {
+ * "true : CARRIER_ACTION_IDX_1",
+ * "false: CARRIER_ACTION_IDX_2"
+ * }
+ * Where {@code true} is a boolean indicates default network available/unavailable
+ * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * Example:
+ * {@link com.android.carrierdefaultapp.CarrierActionUtils
+ * #CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER enable the app as the default URL handler}
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE =
+ "carrier_default_actions_on_default_network_available_string_array";
+ /**
+ * Defines a list of acceptable redirection url for default carrier app
+ * @hides
+ */
+ public static final String KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY =
+ "carrier_default_redirection_url_string_array";
+
+ /**
+ * Each config includes the componentName of the carrier app, followed by a list of interesting
+ * signals(declared in the manifest) which could wake up the app.
+ * @see com.android.internal.telephony.TelephonyIntents
+ * Example:
+ * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
+ * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED,
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
+ * </item>
+ * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
+ * </item>
+ * @hide
+ */
+ public static final String KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+ "carrier_app_wake_signal_config";
+
+ /**
+ * Each config includes the componentName of the carrier app, followed by a list of interesting
+ * signals for the app during run-time. The list of signals(intents) are targeting on run-time
+ * broadcast receivers only, aiming to avoid unnecessary wake-ups and should not be declared in
+ * the app's manifest.
+ * @see com.android.internal.telephony.TelephonyIntents
+ * Example:
+ * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
+ * </item>
+ * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * </item>
+ * @hide
+ */
+ public static final String KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+ "carrier_app_no_wake_signal_config";
+
+ /**
+ * Default value for {@link Settings.Global#DATA_ROAMING}
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL =
+ "carrier_default_data_roaming_enabled_bool";
+
+ /**
+ * Determines whether the carrier supports making non-emergency phone calls while the phone is
+ * in emergency callback mode. Default value is {@code true}, meaning that non-emergency calls
+ * are allowed in emergency callback mode.
+ */
+ public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL =
+ "allow_non_emergency_calls_in_ecm_bool";
+
+ /**
+ * Flag indicating whether to allow carrier video calls to emergency numbers.
+ * When {@code true}, video calls to emergency numbers will be allowed. When {@code false},
+ * video calls to emergency numbers will be initiated as audio-only calls instead.
+ */
+ public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL =
+ "allow_emergency_video_calls_bool";
+
+ /**
+ * Flag indicating whether the carrier supports RCS presence indication for video calls. When
+ * {@code true}, the carrier supports RCS presence indication for video calls. When presence
+ * is supported, the device should use the
+ * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
+ * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
+ * whether each contact supports video calling. The UI is made aware that presence is enabled
+ * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
+ * and can choose to hide or show the video calling icon based on whether a contact supports
+ * video.
+ */
+ public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+
+ /**
+ * The duration in seconds that platform call and message blocking is disabled after the user
+ * contacts emergency services. Platform considers values in the range 0 to 604800 (one week) as
+ * valid. See {@link android.provider.BlockedNumberContract#isBlocked(Context, String)}).
+ */
+ public static final String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT =
+ "duration_blocking_disabled_after_emergency_int";
+
+ /**
+ * For carriers which require an empty flash to be sent before sending the normal 3-way calling
+ * flash, the duration in milliseconds of the empty flash to send. When {@code 0}, no empty
+ * flash is sent.
+ */
+ public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
+
+
+ /**
+ * @hide
+ * The default value for preferred CDMA roaming mode (aka CDMA system select.)
+ * CDMA_ROAMING_MODE_RADIO_DEFAULT = the default roaming mode from the radio
+ * CDMA_ROAMING_MODE_HOME = Home Networks
+ * CDMA_ROAMING_MODE_AFFILIATED = Roaming on Affiliated networks
+ * CDMA_ROAMING_MODE_ANY = Roaming on any networks
+ */
+ public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
+ /** @hide */
+ public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1;
+ /** @hide */
+ public static final int CDMA_ROAMING_MODE_HOME = 0;
+ /** @hide */
+ public static final int CDMA_ROAMING_MODE_AFFILIATED = 1;
+ /** @hide */
+ public static final int CDMA_ROAMING_MODE_ANY = 2;
+ /**
+ * Boolean indicating if support is provided for directly dialing FDN number from FDN list.
+ * If false, this feature is not supported.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL =
+ "support_direct_fdn_dialing_bool";
+
+ /**
+ * Report IMEI as device id even if it's a CDMA/LTE phone.
+ *
+ * @hide
+ */
+ public static final String KEY_FORCE_IMEI_BOOL = "force_imei_bool";
+
+ /**
+ * The families of Radio Access Technologies that will get clustered and ratcheted,
+ * ie, we will report transitions up within the family, but not down until we change
+ * cells. This prevents flapping between base technologies and higher techs that are
+ * granted on demand within the cell.
+ * @hide
+ */
+ public static final String KEY_RATCHET_RAT_FAMILIES =
+ "ratchet_rat_families";
+
+ /**
+ * Flag indicating whether some telephony logic will treat a call which was formerly a video
+ * call as if it is still a video call. When {@code true}:
+ * <p>
+ * Logic which will automatically drop a video call which takes place over WIFI when a
+ * voice call is answered (see {@link #KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL}.
+ * <p>
+ * Logic which determines whether the user can use TTY calling.
+ */
+ public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL =
+ "treat_downgraded_video_calls_as_video_calls_bool";
+
+ /**
+ * When {@code true}, if the user is in an ongoing video call over WIFI and answers an incoming
+ * audio call, the video call will be disconnected before the audio call is answered. This is
+ * in contrast to the usual expected behavior where a foreground video call would be put into
+ * the background and held when an incoming audio call is answered.
+ */
+ public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL =
+ "drop_video_call_when_answering_audio_call_bool";
+
+ /**
+ * Flag indicating whether the carrier supports merging wifi calls when VoWIFI is disabled.
+ * This can happen in the case of a carrier which allows offloading video calls to WIFI
+ * separately of whether voice over wifi is enabled. In such a scenario when two video calls
+ * are downgraded to voice, they remain over wifi. However, if VoWIFI is disabled, these calls
+ * cannot be merged.
+ */
+ public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL =
+ "allow_merge_wifi_calls_when_vowifi_off_bool";
+
+ /**
+ * Flag indicating whether the carrier supports the Hold command while in an IMS call.
+ * <p>
+ * The device configuration value {@code config_device_respects_hold_carrier_config} ultimately
+ * controls whether this carrier configuration option is used. Where
+ * {@code config_device_respects_hold_carrier_config} is false, the value of the
+ * {@link #KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} carrier configuration option is ignored.
+ * @hide
+ */
+ public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+
+ /**
+ * When true, indicates that adding a call is disabled when there is an ongoing video call
+ * or when there is an ongoing call on wifi which was downgraded from video and VoWifi is
+ * turned off.
+ */
+ public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL =
+ "allow_add_call_during_video_call";
+
+ /**
+ * When true, indicates that the HD audio icon in the in-call screen should not be shown for
+ * VoWifi calls.
+ * @hide
+ */
+ public static final String KEY_WIFI_CALLS_CAN_BE_HD_AUDIO = "wifi_calls_can_be_hd_audio";
+
+ /**
+ * When true, indicates that the HD audio icon in the in-call screen should not be shown for
+ * video calls.
+ * @hide
+ */
+ public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
+
+ /**
+ * Whether system apps are allowed to use fallback if carrier video call is not available.
+ * Defaults to {@code true}.
+ *
+ * @hide
+ */
+ public static final String KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL =
+ "allow_video_calling_fallback_bool";
+
+ /**
+ * Defines operator-specific {@link com.android.ims.ImsReasonInfo} mappings.
+ *
+ * Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
+ * Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
+ * {@code MESSAGE} corresponds to an expected {@link ImsReasonInfo#getExtraMessage()} string,
+ * and {@code NEW_CODE} is the new {@code ImsReasonInfo#CODE_*} which this combination of
+ * original code and message shall be remapped to.
+ *
+ * Note: If {@code *} is specified for the original code, any ImsReasonInfo with the matching
+ * {@code MESSAGE} will be remapped to {@code NEW_CODE}.
+ *
+ * Example: "501|call completion elsewhere|1014"
+ * When the {@link ImsReasonInfo#getCode()} is {@link ImsReasonInfo#CODE_USER_TERMINATED} and
+ * the {@link ImsReasonInfo#getExtraMessage()} is {@code "call completion elsewhere"},
+ * {@link ImsReasonInfo#CODE_ANSWERED_ELSEWHERE} shall be used as the {@link ImsReasonInfo}
+ * code instead.
+ * @hide
+ */
+ public static final String KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY =
+ "ims_reasoninfo_mapping_string_array";
+
+ /**
+ * When {@code false}, use default title for Enhanced 4G LTE Mode settings.
+ * When {@code true}, use the variant.
+ * @hide
+ */
+ public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
+ "enhanced_4g_lte_title_variant_bool";
+
+ /**
+ * Indicates whether the carrier wants to notify the user when handover of an LTE video call to
+ * WIFI fails.
+ * <p>
+ * When {@code true}, if a video call starts on LTE and the modem reports a failure to handover
+ * the call to WIFI or if no handover success is reported within 60 seconds of call initiation,
+ * the {@link android.telephony.TelephonyManager#EVENT_HANDOVER_TO_WIFI_FAILED} event is raised
+ * on the connection.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL =
+ "notify_vt_handover_to_wifi_failure_bool";
+
+ /**
+ * A upper case list of CNAP names that are unhelpful to the user for distinguising calls and
+ * should be filtered out of the CNAP information. This includes CNAP names such as "WIRELESS
+ * CALLER" or "UNKNOWN NAME". By default, if there are no filtered names for this carrier, null
+ * is returned.
+ * @hide
+ */
+ public static final String KEY_FILTERED_CNAP_NAMES_STRING_ARRAY = "filtered_cnap_names_string_array";
+
+ /**
+ * The RCS configuration server URL. This URL is used to initiate RCS provisioning.
+ */
+ public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
+
+ /**
+ * Determine whether user can change Wi-Fi Calling preference in roaming.
+ * {@code false} - roaming preference {@link KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT} is
+ * the same as home preference {@link KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT}
+ * and cannot be changed.
+ * {@code true} - roaming preference can be changed by user independently.
+ *
+ * @hide
+ */
+ public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
+ "editable_wfc_roaming_mode_bool";
+
+ /**
+ * Determine whether current lpp_mode used for E-911 needs to be kept persistently.
+ * {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
+ * when sim is not presented.
+ * {@code true} - current lpp_profile of carrier will be kepted persistently
+ * even after sim is removed.
+ *
+ * @hide
+ */
+ public static final String KEY_PERSIST_LPP_MODE_BOOL = "persist_lpp_mode_bool";
+
+ /**
+ * Carrier specified WiFi networks.
+ * @hide
+ */
+ public static final String KEY_CARRIER_WIFI_STRING_ARRAY = "carrier_wifi_string_array";
+
+ /**
+ * Time delay (in ms) after which we show the notification to switch the preferred
+ * network.
+ * @hide
+ */
+ public static final String KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT =
+ "network_notification_delay_int";
+
+ /**
+ * Time delay (in ms) after which we show the notification for emergency calls,
+ * while the device is registered over WFC. Default value is -1, which indicates
+ * that this notification is not pertinent for a particular carrier. We've added a delay
+ * to prevent false positives.
+ * @hide
+ */
+ public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT =
+ "emergency_notification_delay_int";
+
+ /**
+ * When {@code true}, the carrier allows the user of the
+ * {@link TelephonyManager#sendUssdRequest(String, TelephonyManager.UssdResponseCallback,
+ * Handler)} API to perform USSD requests. {@code True} by default.
+ * @hide
+ */
+ public static final String KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL =
+ "allow_ussd_requests_via_telephony_manager_bool";
+
+ /**
+ * Indicates whether the carrier supports 3gpp call forwarding MMI codes while roaming. If
+ * false, the user will be notified that call forwarding is not available when the MMI code
+ * fails.
+ */
+ public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+ "support_3gpp_call_forwarding_while_roaming_bool";
+
+ /**
+ * When {@code true}, the user will be notified when they attempt to place an international call
+ * when the call is placed using wifi calling.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL =
+ "notify_international_call_on_wfc_bool";
+
+ /**
+ * An array containing custom call forwarding number prefixes that will be blocked while the
+ * device is reporting that it is roaming. By default, there are no custom call
+ * forwarding prefixes and none of these numbers will be filtered. If one or more entries are
+ * present, the system will not complete the call and display an error message.
+ *
+ * To display a message to the user when call forwarding fails for 3gpp MMI codes while roaming,
+ * use the {@link #KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL} option instead.
+ */
+ public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY =
+ "call_forwarding_blocks_while_roaming_string_array";
+
+ /**
+ * The day of the month (1-31) on which the data cycle rolls over.
+ * <p>
+ * If the current month does not have this day, the cycle will roll over at
+ * the start of the next month.
+ * <p>
+ * This setting may be still overridden by explicit user choice. By default,
+ * the platform value will be used.
+ */
+ public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT =
+ "monthly_data_cycle_day_int";
+
+ /**
+ * When {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG},
+ * or {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} are set to this value, the platform default
+ * value will be used for that key.
+ *
+ * @hide
+ */
+ @Deprecated
+ public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1;
+
+ /**
+ * Flag indicating that a data cycle threshold should be disabled.
+ * <p>
+ * If {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+ * default data warning, if one exists, will be disabled. A user selected data warning will not
+ * be overridden.
+ * <p>
+ * If {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+ * default data limit, if one exists, will be disabled. A user selected data limit will not be
+ * overridden.
+ */
+ public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2;
+
+ /**
+ * Controls the data usage warning.
+ * <p>
+ * If the user uses more than this amount of data in their billing cycle, as defined by
+ * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, the user will be alerted about the usage.
+ * If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data usage warning will
+ * be disabled.
+ * <p>
+ * This setting may be overridden by explicit user choice. By default, the platform value
+ * will be used.
+ */
+ public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG =
+ "data_warning_threshold_bytes_long";
+
+ /**
+ * Controls the cellular data limit.
+ * <p>
+ * If the user uses more than this amount of data in their billing cycle, as defined by
+ * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, cellular data will be turned off by the user's
+ * phone. If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data limit will be
+ * disabled.
+ * <p>
+ * This setting may be overridden by explicit user choice. By default, the platform value
+ * will be used.
+ */
+ public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG =
+ "data_limit_threshold_bytes_long";
+
+ /**
+ * Offset to be reduced from rsrp threshold while calculating signal strength level.
+ * @hide
+ */
+ public static final String KEY_LTE_EARFCNS_RSRP_BOOST_INT = "lte_earfcns_rsrp_boost_int";
+
+ /**
+ * List of EARFCN (E-UTRA Absolute Radio Frequency Channel Number,
+ * Reference: 3GPP TS 36.104 5.4.3) inclusive ranges on which lte_rsrp_boost_int
+ * will be applied. Format of the String array is expected to be {"erafcn1_start-earfcn1_end",
+ * "earfcn2_start-earfcn2_end" ... }
+ * @hide
+ */
+ public static final String KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY =
+ "boosted_lte_earfcns_string_array";
+
+ /**
+ * Key identifying if voice call barring notification is required to be shown to the user.
+ * @hide
+ */
+ public static final String KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL =
+ "disable_voice_barring_notification_bool";
+
+ /**
+ * List of operators considered non-roaming which won't show roaming icon.
+ * <p>
+ * Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+ * If operators, 21404 and 21407, make roaming agreements, users of 21404 should not see
+ * the roaming icon as using 21407 network.
+ * @hide
+ */
+ public static final String KEY_NON_ROAMING_OPERATOR_STRING_ARRAY =
+ "non_roaming_operator_string_array";
+
+ /**
+ * List of operators considered roaming with the roaming icon.
+ * <p>
+ * Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+ * If operators, 21404 and 21407, make roaming agreements, users of 21404 should see
+ * the roaming icon as using 21407 network.
+ * <p>
+ * A match on this supersedes a match on {@link #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
+ * @hide
+ */
+ public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY =
+ "roaming_operator_string_array";
+
+ /**
+ * URL from which the proto containing the public key of the Carrier used for
+ * IMSI encryption will be downloaded.
+ * @hide
+ */
+ public static final String IMSI_KEY_DOWNLOAD_URL_STRING = "imsi_key_download_url_string";
+
+ /**
+ * Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask.
+ * 0 indicates that neither EPDG or WLAN is enabled.
+ * 1 indicates that key type {@link TelephonyManager#KEY_TYPE_EPDG} is enabled.
+ * 2 indicates that key type {@link TelephonyManager#KEY_TYPE_WLAN} is enabled.
+ * 3 indicates that both are enabled.
+ * @hide
+ */
+ public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
+
+
+ /**
+ * Key identifying if the CDMA Caller ID presentation and suppression MMI codes
+ * should be converted to 3GPP CLIR codes when a multimode (CDMA+UMTS+LTE) device is roaming
+ * on a 3GPP network. Specifically *67<number> will be converted to #31#<number> and
+ * *82<number> will be converted to *31#<number> before dialing a call when this key is
+ * set TRUE and device is roaming on a 3GPP network.
+ * @hide
+ */
+ public static final String KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL =
+ "convert_cdma_caller_id_mmi_codes_while_roaming_on_3gpp_bool";
+
+ /**
+ * Flag specifying whether IMS registration state menu is shown in Status Info setting,
+ * default to false.
+ * @hide
+ */
+ public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL =
+ "show_ims_registration_status_bool";
+
+ /** The default value for every variable. */
+ private final static PersistableBundle sDefaults;
+
+ static {
+ sDefaults = new PersistableBundle();
+ sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
+ sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
+ sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+ sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
+ sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
+ sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
+ sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
+ sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL, false);
+ sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
+ sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
+ sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+ sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
+ sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
+ sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
+ sDefaults.putInt(KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT, 64);
+ sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
+ sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
+ sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
+
+ sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+ sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
+ sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
+ sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+ sDefaults.putBoolean(KEY_PREFER_2G_BOOL, true);
+ sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
+ sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
+ sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL, true);
+ sDefaults.putBoolean(KEY_USE_HFA_FOR_PROVISIONING_BOOL, false);
+ sDefaults.putBoolean(KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL, false);
+ sDefaults.putBoolean(KEY_USE_OTASP_FOR_PROVISIONING_BOOL, false);
+ sDefaults.putBoolean(KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL, false);
+ sDefaults.putBoolean(KEY_VOICE_PRIVACY_DISABLE_UI_BOOL, false);
+ sDefaults.putBoolean(KEY_WORLD_PHONE_BOOL, false);
+ sDefaults.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+ sDefaults.putBoolean(KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL, false);
+ sDefaults.putInt(KEY_VOLTE_REPLACEMENT_RAT_INT, 0);
+ sDefaults.putString(KEY_DEFAULT_SIM_CALL_MANAGER_STRING, "");
+ sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
+ sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
+ sDefaults.putString(KEY_VVM_TYPE_STRING, "");
+ sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
+ sDefaults.putString(KEY_VVM_CLIENT_PREFIX_STRING,"//VVM");
+ sDefaults.putBoolean(KEY_VVM_SSL_ENABLED_BOOL,false);
+ sDefaults.putStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_VVM_LEGACY_MODE_ENABLED_BOOL,false);
+ sDefaults.putBoolean(KEY_VVM_PREFETCH_BOOL, true);
+ sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
+ sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
+ sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
+ sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
+ sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
+ sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
+ sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
+ sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, new String[] {"dun"});
+ sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
+ sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
+ sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true);
+ sDefaults.putLong(KEY_MESSAGE_EXPIRATION_TIME_LONG, 86400000L);
+ sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
+ "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+ "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+ "others:max_retries=3, 5000, 5000, 5000"});
+ sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
+ sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
+ sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 10000);
+ sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
+ sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
+ sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[]{"default", "mms", "dun", "supl"});
+ sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
+ new String[]{"default", "mms", "dun", "supl"});
+ // By default all APNs are unmetered if the device is on IWLAN.
+ sDefaults.putStringArray(KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+ new String[]{});
+
+ sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
+ new int[]{
+ 4, /* IS95A */
+ 5, /* IS95B */
+ 6, /* 1xRTT */
+ 7, /* EVDO_0 */
+ 8, /* EVDO_A */
+ 12 /* EVDO_B */
+ });
+ sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
+ sDefaults.putString(KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+ sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
+ sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0);
+ sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
+ sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
+ sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
+ sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
+ sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
+ sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
+ sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
+ sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
+ sDefaults.putBoolean(KEY_HIDE_ENHANCED_4G_LTE_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
+ sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
+ sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL, false);
+ sDefaults.putStringArray(KEY_ENABLE_APPS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
+ sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
+ sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
+ sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+ sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
+ sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
+ sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+ sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
+
+ // MMS defaults
+ sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_APPEND_TRANSACTION_ID_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_GROUP_MMS_ENABLED_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_MMS_ENABLED_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_MULTIPART_SMS_ENABLED_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL, false);
+ sDefaults.putBoolean(KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL, true);
+ sDefaults.putBoolean(KEY_MMS_CLOSE_CONNECTION_BOOL, false);
+ sDefaults.putInt(KEY_MMS_ALIAS_MAX_CHARS_INT, 48);
+ sDefaults.putInt(KEY_MMS_ALIAS_MIN_CHARS_INT, 2);
+ sDefaults.putInt(KEY_MMS_HTTP_SOCKET_TIMEOUT_INT, 60 * 1000);
+ sDefaults.putInt(KEY_MMS_MAX_IMAGE_HEIGHT_INT, 480);
+ sDefaults.putInt(KEY_MMS_MAX_IMAGE_WIDTH_INT, 640);
+ sDefaults.putInt(KEY_MMS_MAX_MESSAGE_SIZE_INT, 300 * 1024);
+ sDefaults.putInt(KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT, -1);
+ sDefaults.putInt(KEY_MMS_RECIPIENT_LIMIT_INT, Integer.MAX_VALUE);
+ sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT, -1);
+ sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT, -1);
+ sDefaults.putInt(KEY_MMS_SUBJECT_MAX_LENGTH_INT, 40);
+ sDefaults.putString(KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, "");
+ sDefaults.putString(KEY_MMS_HTTP_PARAMS_STRING, "");
+ sDefaults.putString(KEY_MMS_NAI_SUFFIX_STRING, "");
+ sDefaults.putString(KEY_MMS_UA_PROF_TAG_NAME_STRING, "x-wap-profile");
+ sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
+ sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
+ sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
+ sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
+ sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false);
+ sDefaults.putInt(KEY_CDMA_ROAMING_MODE_INT, CDMA_ROAMING_MODE_RADIO_DEFAULT);
+ sDefaults.putString(KEY_RCS_CONFIG_SERVER_URL_STRING, "");
+
+ // Carrier Signalling Receivers
+ sDefaults.putString(KEY_CARRIER_SETUP_APP_STRING, "");
+ sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ new String[]{
+ "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:"
+ + "com.android.internal.telephony.CARRIER_SIGNAL_RESET"
+ });
+ sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
+
+
+ // Default carrier app configurations
+ sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY,
+ new String[]{
+ "9, 4, 1"
+ //9: CARRIER_ACTION_REGISTER_NETWORK_AVAIL
+ //4: CARRIER_ACTION_DISABLE_METERED_APNS
+ //1: CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
+ });
+ sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET, new String[]{
+ "6, 8"
+ //6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
+ //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+ });
+ sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE,
+ new String[] {
+ String.valueOf(false) + ": 7",
+ //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+ String.valueOf(true) + ": 8"
+ //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+ });
+ sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
+
+ sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+ sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+ sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+
+ // Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
+ // {LTE, LTE_CA}
+ // Order is important - lowest precidence first
+ sDefaults.putStringArray(KEY_RATCHET_RAT_FAMILIES,
+ new String[]{"1,2","7,8,12","3,11,9,10,15","14,19"});
+ sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, false);
+ sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
+ sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
+ sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+ sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
+
+ sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+ sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
+ sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
+ sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
+ sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, false);
+ sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
+ sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
+ sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
+ sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+ sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+ sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
+ null);
+ sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
+ sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
+ sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0);
+ sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
+ sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL,
+ false);
+ sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
+ sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
+ }
+
+ /**
+ * Gets the configuration values for a particular subscription, which is associated with a
+ * specific SIM card. If an invalid subId is used, the returned config will contain default
+ * values.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+ * @return A {@link PersistableBundle} containing the config for the given subId, or default
+ * values for an invalid subId.
+ */
+ @Nullable
+ public PersistableBundle getConfigForSubId(int subId) {
+ try {
+ ICarrierConfigLoader loader = getICarrierConfigLoader();
+ if (loader == null) {
+ Rlog.w(TAG, "Error getting config for subId " + subId
+ + " ICarrierConfigLoader is null");
+ return null;
+ }
+ return loader.getConfigForSubId(subId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "Error getting config for subId " + subId + ": "
+ + ex.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Gets the configuration values for the default subscription.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @see #getConfigForSubId
+ */
+ @Nullable
+ public PersistableBundle getConfig() {
+ return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
+ }
+
+ /**
+ * Calling this method triggers telephony services to fetch the current carrier configuration.
+ * <p>
+ * Normally this does not need to be called because the platform reloads config on its own.
+ * This should be called by a carrier service app if it wants to update config at an arbitrary
+ * moment.
+ * </p>
+ * <p>Requires that the calling app has carrier privileges.
+ * <p>
+ * This method returns before the reload has completed, and
+ * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
+ * arbitrary thread.
+ * </p>
+ * @see #hasCarrierPrivileges
+ */
+ public void notifyConfigChangedForSubId(int subId) {
+ try {
+ ICarrierConfigLoader loader = getICarrierConfigLoader();
+ if (loader == null) {
+ Rlog.w(TAG, "Error reloading config for subId=" + subId
+ + " ICarrierConfigLoader is null");
+ return;
+ }
+ loader.notifyConfigChangedForSubId(subId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
+ }
+ }
+
+ /**
+ * Request the carrier config loader to update the cofig for phoneId.
+ * <p>
+ * Depending on simState, the config may be cleared or loaded from config app. This is only used
+ * by SubscriptionInfoUpdater.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void updateConfigForPhoneId(int phoneId, String simState) {
+ try {
+ ICarrierConfigLoader loader = getICarrierConfigLoader();
+ if (loader == null) {
+ Rlog.w(TAG, "Error updating config for phoneId=" + phoneId
+ + " ICarrierConfigLoader is null");
+ return;
+ }
+ loader.updateConfigForPhoneId(phoneId, simState);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex.toString());
+ }
+ }
+
+ /** {@hide} */
+ public String getDefaultCarrierServicePackageName() {
+ try {
+ return getICarrierConfigLoader().getDefaultCarrierServicePackageName();
+ } catch (Throwable t) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a new bundle with the default value for every supported configuration variable.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public static PersistableBundle getDefaultConfig() {
+ return new PersistableBundle(sDefaults);
+ }
+
+ /** @hide */
+ @Nullable
+ private ICarrierConfigLoader getICarrierConfigLoader() {
+ return ICarrierConfigLoader.Stub
+ .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
+ }
+}
diff --git a/android/telephony/CarrierMessagingServiceManager.java b/android/telephony/CarrierMessagingServiceManager.java
new file mode 100644
index 00000000..768d97bc
--- /dev/null
+++ b/android/telephony/CarrierMessagingServiceManager.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.service.carrier.CarrierMessagingService;
+import android.service.carrier.ICarrierMessagingService;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Provides basic structure for platform to connect to the carrier messaging service.
+ * <p>
+ * <code>
+ * CarrierMessagingServiceManager carrierMessagingServiceManager =
+ * new CarrierMessagingServiceManagerImpl();
+ * if (carrierMessagingServiceManager.bindToCarrierMessagingService(context, carrierPackageName)) {
+ * // wait for onServiceReady callback
+ * } else {
+ * // Unable to bind: handle error.
+ * }
+ * </code>
+ * <p> Upon completion {@link #disposeConnection} should be called to unbind the
+ * CarrierMessagingService.
+ * @hide
+ */
+public abstract class CarrierMessagingServiceManager {
+ // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
+ // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
+ private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
+
+ /**
+ * Binds to the carrier messaging service under package {@code carrierPackageName}. This method
+ * should be called exactly once.
+ *
+ * @param context the context
+ * @param carrierPackageName the carrier package name
+ * @return true upon successfully binding to a carrier messaging service, false otherwise
+ */
+ public boolean bindToCarrierMessagingService(Context context, String carrierPackageName) {
+ Preconditions.checkState(mCarrierMessagingServiceConnection == null);
+
+ Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
+ intent.setPackage(carrierPackageName);
+ mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
+ return context.bindService(intent, mCarrierMessagingServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Unbinds the carrier messaging service. This method should be called exactly once.
+ *
+ * @param context the context
+ */
+ public void disposeConnection(Context context) {
+ Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
+ context.unbindService(mCarrierMessagingServiceConnection);
+ mCarrierMessagingServiceConnection = null;
+ }
+
+ /**
+ * Implemented by subclasses to use the carrier messaging service once it is ready.
+ *
+ * @param carrierMessagingService the carrier messaing service interface
+ */
+ protected abstract void onServiceReady(ICarrierMessagingService carrierMessagingService);
+
+ /**
+ * A basic {@link ServiceConnection}.
+ */
+ private final class CarrierMessagingServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }
+}
diff --git a/android/telephony/CellBroadcastMessage.java b/android/telephony/CellBroadcastMessage.java
new file mode 100644
index 00000000..cbb9e342
--- /dev/null
+++ b/android/telephony/CellBroadcastMessage.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.telephony;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.text.format.DateUtils;
+
+/**
+ * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that
+ * decoded broadcast message objects can be passed between running Services.
+ * New broadcasts are received by the CellBroadcastReceiver app, which exports
+ * the database of previously received broadcasts at "content://cellbroadcasts/".
+ * The "android.permission.READ_CELL_BROADCASTS" permission is required to read
+ * from the ContentProvider, and writes to the database are not allowed.<p>
+ *
+ * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows
+ * in the database cursor returned by the ContentProvider.
+ *
+ * {@hide}
+ */
+public class CellBroadcastMessage implements Parcelable {
+
+ /** Identifier for getExtra() when adding this object to an Intent. */
+ public static final String SMS_CB_MESSAGE_EXTRA =
+ "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE";
+
+ /** SmsCbMessage. */
+ private final SmsCbMessage mSmsCbMessage;
+
+ private final long mDeliveryTime;
+ private boolean mIsRead;
+
+ /**
+ * Indicates the subId
+ *
+ * @hide
+ */
+ private int mSubId = 0;
+
+ /**
+ * set Subscription information
+ *
+ * @hide
+ */
+ public void setSubId(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * get Subscription information
+ *
+ * @hide
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ public CellBroadcastMessage(SmsCbMessage message) {
+ mSmsCbMessage = message;
+ mDeliveryTime = System.currentTimeMillis();
+ mIsRead = false;
+ }
+
+ private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) {
+ mSmsCbMessage = message;
+ mDeliveryTime = deliveryTime;
+ mIsRead = isRead;
+ }
+
+ private CellBroadcastMessage(Parcel in) {
+ mSmsCbMessage = new SmsCbMessage(in);
+ mDeliveryTime = in.readLong();
+ mIsRead = (in.readInt() != 0);
+ mSubId = in.readInt();
+ }
+
+ /** Parcelable: no special flags. */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ mSmsCbMessage.writeToParcel(out, flags);
+ out.writeLong(mDeliveryTime);
+ out.writeInt(mIsRead ? 1 : 0);
+ out.writeInt(mSubId);
+ }
+
+ public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
+ = new Parcelable.Creator<CellBroadcastMessage>() {
+ @Override
+ public CellBroadcastMessage createFromParcel(Parcel in) {
+ return new CellBroadcastMessage(in);
+ }
+
+ @Override
+ public CellBroadcastMessage[] newArray(int size) {
+ return new CellBroadcastMessage[size];
+ }
+ };
+
+ /**
+ * Create a CellBroadcastMessage from a row in the database.
+ * @param cursor an open SQLite cursor pointing to the row to read
+ * @return the new CellBroadcastMessage
+ * @throws IllegalArgumentException if one of the required columns is missing
+ */
+ public static CellBroadcastMessage createFromCursor(Cursor cursor) {
+ int geoScope = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE));
+ int serialNum = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER));
+ int category = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY));
+ String language = cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE));
+ String body = cursor.getString(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY));
+ int format = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT));
+ int priority = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY));
+
+ String plmn;
+ int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN);
+ if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
+ plmn = cursor.getString(plmnColumn);
+ } else {
+ plmn = null;
+ }
+
+ int lac;
+ int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC);
+ if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
+ lac = cursor.getInt(lacColumn);
+ } else {
+ lac = -1;
+ }
+
+ int cid;
+ int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID);
+ if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
+ cid = cursor.getInt(cidColumn);
+ } else {
+ cid = -1;
+ }
+
+ SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
+
+ SmsCbEtwsInfo etwsInfo;
+ int etwsWarningTypeColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.ETWS_WARNING_TYPE);
+ if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
+ int warningType = cursor.getInt(etwsWarningTypeColumn);
+ etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null);
+ } else {
+ etwsInfo = null;
+ }
+
+ SmsCbCmasInfo cmasInfo;
+ int cmasMessageClassColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS);
+ if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
+ int messageClass = cursor.getInt(cmasMessageClassColumn);
+
+ int cmasCategory;
+ int cmasCategoryColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_CATEGORY);
+ if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
+ cmasCategory = cursor.getInt(cmasCategoryColumn);
+ } else {
+ cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
+ }
+
+ int responseType;
+ int cmasResponseTypeColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE);
+ if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
+ responseType = cursor.getInt(cmasResponseTypeColumn);
+ } else {
+ responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
+ }
+
+ int severity;
+ int cmasSeverityColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_SEVERITY);
+ if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
+ severity = cursor.getInt(cmasSeverityColumn);
+ } else {
+ severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
+ }
+
+ int urgency;
+ int cmasUrgencyColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_URGENCY);
+ if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
+ urgency = cursor.getInt(cmasUrgencyColumn);
+ } else {
+ urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
+ }
+
+ int certainty;
+ int cmasCertaintyColumn = cursor.getColumnIndex(
+ Telephony.CellBroadcasts.CMAS_CERTAINTY);
+ if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
+ certainty = cursor.getInt(cmasCertaintyColumn);
+ } else {
+ certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
+ }
+
+ cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
+ urgency, certainty);
+ } else {
+ cmasInfo = null;
+ }
+
+ SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category,
+ language, body, priority, etwsInfo, cmasInfo);
+
+ long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow(
+ Telephony.CellBroadcasts.DELIVERY_TIME));
+ boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow(
+ Telephony.CellBroadcasts.MESSAGE_READ)) != 0);
+
+ return new CellBroadcastMessage(msg, deliveryTime, isRead);
+ }
+
+ /**
+ * Return a ContentValues object for insertion into the database.
+ * @return a new ContentValues object containing this object's data
+ */
+ public ContentValues getContentValues() {
+ ContentValues cv = new ContentValues(16);
+ SmsCbMessage msg = mSmsCbMessage;
+ cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope());
+ SmsCbLocation location = msg.getLocation();
+ if (location.getPlmn() != null) {
+ cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn());
+ }
+ if (location.getLac() != -1) {
+ cv.put(Telephony.CellBroadcasts.LAC, location.getLac());
+ }
+ if (location.getCid() != -1) {
+ cv.put(Telephony.CellBroadcasts.CID, location.getCid());
+ }
+ cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber());
+ cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory());
+ cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode());
+ cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody());
+ cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime);
+ cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead);
+ cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat());
+ cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority());
+
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ if (etwsInfo != null) {
+ cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
+ }
+
+ SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
+ if (cmasInfo != null) {
+ cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
+ cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
+ cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
+ cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
+ cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
+ cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
+ }
+
+ return cv;
+ }
+
+ /**
+ * Set or clear the "read message" flag.
+ * @param isRead true if the message has been read; false if not
+ */
+ public void setIsRead(boolean isRead) {
+ mIsRead = isRead;
+ }
+
+ public String getLanguageCode() {
+ return mSmsCbMessage.getLanguageCode();
+ }
+
+ public int getServiceCategory() {
+ return mSmsCbMessage.getServiceCategory();
+ }
+
+ public long getDeliveryTime() {
+ return mDeliveryTime;
+ }
+
+ public String getMessageBody() {
+ return mSmsCbMessage.getMessageBody();
+ }
+
+ public boolean isRead() {
+ return mIsRead;
+ }
+
+ public int getSerialNumber() {
+ return mSmsCbMessage.getSerialNumber();
+ }
+
+ public SmsCbCmasInfo getCmasWarningInfo() {
+ return mSmsCbMessage.getCmasWarningInfo();
+ }
+
+ public SmsCbEtwsInfo getEtwsWarningInfo() {
+ return mSmsCbMessage.getEtwsWarningInfo();
+ }
+
+ /**
+ * Return whether the broadcast is an emergency (PWS) message type.
+ * This includes lower priority test messages and Amber alerts.
+ *
+ * All public alerts show the flashing warning icon in the dialog,
+ * but only emergency alerts play the alert sound and speak the message.
+ *
+ * @return true if the message is PWS type; false otherwise
+ */
+ public boolean isPublicAlertMessage() {
+ return mSmsCbMessage.isEmergencyMessage();
+ }
+
+ /**
+ * Returns whether the broadcast is an emergency (PWS) message type,
+ * including test messages and AMBER alerts.
+ *
+ * @return true if the message is PWS type (ETWS or CMAS)
+ */
+ public boolean isEmergencyAlertMessage() {
+ return mSmsCbMessage.isEmergencyMessage();
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS emergency message type.
+ * @return true if the message is ETWS emergency type; false otherwise
+ */
+ public boolean isEtwsMessage() {
+ return mSmsCbMessage.isEtwsMessage();
+ }
+
+ /**
+ * Return whether the broadcast is a CMAS emergency message type.
+ * @return true if the message is CMAS emergency type; false otherwise
+ */
+ public boolean isCmasMessage() {
+ return mSmsCbMessage.isCmasMessage();
+ }
+
+ /**
+ * Return the CMAS message class.
+ * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or
+ * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert
+ */
+ public int getCmasMessageClass() {
+ if (mSmsCbMessage.isCmasMessage()) {
+ return mSmsCbMessage.getCmasWarningInfo().getMessageClass();
+ } else {
+ return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
+ }
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS popup alert.
+ * This method checks the message ID and the message code.
+ * @return true if the message indicates an ETWS popup alert
+ */
+ public boolean isEtwsPopupAlert() {
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ return etwsInfo != null && etwsInfo.isPopupAlert();
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS emergency user alert.
+ * This method checks the message ID and the message code.
+ * @return true if the message indicates an ETWS emergency user alert
+ */
+ public boolean isEtwsEmergencyUserAlert() {
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ return etwsInfo != null && etwsInfo.isEmergencyUserAlert();
+ }
+
+ /**
+ * Return whether the broadcast is an ETWS test message.
+ * @return true if the message is an ETWS test message; false otherwise
+ */
+ public boolean isEtwsTestMessage() {
+ SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
+ return etwsInfo != null &&
+ etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
+ }
+
+ /**
+ * Return the abbreviated date string for the message delivery time.
+ * @param context the context object
+ * @return a String to use in the broadcast list UI
+ */
+ public String getDateString(Context context) {
+ int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME |
+ DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE |
+ DateUtils.FORMAT_CAP_AMPM;
+ return DateUtils.formatDateTime(context, mDeliveryTime, flags);
+ }
+
+ /**
+ * Return the date string for the message delivery time, suitable for text-to-speech.
+ * @param context the context object
+ * @return a String for populating the list item AccessibilityEvent for TTS
+ */
+ public String getSpokenDateString(Context context) {
+ int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE;
+ return DateUtils.formatDateTime(context, mDeliveryTime, flags);
+ }
+}
diff --git a/android/telephony/CellIdentityCdma.java b/android/telephony/CellIdentityCdma.java
new file mode 100644
index 00000000..b39b4c76
--- /dev/null
+++ b/android/telephony/CellIdentityCdma.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity is to represent a unique CDMA cell
+ */
+public final class CellIdentityCdma implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthCdma";
+ private static final boolean DBG = false;
+
+ // Network Id 0..65535
+ private final int mNetworkId;
+ // CDMA System Id 0..32767
+ private final int mSystemId;
+ // Base Station Id 0..65535
+ private final int mBasestationId;
+ /**
+ * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * It is represented in units of 0.25 seconds and ranges from -2592000
+ * to 2592000, both values inclusive (corresponding to a range of -180
+ * to +180 degrees).
+ */
+ private final int mLongitude;
+ /**
+ * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * It is represented in units of 0.25 seconds and ranges from -1296000
+ * to 1296000, both values inclusive (corresponding to a range of -90
+ * to +90 degrees).
+ */
+ private final int mLatitude;
+
+ /**
+ * @hide
+ */
+ public CellIdentityCdma() {
+ mNetworkId = Integer.MAX_VALUE;
+ mSystemId = Integer.MAX_VALUE;
+ mBasestationId = Integer.MAX_VALUE;
+ mLongitude = Integer.MAX_VALUE;
+ mLatitude = Integer.MAX_VALUE;
+ }
+
+ /**
+ * public constructor
+ * @param nid Network Id 0..65535
+ * @param sid CDMA System Id 0..32767
+ * @param bid Base Station Id 0..65535
+ * @param lon Longitude is a decimal number ranges from -2592000
+ * to 2592000
+ * @param lat Latitude is a decimal number ranges from -1296000
+ * to 1296000
+ *
+ * @hide
+ */
+ public CellIdentityCdma (int nid, int sid, int bid, int lon, int lat) {
+ mNetworkId = nid;
+ mSystemId = sid;
+ mBasestationId = bid;
+ mLongitude = lon;
+ mLatitude = lat;
+ }
+
+ private CellIdentityCdma(CellIdentityCdma cid) {
+ mNetworkId = cid.mNetworkId;
+ mSystemId = cid.mSystemId;
+ mBasestationId = cid.mBasestationId;
+ mLongitude = cid.mLongitude;
+ mLatitude = cid.mLatitude;
+ }
+
+ CellIdentityCdma copy() {
+ return new CellIdentityCdma(this);
+ }
+
+ /**
+ * @return Network Id 0..65535, Integer.MAX_VALUE if unknown
+ */
+ public int getNetworkId() {
+ return mNetworkId;
+ }
+
+ /**
+ * @return System Id 0..32767, Integer.MAX_VALUE if unknown
+ */
+ public int getSystemId() {
+ return mSystemId;
+ }
+
+ /**
+ * @return Base Station Id 0..65535, Integer.MAX_VALUE if unknown
+ */
+ public int getBasestationId() {
+ return mBasestationId;
+ }
+
+ /**
+ * @return Base station longitude, which is a decimal number as
+ * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
+ * of 0.25 seconds and ranges from -2592000 to 2592000, both
+ * values inclusive (corresponding to a range of -180
+ * to +180 degrees). Integer.MAX_VALUE if unknown.
+ */
+ public int getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * @return Base station latitude, which is a decimal number as
+ * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
+ * of 0.25 seconds and ranges from -1296000 to 1296000, both
+ * values inclusive (corresponding to a range of -90
+ * to +90 degrees). Integer.MAX_VALUE if unknown.
+ */
+ public int getLatitude() {
+ return mLatitude;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof CellIdentityCdma)) {
+ return false;
+ }
+
+ CellIdentityCdma o = (CellIdentityCdma) other;
+ return mNetworkId == o.mNetworkId &&
+ mSystemId == o.mSystemId &&
+ mBasestationId == o.mBasestationId &&
+ mLatitude == o.mLatitude &&
+ mLongitude == o.mLongitude;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CellIdentityCdma:{");
+ sb.append(" mNetworkId="); sb.append(mNetworkId);
+ sb.append(" mSystemId="); sb.append(mSystemId);
+ sb.append(" mBasestationId="); sb.append(mBasestationId);
+ sb.append(" mLongitude="); sb.append(mLongitude);
+ sb.append(" mLatitude="); sb.append(mLatitude);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mNetworkId);
+ dest.writeInt(mSystemId);
+ dest.writeInt(mBasestationId);
+ dest.writeInt(mLongitude);
+ dest.writeInt(mLatitude);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private CellIdentityCdma(Parcel in) {
+ mNetworkId = in.readInt();
+ mSystemId = in.readInt();
+ mBasestationId = in.readInt();
+ mLongitude = in.readInt();
+ mLatitude = in.readInt();
+ if (DBG) log("CellIdentityCdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Creator<CellIdentityCdma> CREATOR =
+ new Creator<CellIdentityCdma>() {
+ @Override
+ public CellIdentityCdma createFromParcel(Parcel in) {
+ return new CellIdentityCdma(in);
+ }
+
+ @Override
+ public CellIdentityCdma[] newArray(int size) {
+ return new CellIdentityCdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellIdentityGsm.java b/android/telephony/CellIdentityGsm.java
new file mode 100644
index 00000000..ec008e28
--- /dev/null
+++ b/android/telephony/CellIdentityGsm.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity to represent a unique GSM cell
+ */
+public final class CellIdentityGsm implements Parcelable {
+
+ private static final String LOG_TAG = "CellIdentityGsm";
+ private static final boolean DBG = false;
+
+ // 3-digit Mobile Country Code, 0..999
+ private final int mMcc;
+ // 2 or 3-digit Mobile Network Code, 0..999
+ private final int mMnc;
+ // 16-bit Location Area Code, 0..65535
+ private final int mLac;
+ // 16-bit GSM Cell Identity described in TS 27.007, 0..65535
+ private final int mCid;
+ // 16-bit GSM Absolute RF Channel Number
+ private final int mArfcn;
+ // 6-bit Base Station Identity Code
+ private final int mBsic;
+
+ /**
+ * @hide
+ */
+ public CellIdentityGsm() {
+ mMcc = Integer.MAX_VALUE;
+ mMnc = Integer.MAX_VALUE;
+ mLac = Integer.MAX_VALUE;
+ mCid = Integer.MAX_VALUE;
+ mArfcn = Integer.MAX_VALUE;
+ mBsic = Integer.MAX_VALUE;
+ }
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
+ *
+ * @hide
+ */
+ public CellIdentityGsm (int mcc, int mnc, int lac, int cid) {
+ this(mcc, mnc, lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
+ * @param arfcn 16-bit GSM Absolute RF Channel Number
+ * @param bsic 6-bit Base Station Identity Code
+ *
+ * @hide
+ */
+ public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
+ mMcc = mcc;
+ mMnc = mnc;
+ mLac = lac;
+ mCid = cid;
+ mArfcn = arfcn;
+ mBsic = bsic;
+ }
+
+ private CellIdentityGsm(CellIdentityGsm cid) {
+ mMcc = cid.mMcc;
+ mMnc = cid.mMnc;
+ mLac = cid.mLac;
+ mCid = cid.mCid;
+ mArfcn = cid.mArfcn;
+ mBsic = cid.mBsic;
+ }
+
+ CellIdentityGsm copy() {
+ return new CellIdentityGsm(this);
+ }
+
+ /**
+ * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMcc() {
+ return mMcc;
+ }
+
+ /**
+ * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMnc() {
+ return mMnc;
+ }
+
+ /**
+ * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * @return CID
+ * Either 16-bit GSM Cell Identity described
+ * in TS 27.007, 0..65535, Integer.MAX_VALUE if unknown
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ /**
+ * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ */
+ public int getArfcn() {
+ return mArfcn;
+ }
+
+ /**
+ * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown
+ */
+ public int getBsic() {
+ return mBsic;
+ }
+
+
+ /**
+ * @return Integer.MAX_VALUE, undefined for GSM
+ */
+ @Deprecated
+ public int getPsc() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMcc, mMnc, mLac, mCid);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof CellIdentityGsm)) {
+ return false;
+ }
+
+ CellIdentityGsm o = (CellIdentityGsm) other;
+ return mMcc == o.mMcc &&
+ mMnc == o.mMnc &&
+ mLac == o.mLac &&
+ mCid == o.mCid &&
+ mArfcn == o.mArfcn &&
+ mBsic == o.mBsic;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CellIdentityGsm:{");
+ sb.append(" mMcc=").append(mMcc);
+ sb.append(" mMnc=").append(mMnc);
+ sb.append(" mLac=").append(mLac);
+ sb.append(" mCid=").append(mCid);
+ sb.append(" mArfcn=").append(mArfcn);
+ sb.append(" mBsic=").append("0x").append(Integer.toHexString(mBsic));
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mMcc);
+ dest.writeInt(mMnc);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ dest.writeInt(mArfcn);
+ dest.writeInt(mBsic);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private CellIdentityGsm(Parcel in) {
+ mMcc = in.readInt();
+ mMnc = in.readInt();
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mArfcn = in.readInt();
+ int bsic = in.readInt();
+ // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
+ // for inbound parcels
+ if (bsic == 0xFF) bsic = Integer.MAX_VALUE;
+ mBsic = bsic;
+
+ if (DBG) log("CellIdentityGsm(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Creator<CellIdentityGsm> CREATOR =
+ new Creator<CellIdentityGsm>() {
+ @Override
+ public CellIdentityGsm createFromParcel(Parcel in) {
+ return new CellIdentityGsm(in);
+ }
+
+ @Override
+ public CellIdentityGsm[] newArray(int size) {
+ return new CellIdentityGsm[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellIdentityLte.java b/android/telephony/CellIdentityLte.java
new file mode 100644
index 00000000..ce743835
--- /dev/null
+++ b/android/telephony/CellIdentityLte.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity is to represent a unique LTE cell
+ */
+public final class CellIdentityLte implements Parcelable {
+
+ private static final String LOG_TAG = "CellIdentityLte";
+ private static final boolean DBG = false;
+
+ // 3-digit Mobile Country Code, 0..999
+ private final int mMcc;
+ // 2 or 3-digit Mobile Network Code, 0..999
+ private final int mMnc;
+ // 28-bit cell identity
+ private final int mCi;
+ // physical cell id 0..503
+ private final int mPci;
+ // 16-bit tracking area code
+ private final int mTac;
+ // 18-bit Absolute RF Channel Number
+ private final int mEarfcn;
+
+ /**
+ * @hide
+ */
+ public CellIdentityLte() {
+ mMcc = Integer.MAX_VALUE;
+ mMnc = Integer.MAX_VALUE;
+ mCi = Integer.MAX_VALUE;
+ mPci = Integer.MAX_VALUE;
+ mTac = Integer.MAX_VALUE;
+ mEarfcn = Integer.MAX_VALUE;
+ }
+
+ /**
+ *
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param ci 28-bit Cell Identity
+ * @param pci Physical Cell Id 0..503
+ * @param tac 16-bit Tracking Area Code
+ *
+ * @hide
+ */
+ public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) {
+ this(mcc, mnc, ci, pci, tac, Integer.MAX_VALUE);
+ }
+
+ /**
+ *
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param ci 28-bit Cell Identity
+ * @param pci Physical Cell Id 0..503
+ * @param tac 16-bit Tracking Area Code
+ * @param earfcn 18-bit LTE Absolute RF Channel Number
+ *
+ * @hide
+ */
+ public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
+ mMcc = mcc;
+ mMnc = mnc;
+ mCi = ci;
+ mPci = pci;
+ mTac = tac;
+ mEarfcn = earfcn;
+ }
+
+ private CellIdentityLte(CellIdentityLte cid) {
+ mMcc = cid.mMcc;
+ mMnc = cid.mMnc;
+ mCi = cid.mCi;
+ mPci = cid.mPci;
+ mTac = cid.mTac;
+ mEarfcn = cid.mEarfcn;
+ }
+
+ CellIdentityLte copy() {
+ return new CellIdentityLte(this);
+ }
+
+ /**
+ * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMcc() {
+ return mMcc;
+ }
+
+ /**
+ * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMnc() {
+ return mMnc;
+ }
+
+ /**
+ * @return 28-bit Cell Identity, Integer.MAX_VALUE if unknown
+ */
+ public int getCi() {
+ return mCi;
+ }
+
+ /**
+ * @return Physical Cell Id 0..503, Integer.MAX_VALUE if unknown
+ */
+ public int getPci() {
+ return mPci;
+ }
+
+ /**
+ * @return 16-bit Tracking Area Code, Integer.MAX_VALUE if unknown
+ */
+ public int getTac() {
+ return mTac;
+ }
+
+ /**
+ * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ */
+ public int getEarfcn() {
+ return mEarfcn;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMcc, mMnc, mCi, mPci, mTac);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof CellIdentityLte)) {
+ return false;
+ }
+
+ CellIdentityLte o = (CellIdentityLte) other;
+ return mMcc == o.mMcc &&
+ mMnc == o.mMnc &&
+ mCi == o.mCi &&
+ mPci == o.mPci &&
+ mTac == o.mTac &&
+ mEarfcn == o.mEarfcn;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CellIdentityLte:{");
+ sb.append(" mMcc="); sb.append(mMcc);
+ sb.append(" mMnc="); sb.append(mMnc);
+ sb.append(" mCi="); sb.append(mCi);
+ sb.append(" mPci="); sb.append(mPci);
+ sb.append(" mTac="); sb.append(mTac);
+ sb.append(" mEarfcn="); sb.append(mEarfcn);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mMcc);
+ dest.writeInt(mMnc);
+ dest.writeInt(mCi);
+ dest.writeInt(mPci);
+ dest.writeInt(mTac);
+ dest.writeInt(mEarfcn);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private CellIdentityLte(Parcel in) {
+ mMcc = in.readInt();
+ mMnc = in.readInt();
+ mCi = in.readInt();
+ mPci = in.readInt();
+ mTac = in.readInt();
+ mEarfcn = in.readInt();
+ if (DBG) log("CellIdentityLte(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Creator<CellIdentityLte> CREATOR =
+ new Creator<CellIdentityLte>() {
+ @Override
+ public CellIdentityLte createFromParcel(Parcel in) {
+ return new CellIdentityLte(in);
+ }
+
+ @Override
+ public CellIdentityLte[] newArray(int size) {
+ return new CellIdentityLte[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellIdentityWcdma.java b/android/telephony/CellIdentityWcdma.java
new file mode 100644
index 00000000..0d13efd2
--- /dev/null
+++ b/android/telephony/CellIdentityWcdma.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity to represent a unique UMTS cell
+ */
+public final class CellIdentityWcdma implements Parcelable {
+
+ private static final String LOG_TAG = "CellIdentityWcdma";
+ private static final boolean DBG = false;
+
+ // 3-digit Mobile Country Code, 0..999
+ private final int mMcc;
+ // 2 or 3-digit Mobile Network Code, 0..999
+ private final int mMnc;
+ // 16-bit Location Area Code, 0..65535
+ private final int mLac;
+ // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455
+ private final int mCid;
+ // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
+ private final int mPsc;
+ // 16-bit UMTS Absolute RF Channel Number
+ private final int mUarfcn;
+
+ /**
+ * @hide
+ */
+ public CellIdentityWcdma() {
+ mMcc = Integer.MAX_VALUE;
+ mMnc = Integer.MAX_VALUE;
+ mLac = Integer.MAX_VALUE;
+ mCid = Integer.MAX_VALUE;
+ mPsc = Integer.MAX_VALUE;
+ mUarfcn = Integer.MAX_VALUE;
+ }
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 28-bit UMTS Cell Identity
+ * @param psc 9-bit UMTS Primary Scrambling Code
+ *
+ * @hide
+ */
+ public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
+ this(mcc, mnc, lac, cid, psc, Integer.MAX_VALUE);
+ }
+
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 28-bit UMTS Cell Identity
+ * @param psc 9-bit UMTS Primary Scrambling Code
+ * @param uarfcn 16-bit UMTS Absolute RF Channel Number
+ *
+ * @hide
+ */
+ public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc, int uarfcn) {
+ mMcc = mcc;
+ mMnc = mnc;
+ mLac = lac;
+ mCid = cid;
+ mPsc = psc;
+ mUarfcn = uarfcn;
+ }
+
+ private CellIdentityWcdma(CellIdentityWcdma cid) {
+ mMcc = cid.mMcc;
+ mMnc = cid.mMnc;
+ mLac = cid.mLac;
+ mCid = cid.mCid;
+ mPsc = cid.mPsc;
+ mUarfcn = cid.mUarfcn;
+ }
+
+ CellIdentityWcdma copy() {
+ return new CellIdentityWcdma(this);
+ }
+
+ /**
+ * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMcc() {
+ return mMcc;
+ }
+
+ /**
+ * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMnc() {
+ return mMnc;
+ }
+
+ /**
+ * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * @return CID
+ * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, Integer.MAX_VALUE if unknown
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ /**
+ * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, Integer.MAX_VALUE
+ * if unknown
+ */
+ public int getPsc() {
+ return mPsc;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMcc, mMnc, mLac, mCid, mPsc);
+ }
+
+ /**
+ * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ */
+ public int getUarfcn() {
+ return mUarfcn;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof CellIdentityWcdma)) {
+ return false;
+ }
+
+ CellIdentityWcdma o = (CellIdentityWcdma) other;
+ return mMcc == o.mMcc &&
+ mMnc == o.mMnc &&
+ mLac == o.mLac &&
+ mCid == o.mCid &&
+ mPsc == o.mPsc &&
+ mUarfcn == o.mUarfcn;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CellIdentityWcdma:{");
+ sb.append(" mMcc=").append(mMcc);
+ sb.append(" mMnc=").append(mMnc);
+ sb.append(" mLac=").append(mLac);
+ sb.append(" mCid=").append(mCid);
+ sb.append(" mPsc=").append(mPsc);
+ sb.append(" mUarfcn=").append(mUarfcn);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mMcc);
+ dest.writeInt(mMnc);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ dest.writeInt(mPsc);
+ dest.writeInt(mUarfcn);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private CellIdentityWcdma(Parcel in) {
+ mMcc = in.readInt();
+ mMnc = in.readInt();
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mPsc = in.readInt();
+ mUarfcn = in.readInt();
+ if (DBG) log("CellIdentityWcdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Creator<CellIdentityWcdma> CREATOR =
+ new Creator<CellIdentityWcdma>() {
+ @Override
+ public CellIdentityWcdma createFromParcel(Parcel in) {
+ return new CellIdentityWcdma(in);
+ }
+
+ @Override
+ public CellIdentityWcdma[] newArray(int size) {
+ return new CellIdentityWcdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellInfo.java b/android/telephony/CellInfo.java
new file mode 100644
index 00000000..b5e4eef8
--- /dev/null
+++ b/android/telephony/CellInfo.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public abstract class CellInfo implements Parcelable {
+
+ // Type fields for parceling
+ /** @hide */
+ protected static final int TYPE_GSM = 1;
+ /** @hide */
+ protected static final int TYPE_CDMA = 2;
+ /** @hide */
+ protected static final int TYPE_LTE = 3;
+ /** @hide */
+ protected static final int TYPE_WCDMA = 4;
+
+ // Type to distinguish where time stamp gets recorded.
+
+ /** @hide */
+ public static final int TIMESTAMP_TYPE_UNKNOWN = 0;
+ /** @hide */
+ public static final int TIMESTAMP_TYPE_ANTENNA = 1;
+ /** @hide */
+ public static final int TIMESTAMP_TYPE_MODEM = 2;
+ /** @hide */
+ public static final int TIMESTAMP_TYPE_OEM_RIL = 3;
+ /** @hide */
+ public static final int TIMESTAMP_TYPE_JAVA_RIL = 4;
+
+ // True if device is mRegistered to the mobile network
+ private boolean mRegistered;
+
+ // Observation time stamped as type in nanoseconds since boot
+ private long mTimeStamp;
+
+ // Where time stamp gets recorded.
+ // Value of TIMESTAMP_TYPE_XXXX
+ private int mTimeStampType;
+
+ /** @hide */
+ protected CellInfo() {
+ this.mRegistered = false;
+ this.mTimeStampType = TIMESTAMP_TYPE_UNKNOWN;
+ this.mTimeStamp = Long.MAX_VALUE;
+ }
+
+ /** @hide */
+ protected CellInfo(CellInfo ci) {
+ this.mRegistered = ci.mRegistered;
+ this.mTimeStampType = ci.mTimeStampType;
+ this.mTimeStamp = ci.mTimeStamp;
+ }
+
+ /** True if this cell is registered to the mobile network */
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+ /** @hide */
+ public void setRegistered(boolean registered) {
+ mRegistered = registered;
+ }
+
+ /** Approximate time of this cell information in nanos since boot */
+ public long getTimeStamp() {
+ return mTimeStamp;
+ }
+ /** @hide */
+ public void setTimeStamp(long timeStamp) {
+ mTimeStamp = timeStamp;
+ }
+
+ /**
+ * Where time stamp gets recorded.
+ * @return one of TIMESTAMP_TYPE_XXXX
+ *
+ * @hide
+ */
+ public int getTimeStampType() {
+ return mTimeStampType;
+ }
+ /** @hide */
+ public void setTimeStampType(int timeStampType) {
+ if (timeStampType < TIMESTAMP_TYPE_UNKNOWN || timeStampType > TIMESTAMP_TYPE_JAVA_RIL) {
+ mTimeStampType = TIMESTAMP_TYPE_UNKNOWN;
+ } else {
+ mTimeStampType = timeStampType;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
+ + (mTimeStampType * primeNum);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ try {
+ CellInfo o = (CellInfo) other;
+ return mRegistered == o.mRegistered
+ && mTimeStamp == o.mTimeStamp && mTimeStampType == o.mTimeStampType;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ private static String timeStampTypeToString(int type) {
+ switch (type) {
+ case 1:
+ return "antenna";
+ case 2:
+ return "modem";
+ case 3:
+ return "oem_ril";
+ case 4:
+ return "java_ril";
+ default:
+ return "unknown";
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ String timeStampType;
+
+ sb.append("mRegistered=").append(mRegistered ? "YES" : "NO");
+ timeStampType = timeStampTypeToString(mTimeStampType);
+ sb.append(" mTimeStampType=").append(timeStampType);
+ sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+
+ return sb.toString();
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public abstract void writeToParcel(Parcel dest, int flags);
+
+ /**
+ * Used by child classes for parceling.
+ *
+ * @hide
+ */
+ protected void writeToParcel(Parcel dest, int flags, int type) {
+ dest.writeInt(type);
+ dest.writeInt(mRegistered ? 1 : 0);
+ dest.writeInt(mTimeStampType);
+ dest.writeLong(mTimeStamp);
+ }
+
+ /**
+ * Used by child classes for parceling
+ *
+ * @hide
+ */
+ protected CellInfo(Parcel in) {
+ mRegistered = (in.readInt() == 1) ? true : false;
+ mTimeStampType = in.readInt();
+ mTimeStamp = in.readLong();
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfo> CREATOR = new Creator<CellInfo>() {
+ @Override
+ public CellInfo createFromParcel(Parcel in) {
+ int type = in.readInt();
+ switch (type) {
+ case TYPE_GSM: return CellInfoGsm.createFromParcelBody(in);
+ case TYPE_CDMA: return CellInfoCdma.createFromParcelBody(in);
+ case TYPE_LTE: return CellInfoLte.createFromParcelBody(in);
+ case TYPE_WCDMA: return CellInfoWcdma.createFromParcelBody(in);
+ default: throw new RuntimeException("Bad CellInfo Parcel");
+ }
+ }
+
+ @Override
+ public CellInfo[] newArray(int size) {
+ return new CellInfo[size];
+ }
+ };
+}
diff --git a/android/telephony/CellInfoCdma.java b/android/telephony/CellInfoCdma.java
new file mode 100644
index 00000000..6f2f1f67
--- /dev/null
+++ b/android/telephony/CellInfoCdma.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public final class CellInfoCdma extends CellInfo implements Parcelable {
+
+ private static final String LOG_TAG = "CellInfoCdma";
+ private static final boolean DBG = false;
+
+ private CellIdentityCdma mCellIdentityCdma;
+ private CellSignalStrengthCdma mCellSignalStrengthCdma;
+
+ /** @hide */
+ public CellInfoCdma() {
+ super();
+ mCellIdentityCdma = new CellIdentityCdma();
+ mCellSignalStrengthCdma = new CellSignalStrengthCdma();
+ }
+
+ /** @hide */
+ public CellInfoCdma(CellInfoCdma ci) {
+ super(ci);
+ this.mCellIdentityCdma = ci.mCellIdentityCdma.copy();
+ this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
+ }
+
+ public CellIdentityCdma getCellIdentity() {
+ return mCellIdentityCdma;
+ }
+ /** @hide */
+ public void setCellIdentity(CellIdentityCdma cid) {
+ mCellIdentityCdma = cid;
+ }
+
+ public CellSignalStrengthCdma getCellSignalStrength() {
+ return mCellSignalStrengthCdma;
+ }
+ /** @hide */
+ public void setCellSignalStrength(CellSignalStrengthCdma css) {
+ mCellSignalStrengthCdma = css;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode() + mCellIdentityCdma.hashCode() + mCellSignalStrengthCdma.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ try {
+ CellInfoCdma o = (CellInfoCdma) other;
+ return mCellIdentityCdma.equals(o.mCellIdentityCdma)
+ && mCellSignalStrengthCdma.equals(o.mCellSignalStrengthCdma);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CellInfoCdma:{");
+ sb.append(super.toString());
+ sb.append(" ").append(mCellIdentityCdma);
+ sb.append(" ").append(mCellSignalStrengthCdma);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags, TYPE_CDMA);
+ mCellIdentityCdma.writeToParcel(dest, flags);
+ mCellSignalStrengthCdma.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Construct a CellInfoCdma object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellInfoCdma(Parcel in) {
+ super(in);
+ mCellIdentityCdma = CellIdentityCdma.CREATOR.createFromParcel(in);
+ mCellSignalStrengthCdma = CellSignalStrengthCdma.CREATOR.createFromParcel(in);
+ if (DBG) log("CellInfoCdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfoCdma> CREATOR = new Creator<CellInfoCdma>() {
+ @Override
+ public CellInfoCdma createFromParcel(Parcel in) {
+ in.readInt(); // Skip past token, we know what it is
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellInfoCdma[] newArray(int size) {
+ return new CellInfoCdma[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellInfoCdma createFromParcelBody(Parcel in) {
+ return new CellInfoCdma(in);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellInfoGsm.java b/android/telephony/CellInfoGsm.java
new file mode 100644
index 00000000..1bedddb6
--- /dev/null
+++ b/android/telephony/CellInfoGsm.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public final class CellInfoGsm extends CellInfo implements Parcelable {
+
+ private static final String LOG_TAG = "CellInfoGsm";
+ private static final boolean DBG = false;
+
+ private CellIdentityGsm mCellIdentityGsm;
+ private CellSignalStrengthGsm mCellSignalStrengthGsm;
+
+ /** @hide */
+ public CellInfoGsm() {
+ super();
+ mCellIdentityGsm = new CellIdentityGsm();
+ mCellSignalStrengthGsm = new CellSignalStrengthGsm();
+ }
+
+ /** @hide */
+ public CellInfoGsm(CellInfoGsm ci) {
+ super(ci);
+ this.mCellIdentityGsm = ci.mCellIdentityGsm.copy();
+ this.mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
+ }
+
+ public CellIdentityGsm getCellIdentity() {
+ return mCellIdentityGsm;
+ }
+ /** @hide */
+ public void setCellIdentity(CellIdentityGsm cid) {
+ mCellIdentityGsm = cid;
+ }
+
+ public CellSignalStrengthGsm getCellSignalStrength() {
+ return mCellSignalStrengthGsm;
+ }
+ /** @hide */
+ public void setCellSignalStrength(CellSignalStrengthGsm css) {
+ mCellSignalStrengthGsm = css;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode() + mCellIdentityGsm.hashCode() + mCellSignalStrengthGsm.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ try {
+ CellInfoGsm o = (CellInfoGsm) other;
+ return mCellIdentityGsm.equals(o.mCellIdentityGsm)
+ && mCellSignalStrengthGsm.equals(o.mCellSignalStrengthGsm);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CellInfoGsm:{");
+ sb.append(super.toString());
+ sb.append(" ").append(mCellIdentityGsm);
+ sb.append(" ").append(mCellSignalStrengthGsm);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags, TYPE_GSM);
+ mCellIdentityGsm.writeToParcel(dest, flags);
+ mCellSignalStrengthGsm.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Construct a CellInfoGsm object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellInfoGsm(Parcel in) {
+ super(in);
+ mCellIdentityGsm = CellIdentityGsm.CREATOR.createFromParcel(in);
+ mCellSignalStrengthGsm = CellSignalStrengthGsm.CREATOR.createFromParcel(in);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfoGsm> CREATOR = new Creator<CellInfoGsm>() {
+ @Override
+ public CellInfoGsm createFromParcel(Parcel in) {
+ in.readInt(); // Skip past token, we know what it is
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellInfoGsm[] newArray(int size) {
+ return new CellInfoGsm[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellInfoGsm createFromParcelBody(Parcel in) {
+ return new CellInfoGsm(in);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellInfoLte.java b/android/telephony/CellInfoLte.java
new file mode 100644
index 00000000..287c9f04
--- /dev/null
+++ b/android/telephony/CellInfoLte.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public final class CellInfoLte extends CellInfo implements Parcelable {
+
+ private static final String LOG_TAG = "CellInfoLte";
+ private static final boolean DBG = false;
+
+ private CellIdentityLte mCellIdentityLte;
+ private CellSignalStrengthLte mCellSignalStrengthLte;
+
+ /** @hide */
+ public CellInfoLte() {
+ super();
+ mCellIdentityLte = new CellIdentityLte();
+ mCellSignalStrengthLte = new CellSignalStrengthLte();
+ }
+
+ /** @hide */
+ public CellInfoLte(CellInfoLte ci) {
+ super(ci);
+ this.mCellIdentityLte = ci.mCellIdentityLte.copy();
+ this.mCellSignalStrengthLte = ci.mCellSignalStrengthLte.copy();
+ }
+
+ public CellIdentityLte getCellIdentity() {
+ if (DBG) log("getCellIdentity: " + mCellIdentityLte);
+ return mCellIdentityLte;
+ }
+ /** @hide */
+ public void setCellIdentity(CellIdentityLte cid) {
+ if (DBG) log("setCellIdentity: " + cid);
+ mCellIdentityLte = cid;
+ }
+
+ public CellSignalStrengthLte getCellSignalStrength() {
+ if (DBG) log("getCellSignalStrength: " + mCellSignalStrengthLte);
+ return mCellSignalStrengthLte;
+ }
+ /** @hide */
+ public void setCellSignalStrength(CellSignalStrengthLte css) {
+ if (DBG) log("setCellSignalStrength: " + css);
+ mCellSignalStrengthLte = css;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode() + mCellIdentityLte.hashCode() + mCellSignalStrengthLte.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ try {
+ CellInfoLte o = (CellInfoLte) other;
+ return mCellIdentityLte.equals(o.mCellIdentityLte)
+ && mCellSignalStrengthLte.equals(o.mCellSignalStrengthLte);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CellInfoLte:{");
+ sb.append(super.toString());
+ sb.append(" ").append(mCellIdentityLte);
+ sb.append(" ").append(mCellSignalStrengthLte);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ super.writeToParcel(dest, flags, TYPE_LTE);
+ mCellIdentityLte.writeToParcel(dest, flags);
+ mCellSignalStrengthLte.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Construct a CellInfoLte object from the given parcel
+ * where the TYPE_LTE token is already been processed.
+ */
+ private CellInfoLte(Parcel in) {
+ super(in);
+ mCellIdentityLte = CellIdentityLte.CREATOR.createFromParcel(in);
+ mCellSignalStrengthLte = CellSignalStrengthLte.CREATOR.createFromParcel(in);
+ if (DBG) log("CellInfoLte(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfoLte> CREATOR = new Creator<CellInfoLte>() {
+ @Override
+ public CellInfoLte createFromParcel(Parcel in) {
+ in.readInt(); // Skip past token, we know what it is
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellInfoLte[] newArray(int size) {
+ return new CellInfoLte[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellInfoLte createFromParcelBody(Parcel in) {
+ return new CellInfoLte(in);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellInfoWcdma.java b/android/telephony/CellInfoWcdma.java
new file mode 100644
index 00000000..06157022
--- /dev/null
+++ b/android/telephony/CellInfoWcdma.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public final class CellInfoWcdma extends CellInfo implements Parcelable {
+
+ private static final String LOG_TAG = "CellInfoWcdma";
+ private static final boolean DBG = false;
+
+ private CellIdentityWcdma mCellIdentityWcdma;
+ private CellSignalStrengthWcdma mCellSignalStrengthWcdma;
+
+ /** @hide */
+ public CellInfoWcdma() {
+ super();
+ mCellIdentityWcdma = new CellIdentityWcdma();
+ mCellSignalStrengthWcdma = new CellSignalStrengthWcdma();
+ }
+
+ /** @hide */
+ public CellInfoWcdma(CellInfoWcdma ci) {
+ super(ci);
+ this.mCellIdentityWcdma = ci.mCellIdentityWcdma.copy();
+ this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
+ }
+
+ public CellIdentityWcdma getCellIdentity() {
+ return mCellIdentityWcdma;
+ }
+ /** @hide */
+ public void setCellIdentity(CellIdentityWcdma cid) {
+ mCellIdentityWcdma = cid;
+ }
+
+ public CellSignalStrengthWcdma getCellSignalStrength() {
+ return mCellSignalStrengthWcdma;
+ }
+ /** @hide */
+ public void setCellSignalStrength(CellSignalStrengthWcdma css) {
+ mCellSignalStrengthWcdma = css;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode() + mCellIdentityWcdma.hashCode() + mCellSignalStrengthWcdma.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ try {
+ CellInfoWcdma o = (CellInfoWcdma) other;
+ return mCellIdentityWcdma.equals(o.mCellIdentityWcdma)
+ && mCellSignalStrengthWcdma.equals(o.mCellSignalStrengthWcdma);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CellInfoWcdma:{");
+ sb.append(super.toString());
+ sb.append(" ").append(mCellIdentityWcdma);
+ sb.append(" ").append(mCellSignalStrengthWcdma);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags, TYPE_WCDMA);
+ mCellIdentityWcdma.writeToParcel(dest, flags);
+ mCellSignalStrengthWcdma.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Construct a CellInfoWcdma object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellInfoWcdma(Parcel in) {
+ super(in);
+ mCellIdentityWcdma = CellIdentityWcdma.CREATOR.createFromParcel(in);
+ mCellSignalStrengthWcdma = CellSignalStrengthWcdma.CREATOR.createFromParcel(in);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfoWcdma> CREATOR = new Creator<CellInfoWcdma>() {
+ @Override
+ public CellInfoWcdma createFromParcel(Parcel in) {
+ in.readInt(); // Skip past token, we know what it is
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellInfoWcdma[] newArray(int size) {
+ return new CellInfoWcdma[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellInfoWcdma createFromParcelBody(Parcel in) {
+ return new CellInfoWcdma(in);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellLocation.java b/android/telephony/CellLocation.java
new file mode 100644
index 00000000..5bcaa6e7
--- /dev/null
+++ b/android/telephony/CellLocation.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
+
+/**
+ * Abstract class that represents the location of the device. {@more}
+ */
+public abstract class CellLocation {
+
+ /**
+ * Request an update of the current location. If the location has changed,
+ * a broadcast will be sent to everyone registered with {@link
+ * PhoneStateListener#LISTEN_CELL_LOCATION}.
+ */
+ public static void requestLocationUpdate() {
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
+ if (phone != null) {
+ phone.updateServiceLocation();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Create a new CellLocation from a intent notifier Bundle
+ *
+ * This method is used by PhoneStateIntentReceiver and maybe by
+ * external applications.
+ *
+ * @param bundle Bundle from intent notifier
+ * @return newly created CellLocation
+ *
+ * @hide
+ */
+ public static CellLocation newFromBundle(Bundle bundle) {
+ // TelephonyManager.getDefault().getCurrentPhoneType() handles the case when
+ // ITelephony interface is not up yet.
+ switch(TelephonyManager.getDefault().getCurrentPhoneType()) {
+ case PhoneConstants.PHONE_TYPE_CDMA:
+ return new CdmaCellLocation(bundle);
+ case PhoneConstants.PHONE_TYPE_GSM:
+ return new GsmCellLocation(bundle);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public abstract void fillInNotifierBundle(Bundle bundle);
+
+ /**
+ * @hide
+ */
+ public abstract boolean isEmpty();
+
+ /**
+ * Invalidate this object. The location area code and the cell id are set to -1.
+ * @hide
+ */
+ public abstract void setStateInvalid();
+
+ /**
+ * Return a new CellLocation object representing an unknown
+ * location, or null for unknown/none phone radio types.
+ *
+ */
+ public static CellLocation getEmpty() {
+ // TelephonyManager.getDefault().getCurrentPhoneType() handles the case when
+ // ITelephony interface is not up yet.
+ switch(TelephonyManager.getDefault().getCurrentPhoneType()) {
+ case PhoneConstants.PHONE_TYPE_CDMA:
+ return new CdmaCellLocation();
+ case PhoneConstants.PHONE_TYPE_GSM:
+ return new GsmCellLocation();
+ default:
+ return null;
+ }
+ }
+}
diff --git a/android/telephony/CellSignalStrength.java b/android/telephony/CellSignalStrength.java
new file mode 100644
index 00000000..2a3ef219
--- /dev/null
+++ b/android/telephony/CellSignalStrength.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Abstract base class for cell phone signal strength related information.
+ */
+public abstract class CellSignalStrength {
+
+ public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+
+ public static final int SIGNAL_STRENGTH_POOR = 1;
+
+ public static final int SIGNAL_STRENGTH_MODERATE = 2;
+
+ public static final int SIGNAL_STRENGTH_GOOD = 3;
+
+ public static final int SIGNAL_STRENGTH_GREAT = 4;
+
+ /** @hide */
+ public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+
+ /** @hide */
+ public static final String[] SIGNAL_STRENGTH_NAMES = {
+ "none", "poor", "moderate", "good", "great"
+ };
+
+ /** @hide */
+ protected CellSignalStrength() {
+ }
+
+ /** @hide */
+ public abstract void setDefaultValues();
+
+ /**
+ * Get signal level as an int from 0..4
+ * <p>
+ * @see SIGNAL_STRENGTH_NONE_OR_UNKNOWN
+ * @see SIGNAL_STRENGTH_POOR
+ * @see SIGNAL_STRENGTH_MODERATE
+ * @see SIGNAL_STRENGTH_GOOD
+ * @see SIGNAL_STRENGTH_GREAT
+ */
+ public abstract int getLevel();
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ */
+ public abstract int getAsuLevel();
+
+ /**
+ * Get the signal strength as dBm
+ */
+ public abstract int getDbm();
+
+ /**
+ * Copies the CellSignalStrength.
+ *
+ * @return A deep copy of this class.
+ * @hide
+ */
+ public abstract CellSignalStrength copy();
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals (Object o);
+}
diff --git a/android/telephony/CellSignalStrengthCdma.java b/android/telephony/CellSignalStrengthCdma.java
new file mode 100644
index 00000000..04743620
--- /dev/null
+++ b/android/telephony/CellSignalStrengthCdma.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Signal strength related information.
+ */
+public final class CellSignalStrengthCdma extends CellSignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthCdma";
+ private static final boolean DBG = false;
+
+ private int mCdmaDbm; // This value is the RSSI value
+ private int mCdmaEcio; // This value is the Ec/Io
+ private int mEvdoDbm; // This value is the EVDO RSSI value
+ private int mEvdoEcio; // This value is the EVDO Ec/Io
+ private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthCdma() {
+ setDefaultValues();
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
+ int evdoSnr) {
+ initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param cdmaDbm
+ * @param cdmaEcio
+ * @param evdoDbm
+ * @param evdoEcio
+ * @param evdoSnr
+ *
+ * @hide
+ */
+ public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
+ mCdmaDbm = cdmaDbm;
+ mCdmaEcio = cdmaEcio;
+ mEvdoDbm = evdoDbm;
+ mEvdoEcio = evdoEcio;
+ mEvdoSnr = evdoSnr;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(CellSignalStrengthCdma s) {
+ mCdmaDbm = s.mCdmaDbm;
+ mCdmaEcio = s.mCdmaEcio;
+ mEvdoDbm = s.mEvdoDbm;
+ mEvdoEcio = s.mEvdoEcio;
+ mEvdoSnr = s.mEvdoSnr;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CellSignalStrengthCdma copy() {
+ return new CellSignalStrengthCdma(this);
+ }
+
+ /** @hide */
+ @Override
+ public void setDefaultValues() {
+ mCdmaDbm = Integer.MAX_VALUE;
+ mCdmaEcio = Integer.MAX_VALUE;
+ mEvdoDbm = Integer.MAX_VALUE;
+ mEvdoEcio = Integer.MAX_VALUE;
+ mEvdoSnr = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Get signal level as an int from 0..4
+ */
+ @Override
+ public int getLevel() {
+ int level;
+
+ int cdmaLevel = getCdmaLevel();
+ int evdoLevel = getEvdoLevel();
+ if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ /* We don't know evdo, use cdma */
+ level = getCdmaLevel();
+ } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ /* We don't know cdma, use evdo */
+ level = getEvdoLevel();
+ } else {
+ /* We know both, use the lowest level */
+ level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
+ }
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..97, 99 is unknown
+ */
+ @Override
+ public int getAsuLevel() {
+ final int cdmaDbm = getCdmaDbm();
+ final int cdmaEcio = getCdmaEcio();
+ int cdmaAsuLevel;
+ int ecioAsuLevel;
+
+ if (cdmaDbm == Integer.MAX_VALUE) cdmaAsuLevel = 99;
+ else if (cdmaDbm >= -75) cdmaAsuLevel = 16;
+ else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
+ else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
+ else if (cdmaDbm >= -95) cdmaAsuLevel = 2;
+ else if (cdmaDbm >= -100) cdmaAsuLevel = 1;
+ else cdmaAsuLevel = 99;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio == Integer.MAX_VALUE) ecioAsuLevel = 99;
+ else if (cdmaEcio >= -90) ecioAsuLevel = 16;
+ else if (cdmaEcio >= -100) ecioAsuLevel = 8;
+ else if (cdmaEcio >= -115) ecioAsuLevel = 4;
+ else if (cdmaEcio >= -130) ecioAsuLevel = 2;
+ else if (cdmaEcio >= -150) ecioAsuLevel = 1;
+ else ecioAsuLevel = 99;
+
+ int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel;
+ if (DBG) log("getAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get cdma as level 0..4
+ */
+ public int getCdmaLevel() {
+ final int cdmaDbm = getCdmaDbm();
+ final int cdmaEcio = getCdmaEcio();
+ int levelDbm;
+ int levelEcio;
+
+ if (cdmaDbm == Integer.MAX_VALUE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
+ else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
+ else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
+ else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
+ else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio == Integer.MAX_VALUE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
+ else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
+ else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
+ else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
+ else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ if (DBG) log("getCdmaLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get Evdo as level 0..4
+ */
+ public int getEvdoLevel() {
+ int evdoDbm = getEvdoDbm();
+ int evdoSnr = getEvdoSnr();
+ int levelEvdoDbm;
+ int levelEvdoSnr;
+
+ if (evdoDbm == Integer.MAX_VALUE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
+ else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
+ else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
+ else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
+ else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (evdoSnr == Integer.MAX_VALUE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
+ else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
+ else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
+ else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
+ else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ if (DBG) log("getEvdoLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ */
+ @Override
+ public int getDbm() {
+ int cdmaDbm = getCdmaDbm();
+ int evdoDbm = getEvdoDbm();
+
+ // Use the lower value to be conservative
+ return (cdmaDbm < evdoDbm) ? cdmaDbm : evdoDbm;
+ }
+
+ /**
+ * Get the CDMA RSSI value in dBm
+ */
+ public int getCdmaDbm() {
+ return mCdmaDbm;
+ }
+ /** @hide */
+ public void setCdmaDbm(int cdmaDbm) {
+ mCdmaDbm = cdmaDbm;
+ }
+
+ /**
+ * Get the CDMA Ec/Io value in dB*10
+ */
+ public int getCdmaEcio() {
+ return mCdmaEcio;
+ }
+ /** @hide */
+ public void setCdmaEcio(int cdmaEcio) {
+ mCdmaEcio = cdmaEcio;
+ }
+
+ /**
+ * Get the EVDO RSSI value in dBm
+ */
+ public int getEvdoDbm() {
+ return mEvdoDbm;
+ }
+ /** @hide */
+ public void setEvdoDbm(int evdoDbm) {
+ mEvdoDbm = evdoDbm;
+ }
+
+ /**
+ * Get the EVDO Ec/Io value in dB*10
+ */
+ public int getEvdoEcio() {
+ return mEvdoEcio;
+ }
+ /** @hide */
+ public void setEvdoEcio(int evdoEcio) {
+ mEvdoEcio = evdoEcio;
+ }
+
+ /**
+ * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
+ */
+ public int getEvdoSnr() {
+ return mEvdoSnr;
+ }
+ /** @hide */
+ public void setEvdoSnr(int evdoSnr) {
+ mEvdoSnr = evdoSnr;
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return ((mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
+ + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum));
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ CellSignalStrengthCdma s;
+
+ try {
+ s = (CellSignalStrengthCdma) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mCdmaDbm == s.mCdmaDbm
+ && mCdmaEcio == s.mCdmaEcio
+ && mEvdoDbm == s.mEvdoDbm
+ && mEvdoEcio == s.mEvdoEcio
+ && mEvdoSnr == s.mEvdoSnr;
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return "CellSignalStrengthCdma:"
+ + " cdmaDbm=" + mCdmaDbm
+ + " cdmaEcio=" + mCdmaEcio
+ + " evdoDbm=" + mEvdoDbm
+ + " evdoEcio=" + mEvdoEcio
+ + " evdoSnr=" + mEvdoSnr;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ // Need to multiply CdmaDbm, CdmaEcio, EvdoDbm and EvdoEcio by -1
+ // to ensure consistency when reading values written here
+ // unless the value is invalid
+ dest.writeInt(mCdmaDbm * (mCdmaDbm != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mCdmaEcio * (mCdmaEcio != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mEvdoDbm * (mEvdoDbm != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mEvdoEcio * (mEvdoEcio != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mEvdoSnr);
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel
+ * where the TYPE_CDMA token is already been processed.
+ */
+ private CellSignalStrengthCdma(Parcel in) {
+ // CdmaDbm, CdmaEcio, EvdoDbm and EvdoEcio are written into
+ // the parcel as positive values.
+ // Need to convert into negative values unless the value is invalid
+ mCdmaDbm = in.readInt();
+ if (mCdmaDbm != Integer.MAX_VALUE) mCdmaDbm *= -1;
+ mCdmaEcio = in.readInt();
+ if (mCdmaEcio != Integer.MAX_VALUE) mCdmaEcio *= -1;
+ mEvdoDbm = in.readInt();
+ if (mEvdoDbm != Integer.MAX_VALUE) mEvdoDbm *= -1;
+ mEvdoEcio = in.readInt();
+ if (mEvdoEcio != Integer.MAX_VALUE) mEvdoEcio *= -1;
+ mEvdoSnr = in.readInt();
+ if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<CellSignalStrengthCdma> CREATOR =
+ new Parcelable.Creator<CellSignalStrengthCdma>() {
+ @Override
+ public CellSignalStrengthCdma createFromParcel(Parcel in) {
+ return new CellSignalStrengthCdma(in);
+ }
+
+ @Override
+ public CellSignalStrengthCdma[] newArray(int size) {
+ return new CellSignalStrengthCdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellSignalStrengthGsm.java b/android/telephony/CellSignalStrengthGsm.java
new file mode 100644
index 00000000..4137853e
--- /dev/null
+++ b/android/telephony/CellSignalStrengthGsm.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * GSM signal strength related information.
+ */
+public final class CellSignalStrengthGsm extends CellSignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthGsm";
+ private static final boolean DBG = false;
+
+ private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
+ private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
+ private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
+
+ private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mTimingAdvance;
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthGsm() {
+ setDefaultValues();
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthGsm(int ss, int ber) {
+ initialize(ss, ber);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param ss SignalStrength as ASU value
+ * @param ber is Bit Error Rate
+ *
+ * @hide
+ */
+ public void initialize(int ss, int ber) {
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
+ mTimingAdvance = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param ss SignalStrength as ASU value
+ * @param ber is Bit Error Rate
+ * @param ta timing advance
+ *
+ * @hide
+ */
+ public void initialize(int ss, int ber, int ta) {
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
+ mTimingAdvance = ta;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(CellSignalStrengthGsm s) {
+ mSignalStrength = s.mSignalStrength;
+ mBitErrorRate = s.mBitErrorRate;
+ mTimingAdvance = s.mTimingAdvance;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CellSignalStrengthGsm copy() {
+ return new CellSignalStrengthGsm(this);
+ }
+
+ /** @hide */
+ @Override
+ public void setDefaultValues() {
+ mSignalStrength = Integer.MAX_VALUE;
+ mBitErrorRate = Integer.MAX_VALUE;
+ mTimingAdvance = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Get signal level as an int from 0..4
+ */
+ @Override
+ public int getLevel() {
+ int level;
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int asu = mSignalStrength;
+ if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
+ else if (asu >= GSM_SIGNAL_STRENGTH_GOOD) level = SIGNAL_STRENGTH_GOOD;
+ else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE) level = SIGNAL_STRENGTH_MODERATE;
+ else level = SIGNAL_STRENGTH_POOR;
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the GSM timing advance between 0..219 symbols (normally 0..63).
+ * Integer.MAX_VALUE is reported when there is no RR connection.
+ * Refer to 3GPP 45.010 Sec 5.8
+ * @return the current GSM timing advance, if available.
+ */
+ public int getTimingAdvance() {
+ return mTimingAdvance;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ */
+ @Override
+ public int getDbm() {
+ int dBm;
+
+ int level = mSignalStrength;
+ int asu = (level == 99 ? Integer.MAX_VALUE : level);
+ if (asu != Integer.MAX_VALUE) {
+ dBm = -113 + (2 * asu);
+ } else {
+ dBm = Integer.MAX_VALUE;
+ }
+ if (DBG) log("getDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ */
+ @Override
+ public int getAsuLevel() {
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int level = mSignalStrength;
+ if (DBG) log("getAsuLevel=" + level);
+ return level;
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ CellSignalStrengthGsm s;
+
+ try {
+ s = (CellSignalStrengthGsm) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate &&
+ s.mTimingAdvance == mTimingAdvance;
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return "CellSignalStrengthGsm:"
+ + " ss=" + mSignalStrength
+ + " ber=" + mBitErrorRate
+ + " mTa=" + mTimingAdvance;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mSignalStrength);
+ dest.writeInt(mBitErrorRate);
+ dest.writeInt(mTimingAdvance);
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellSignalStrengthGsm(Parcel in) {
+ mSignalStrength = in.readInt();
+ mBitErrorRate = in.readInt();
+ mTimingAdvance = in.readInt();
+ if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<CellSignalStrengthGsm> CREATOR =
+ new Parcelable.Creator<CellSignalStrengthGsm>() {
+ @Override
+ public CellSignalStrengthGsm createFromParcel(Parcel in) {
+ return new CellSignalStrengthGsm(in);
+ }
+
+ @Override
+ public CellSignalStrengthGsm[] newArray(int size) {
+ return new CellSignalStrengthGsm[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellSignalStrengthLte.java b/android/telephony/CellSignalStrengthLte.java
new file mode 100644
index 00000000..0d07a408
--- /dev/null
+++ b/android/telephony/CellSignalStrengthLte.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * LTE signal strength related information.
+ */
+public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthLte";
+ private static final boolean DBG = false;
+
+ private int mSignalStrength;
+ private int mRsrp;
+ private int mRsrq;
+ private int mRssnr;
+ private int mCqi;
+ private int mTimingAdvance;
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthLte() {
+ setDefaultValues();
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
+ int timingAdvance) {
+ initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public CellSignalStrengthLte(CellSignalStrengthLte s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param lteSignalStrength
+ * @param rsrp
+ * @param rsrq
+ * @param rssnr
+ * @param cqi
+ *
+ * @hide
+ */
+ public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
+ int timingAdvance) {
+ mSignalStrength = lteSignalStrength;
+ mRsrp = rsrp;
+ mRsrq = rsrq;
+ mRssnr = rssnr;
+ mCqi = cqi;
+ mTimingAdvance = timingAdvance;
+ }
+
+ /**
+ * Initialize from the SignalStrength structure.
+ *
+ * @param ss
+ *
+ * @hide
+ */
+ public void initialize(SignalStrength ss, int timingAdvance) {
+ mSignalStrength = ss.getLteSignalStrength();
+ mRsrp = ss.getLteRsrp();
+ mRsrq = ss.getLteRsrq();
+ mRssnr = ss.getLteRssnr();
+ mCqi = ss.getLteCqi();
+ mTimingAdvance = timingAdvance;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(CellSignalStrengthLte s) {
+ mSignalStrength = s.mSignalStrength;
+ mRsrp = s.mRsrp;
+ mRsrq = s.mRsrq;
+ mRssnr = s.mRssnr;
+ mCqi = s.mCqi;
+ mTimingAdvance = s.mTimingAdvance;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CellSignalStrengthLte copy() {
+ return new CellSignalStrengthLte(this);
+ }
+
+ /** @hide */
+ @Override
+ public void setDefaultValues() {
+ mSignalStrength = Integer.MAX_VALUE;
+ mRsrp = Integer.MAX_VALUE;
+ mRsrq = Integer.MAX_VALUE;
+ mRssnr = Integer.MAX_VALUE;
+ mCqi = Integer.MAX_VALUE;
+ mTimingAdvance = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Get signal level as an int from 0..4
+ */
+ @Override
+ public int getLevel() {
+ int levelRsrp = 0;
+ int levelRssnr = 0;
+
+ if (mRsrp == Integer.MAX_VALUE) levelRsrp = 0;
+ else if (mRsrp >= -95) levelRsrp = SIGNAL_STRENGTH_GREAT;
+ else if (mRsrp >= -105) levelRsrp = SIGNAL_STRENGTH_GOOD;
+ else if (mRsrp >= -115) levelRsrp = SIGNAL_STRENGTH_MODERATE;
+ else levelRsrp = SIGNAL_STRENGTH_POOR;
+
+ // See RIL_LTE_SignalStrength in ril.h
+ if (mRssnr == Integer.MAX_VALUE) levelRssnr = 0;
+ else if (mRssnr >= 45) levelRssnr = SIGNAL_STRENGTH_GREAT;
+ else if (mRssnr >= 10) levelRssnr = SIGNAL_STRENGTH_GOOD;
+ else if (mRssnr >= -30) levelRssnr = SIGNAL_STRENGTH_MODERATE;
+ else levelRssnr = SIGNAL_STRENGTH_POOR;
+
+ int level;
+ if (mRsrp == Integer.MAX_VALUE)
+ level = levelRssnr;
+ else if (mRssnr == Integer.MAX_VALUE)
+ level = levelRsrp;
+ else
+ level = (levelRssnr < levelRsrp) ? levelRssnr : levelRsrp;
+
+ if (DBG) log("Lte rsrp level: " + levelRsrp
+ + " snr level: " + levelRssnr + " level: " + level);
+ return level;
+ }
+
+ /**
+ * Get reference signal received quality
+ */
+ public int getRsrq() {
+ return mRsrq;
+ }
+
+ /**
+ * Get reference signal signal-to-noise ratio
+ */
+ public int getRssnr() {
+ return mRssnr;
+ }
+
+ /**
+ * Get reference signal received power
+ */
+ public int getRsrp() {
+ return mRsrp;
+ }
+
+ /**
+ * Get channel quality indicator
+ */
+ public int getCqi() {
+ return mCqi;
+ }
+
+ /**
+ * Get signal strength as dBm
+ */
+ @Override
+ public int getDbm() {
+ return mRsrp;
+ }
+
+ /**
+ * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ */
+ @Override
+ public int getAsuLevel() {
+ int lteAsuLevel = 99;
+ int lteDbm = getDbm();
+ if (lteDbm == Integer.MAX_VALUE) lteAsuLevel = 99;
+ else if (lteDbm <= -140) lteAsuLevel = 0;
+ else if (lteDbm >= -43) lteAsuLevel = 97;
+ else lteAsuLevel = lteDbm + 140;
+ if (DBG) log("Lte Asu level: "+lteAsuLevel);
+ return lteAsuLevel;
+ }
+
+ /**
+ * Get the timing advance value for LTE, as a value between 0..63.
+ * Integer.MAX_VALUE is reported when there is no active RRC
+ * connection. Refer to 3GPP 36.213 Sec 4.2.3
+ * @return the LTE timing advance, if available.
+ */
+ public int getTimingAdvance() {
+ return mTimingAdvance;
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return (mSignalStrength * primeNum) + (mRsrp * primeNum)
+ + (mRsrq * primeNum) + (mRssnr * primeNum) + (mCqi * primeNum)
+ + (mTimingAdvance * primeNum);
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ CellSignalStrengthLte s;
+
+ try {
+ s = (CellSignalStrengthLte) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mSignalStrength == s.mSignalStrength
+ && mRsrp == s.mRsrp
+ && mRsrq == s.mRsrq
+ && mRssnr == s.mRssnr
+ && mCqi == s.mCqi
+ && mTimingAdvance == s.mTimingAdvance;
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return "CellSignalStrengthLte:"
+ + " ss=" + mSignalStrength
+ + " rsrp=" + mRsrp
+ + " rsrq=" + mRsrq
+ + " rssnr=" + mRssnr
+ + " cqi=" + mCqi
+ + " ta=" + mTimingAdvance;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mSignalStrength);
+ // Need to multiply rsrp and rsrq by -1
+ // to ensure consistency when reading values written here
+ // unless the values are invalid
+ dest.writeInt(mRsrp * (mRsrp != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mRsrq * (mRsrq != Integer.MAX_VALUE ? -1 : 1));
+ dest.writeInt(mRssnr);
+ dest.writeInt(mCqi);
+ dest.writeInt(mTimingAdvance);
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellSignalStrengthLte(Parcel in) {
+ mSignalStrength = in.readInt();
+ // rsrp and rsrq are written into the parcel as positive values.
+ // Need to convert into negative values unless the values are invalid
+ mRsrp = in.readInt();
+ if (mRsrp != Integer.MAX_VALUE) mRsrp *= -1;
+ mRsrq = in.readInt();
+ if (mRsrq != Integer.MAX_VALUE) mRsrq *= -1;
+ mRssnr = in.readInt();
+ mCqi = in.readInt();
+ mTimingAdvance = in.readInt();
+ if (DBG) log("CellSignalStrengthLte(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<CellSignalStrengthLte> CREATOR =
+ new Parcelable.Creator<CellSignalStrengthLte>() {
+ @Override
+ public CellSignalStrengthLte createFromParcel(Parcel in) {
+ return new CellSignalStrengthLte(in);
+ }
+
+ @Override
+ public CellSignalStrengthLte[] newArray(int size) {
+ return new CellSignalStrengthLte[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/CellSignalStrengthWcdma.java b/android/telephony/CellSignalStrengthWcdma.java
new file mode 100644
index 00000000..b94b01da
--- /dev/null
+++ b/android/telephony/CellSignalStrengthWcdma.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Wcdma signal strength related information.
+ */
+public final class CellSignalStrengthWcdma extends CellSignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthWcdma";
+ private static final boolean DBG = false;
+
+ private static final int WCDMA_SIGNAL_STRENGTH_GREAT = 12;
+ private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
+ private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+
+ private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthWcdma() {
+ setDefaultValues();
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthWcdma(int ss, int ber) {
+ initialize(ss, ber);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param ss SignalStrength as ASU value
+ * @param ber is Bit Error Rate
+ *
+ * @hide
+ */
+ public void initialize(int ss, int ber) {
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(CellSignalStrengthWcdma s) {
+ mSignalStrength = s.mSignalStrength;
+ mBitErrorRate = s.mBitErrorRate;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CellSignalStrengthWcdma copy() {
+ return new CellSignalStrengthWcdma(this);
+ }
+
+ /** @hide */
+ @Override
+ public void setDefaultValues() {
+ mSignalStrength = Integer.MAX_VALUE;
+ mBitErrorRate = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Get signal level as an int from 0..4
+ */
+ @Override
+ public int getLevel() {
+ int level;
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int asu = mSignalStrength;
+ if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (asu >= WCDMA_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
+ else if (asu >= WCDMA_SIGNAL_STRENGTH_GOOD) level = SIGNAL_STRENGTH_GOOD;
+ else if (asu >= WCDMA_SIGNAL_STRENGTH_MODERATE) level = SIGNAL_STRENGTH_MODERATE;
+ else level = SIGNAL_STRENGTH_POOR;
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ */
+ @Override
+ public int getDbm() {
+ int dBm;
+
+ int level = mSignalStrength;
+ int asu = (level == 99 ? Integer.MAX_VALUE : level);
+ if (asu != Integer.MAX_VALUE) {
+ dBm = -113 + (2 * asu);
+ } else {
+ dBm = Integer.MAX_VALUE;
+ }
+ if (DBG) log("getDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ */
+ @Override
+ public int getAsuLevel() {
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int level = mSignalStrength;
+ if (DBG) log("getAsuLevel=" + level);
+ return level;
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ CellSignalStrengthWcdma s;
+
+ try {
+ s = (CellSignalStrengthWcdma) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate;
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return "CellSignalStrengthWcdma:"
+ + " ss=" + mSignalStrength
+ + " ber=" + mBitErrorRate;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mSignalStrength);
+ dest.writeInt(mBitErrorRate);
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellSignalStrengthWcdma(Parcel in) {
+ mSignalStrength = in.readInt();
+ mBitErrorRate = in.readInt();
+ if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<CellSignalStrengthWcdma> CREATOR =
+ new Parcelable.Creator<CellSignalStrengthWcdma>() {
+ @Override
+ public CellSignalStrengthWcdma createFromParcel(Parcel in) {
+ return new CellSignalStrengthWcdma(in);
+ }
+
+ @Override
+ public CellSignalStrengthWcdma[] newArray(int size) {
+ return new CellSignalStrengthWcdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/ClientRequestStats.java b/android/telephony/ClientRequestStats.java
new file mode 100644
index 00000000..381c847d
--- /dev/null
+++ b/android/telephony/ClientRequestStats.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyHistogram;
+import android.util.SparseArray;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parcelable class to store Client request statistics information.
+ *
+ * @hide
+ */
+public final class ClientRequestStats implements Parcelable {
+ public static final Parcelable.Creator<ClientRequestStats> CREATOR =
+ new Parcelable.Creator<ClientRequestStats>() {
+
+ public ClientRequestStats createFromParcel(Parcel in) {
+ return new ClientRequestStats(in);
+ }
+
+ public ClientRequestStats[] newArray(int size) {
+ return new ClientRequestStats[size];
+ }
+ };
+ private static final int REQUEST_HISTOGRAM_BUCKET_COUNT = 5;
+ private String mCallingPackage;
+ /* completed requests wake lock time in milli seconds */
+ private long mCompletedRequestsWakelockTime = 0;
+ private long mCompletedRequestsCount = 0;
+ private long mPendingRequestsWakelockTime = 0;
+ private long mPendingRequestsCount = 0;
+ private SparseArray<TelephonyHistogram> mRequestHistograms =
+ new SparseArray<TelephonyHistogram>();
+
+ public ClientRequestStats(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public ClientRequestStats() {
+ }
+
+ public ClientRequestStats(ClientRequestStats clientRequestStats) {
+ mCallingPackage = clientRequestStats.getCallingPackage();
+ mCompletedRequestsCount = clientRequestStats.getCompletedRequestsCount();
+ mCompletedRequestsWakelockTime = clientRequestStats.getCompletedRequestsWakelockTime();
+ mPendingRequestsCount = clientRequestStats.getPendingRequestsCount();
+ mPendingRequestsWakelockTime = clientRequestStats.getPendingRequestsWakelockTime();
+
+ List<TelephonyHistogram> list = clientRequestStats.getRequestHistograms();
+ for (TelephonyHistogram entry : list) {
+ mRequestHistograms.put(entry.getId(), entry);
+ }
+ }
+
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ public void setCallingPackage(String mCallingPackage) {
+ this.mCallingPackage = mCallingPackage;
+ }
+
+ public long getCompletedRequestsWakelockTime() {
+ return mCompletedRequestsWakelockTime;
+ }
+
+ public void addCompletedWakelockTime(long completedRequestsWakelockTime) {
+ this.mCompletedRequestsWakelockTime += completedRequestsWakelockTime;
+ }
+
+ public long getPendingRequestsWakelockTime() {
+ return mPendingRequestsWakelockTime;
+ }
+
+ public void setPendingRequestsWakelockTime(long pendingRequestsWakelockTime) {
+ this.mPendingRequestsWakelockTime = pendingRequestsWakelockTime;
+ }
+
+ public long getCompletedRequestsCount() {
+ return mCompletedRequestsCount;
+ }
+
+ public void incrementCompletedRequestsCount() {
+ this.mCompletedRequestsCount++;
+ }
+
+ public long getPendingRequestsCount() {
+ return mPendingRequestsCount;
+ }
+
+ public void setPendingRequestsCount(long pendingRequestsCount) {
+ this.mPendingRequestsCount = pendingRequestsCount;
+ }
+
+ public List<TelephonyHistogram> getRequestHistograms() {
+ List<TelephonyHistogram> list;
+ synchronized (mRequestHistograms) {
+ list = new ArrayList<>(mRequestHistograms.size());
+ for (int i = 0; i < mRequestHistograms.size(); i++) {
+ TelephonyHistogram entry = new TelephonyHistogram(mRequestHistograms.valueAt(i));
+ list.add(entry);
+ }
+ }
+ return list;
+ }
+
+ public void updateRequestHistograms(int requestId, int time) {
+ synchronized (mRequestHistograms) {
+ TelephonyHistogram entry = mRequestHistograms.get(requestId);
+ if (entry == null) {
+ entry = new TelephonyHistogram(TelephonyHistogram.TELEPHONY_CATEGORY_RIL,
+ requestId, REQUEST_HISTOGRAM_BUCKET_COUNT);
+ mRequestHistograms.put(requestId, entry);
+ }
+ entry.addTimeTaken(time);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClientRequestStats{" +
+ "mCallingPackage='" + mCallingPackage + '\'' +
+ ", mCompletedRequestsWakelockTime=" + mCompletedRequestsWakelockTime +
+ ", mCompletedRequestsCount=" + mCompletedRequestsCount +
+ ", mPendingRequestsWakelockTime=" + mPendingRequestsWakelockTime +
+ ", mPendingRequestsCount=" + mPendingRequestsCount +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public void readFromParcel(Parcel in) {
+ mCallingPackage = in.readString();
+ mCompletedRequestsWakelockTime = in.readLong();
+ mCompletedRequestsCount = in.readLong();
+ mPendingRequestsWakelockTime = in.readLong();
+ mPendingRequestsCount = in.readLong();
+ ArrayList<TelephonyHistogram> requestHistograms = new ArrayList<TelephonyHistogram>();
+ in.readTypedList(requestHistograms, TelephonyHistogram.CREATOR);
+ for (TelephonyHistogram h : requestHistograms) {
+ mRequestHistograms.put(h.getId(), h);
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mCallingPackage);
+ dest.writeLong(mCompletedRequestsWakelockTime);
+ dest.writeLong(mCompletedRequestsCount);
+ dest.writeLong(mPendingRequestsWakelockTime);
+ dest.writeLong(mPendingRequestsCount);
+ dest.writeTypedList(getRequestHistograms());
+ }
+}
diff --git a/android/telephony/DataConnectionRealTimeInfo.java b/android/telephony/DataConnectionRealTimeInfo.java
new file mode 100644
index 00000000..f71f58d0
--- /dev/null
+++ b/android/telephony/DataConnectionRealTimeInfo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data connection real time information
+ *
+ * TODO: How to handle multiple subscriptions?
+ * @hide
+ */
+public class DataConnectionRealTimeInfo implements Parcelable {
+ private long mTime; // Time the info was collected since boot in nanos;
+
+ public static final int DC_POWER_STATE_LOW = 1;
+ public static final int DC_POWER_STATE_MEDIUM = 2;
+ public static final int DC_POWER_STATE_HIGH = 3;
+ public static final int DC_POWER_STATE_UNKNOWN = Integer.MAX_VALUE;
+
+ private int mDcPowerState; // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public DataConnectionRealTimeInfo(long time, int dcPowerState) {
+ mTime = time;
+ mDcPowerState = dcPowerState;
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public DataConnectionRealTimeInfo() {
+ mTime = Long.MAX_VALUE;
+ mDcPowerState = DC_POWER_STATE_UNKNOWN;
+ }
+
+ /**
+ * Construct a PreciseCallState object from the given parcel.
+ */
+ private DataConnectionRealTimeInfo(Parcel in) {
+ mTime = in.readLong();
+ mDcPowerState = in.readInt();
+ }
+
+ /**
+ * @return time the information was collected or Long.MAX_VALUE if unknown
+ */
+ public long getTime() {
+ return mTime;
+ }
+
+ /**
+ * @return DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
+ */
+ public int getDcPowerState() {
+ return mDcPowerState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mTime);
+ out.writeInt(mDcPowerState);
+ }
+
+ public static final Parcelable.Creator<DataConnectionRealTimeInfo> CREATOR
+ = new Parcelable.Creator<DataConnectionRealTimeInfo>() {
+
+ @Override
+ public DataConnectionRealTimeInfo createFromParcel(Parcel in) {
+ return new DataConnectionRealTimeInfo(in);
+ }
+
+ @Override
+ public DataConnectionRealTimeInfo[] newArray(int size) {
+ return new DataConnectionRealTimeInfo[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ final long prime = 17;
+ long result = 1;
+ result = (prime * result) + mTime;
+ result += (prime * result) + mDcPowerState;
+ return (int)result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ DataConnectionRealTimeInfo other = (DataConnectionRealTimeInfo) obj;
+ return (mTime == other.mTime)
+ && (mDcPowerState == other.mDcPowerState);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("mTime=").append(mTime);
+ sb.append(" mDcPowerState=").append(mDcPowerState);
+
+ return sb.toString();
+ }
+}
diff --git a/android/telephony/DisconnectCause.java b/android/telephony/DisconnectCause.java
new file mode 100644
index 00000000..98fb6534
--- /dev/null
+++ b/android/telephony/DisconnectCause.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Contains disconnect call causes generated by the framework and the RIL.
+ * @hide
+ */
+public class DisconnectCause {
+
+ /** The disconnect cause is not valid (Not received a disconnect cause) */
+ public static final int NOT_VALID = -1;
+ /** Has not yet disconnected */
+ public static final int NOT_DISCONNECTED = 0;
+ /** An incoming call that was missed and never answered */
+ public static final int INCOMING_MISSED = 1;
+ /** Normal; Remote hangup*/
+ public static final int NORMAL = 2;
+ /** Normal; Local hangup */
+ public static final int LOCAL = 3;
+ /** Outgoing call to busy line */
+ public static final int BUSY = 4;
+ /** Outgoing call to congested network */
+ public static final int CONGESTION = 5;
+ /** Not presently used */
+ public static final int MMI = 6;
+ /** Invalid dial string */
+ public static final int INVALID_NUMBER = 7;
+ /** Cannot reach the peer */
+ public static final int NUMBER_UNREACHABLE = 8;
+ /** Cannot reach the server */
+ public static final int SERVER_UNREACHABLE = 9;
+ /** Invalid credentials */
+ public static final int INVALID_CREDENTIALS = 10;
+ /** Calling from out of network is not allowed */
+ public static final int OUT_OF_NETWORK = 11;
+ /** Server error */
+ public static final int SERVER_ERROR = 12;
+ /** Client timed out */
+ public static final int TIMED_OUT = 13;
+ /** Client went out of network range */
+ public static final int LOST_SIGNAL = 14;
+ /** GSM or CDMA ACM limit exceeded */
+ public static final int LIMIT_EXCEEDED = 15;
+ /** An incoming call that was rejected */
+ public static final int INCOMING_REJECTED = 16;
+ /** Radio is turned off explicitly */
+ public static final int POWER_OFF = 17;
+ /** Out of service */
+ public static final int OUT_OF_SERVICE = 18;
+ /** No ICC, ICC locked, or other ICC error */
+ public static final int ICC_ERROR = 19;
+ /** Call was blocked by call barring */
+ public static final int CALL_BARRED = 20;
+ /** Call was blocked by fixed dial number */
+ public static final int FDN_BLOCKED = 21;
+ /** Call was blocked by restricted all voice access */
+ public static final int CS_RESTRICTED = 22;
+ /** Call was blocked by restricted normal voice access */
+ public static final int CS_RESTRICTED_NORMAL = 23;
+ /** Call was blocked by restricted emergency voice access */
+ public static final int CS_RESTRICTED_EMERGENCY = 24;
+ /** Unassigned number */
+ public static final int UNOBTAINABLE_NUMBER = 25;
+ /** MS is locked until next power cycle */
+ public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26;
+ /** Drop call*/
+ public static final int CDMA_DROP = 27;
+ /** INTERCEPT order received, MS state idle entered */
+ public static final int CDMA_INTERCEPT = 28;
+ /** MS has been redirected, call is cancelled */
+ public static final int CDMA_REORDER = 29;
+ /** Service option rejection */
+ public static final int CDMA_SO_REJECT = 30;
+ /** Requested service is rejected, retry delay is set */
+ public static final int CDMA_RETRY_ORDER = 31;
+ /** Unable to obtain access to the CDMA system */
+ public static final int CDMA_ACCESS_FAILURE = 32;
+ /** Not a preempted call */
+ public static final int CDMA_PREEMPTED = 33;
+ /** Not an emergency call */
+ public static final int CDMA_NOT_EMERGENCY = 34;
+ /** Access Blocked by CDMA network */
+ public static final int CDMA_ACCESS_BLOCKED = 35;
+ /** Unknown error or not specified */
+ public static final int ERROR_UNSPECIFIED = 36;
+ /**
+ * Only emergency numbers are allowed, but we tried to dial
+ * a non-emergency number.
+ */
+ // TODO: This should be the same as NOT_EMERGENCY
+ public static final int EMERGENCY_ONLY = 37;
+ /**
+ * The supplied CALL Intent didn't contain a valid phone number.
+ */
+ public static final int NO_PHONE_NUMBER_SUPPLIED = 38;
+ /**
+ * Our initial phone number was actually an MMI sequence.
+ */
+ public static final int DIALED_MMI = 39;
+ /**
+ * We tried to call a voicemail: URI but the device has no
+ * voicemail number configured.
+ */
+ public static final int VOICEMAIL_NUMBER_MISSING = 40;
+ /**
+ * This status indicates that InCallScreen should display the
+ * CDMA-specific "call lost" dialog. (If an outgoing call fails,
+ * and the CDMA "auto-retry" feature is enabled, *and* the retried
+ * call fails too, we display this specific dialog.)
+ *
+ * TODO: this is currently unused, since the "call lost" dialog
+ * needs to be triggered by a *disconnect* event, rather than when
+ * the InCallScreen first comes to the foreground. For now we use
+ * the needToShowCallLostDialog field for this (see below.)
+ */
+ public static final int CDMA_CALL_LOST = 41;
+ /**
+ * This status indicates that the call was placed successfully,
+ * but additionally, the InCallScreen needs to display the
+ * "Exiting ECM" dialog.
+ *
+ * (Details: "Emergency callback mode" is a CDMA-specific concept
+ * where the phone disallows data connections over the cell
+ * network for some period of time after you make an emergency
+ * call. If the phone is in ECM and you dial a non-emergency
+ * number, that automatically *cancels* ECM, but we additionally
+ * need to warn the user that ECM has been canceled (see bug
+ * 4207607.))
+ *
+ * TODO: Rethink where the best place to put this is. It is not a notification
+ * of a failure of the connection -- it is an additional message that accompanies
+ * a successful connection giving the user important information about what happened.
+ *
+ * {@hide}
+ */
+ public static final int EXITED_ECM = 42;
+
+ /**
+ * The outgoing call failed with an unknown cause.
+ */
+ public static final int OUTGOING_FAILURE = 43;
+
+ /**
+ * The outgoing call was canceled by the {@link android.telecom.ConnectionService}.
+ */
+ public static final int OUTGOING_CANCELED = 44;
+
+ /**
+ * The call, which was an IMS call, disconnected because it merged with another call.
+ */
+ public static final int IMS_MERGED_SUCCESSFULLY = 45;
+
+ /**
+ * Stk Call Control modified DIAL request to USSD request.
+ * {@hide}
+ */
+ public static final int DIAL_MODIFIED_TO_USSD = 46;
+ /**
+ * Stk Call Control modified DIAL request to SS request.
+ * {@hide}
+ */
+ public static final int DIAL_MODIFIED_TO_SS = 47;
+ /**
+ * Stk Call Control modified DIAL request to DIAL with modified data.
+ * {@hide}
+ */
+ public static final int DIAL_MODIFIED_TO_DIAL = 48;
+
+ /**
+ * The call was terminated because CDMA phone service and roaming have already been activated.
+ * {@hide}
+ */
+ public static final int CDMA_ALREADY_ACTIVATED = 49;
+
+ /**
+ * The call was terminated because it is not possible to place a video call while TTY is
+ * enabled.
+ * {@hide}
+ */
+ public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
+
+ /**
+ * The call was terminated because it was pulled to another device.
+ * {@hide}
+ */
+ public static final int CALL_PULLED = 51;
+
+ /**
+ * The call was terminated because it was answered on another device.
+ * {@hide}
+ */
+ public static final int ANSWERED_ELSEWHERE = 52;
+
+ /**
+ * The call was terminated because the maximum allowable number of calls has been reached.
+ * {@hide}
+ */
+ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
+
+ /**
+ * The call was terminated because cellular data has been disabled.
+ * Used when in a video call and the user disables cellular data via the settings.
+ * {@hide}
+ */
+ public static final int DATA_DISABLED = 54;
+
+ /**
+ * The call was terminated because the data policy has disabled cellular data.
+ * Used when in a video call and the user has exceeded the device data limit.
+ * {@hide}
+ */
+ public static final int DATA_LIMIT_REACHED = 55;
+
+ /**
+ * The call being placed was detected as a call forwarding number and was being dialed while
+ * roaming on a carrier that does not allow this.
+ */
+ public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57;
+
+ /**
+ * The network does not accept the emergency call request because IMEI was used as
+ * identification and this cability is not supported by the network.
+ * {@hide}
+ */
+ public static final int IMEI_NOT_ACCEPTED = 58;
+
+ /**
+ * A call over WIFI was disconnected because the WIFI signal was lost or became too degraded to
+ * continue the call.
+ */
+ public static final int WIFI_LOST = 59;
+
+ /**
+ * The call has failed because of access class barring.
+ * {@hide}
+ */
+ public static final int IMS_ACCESS_BLOCKED = 60;
+
+ /**
+ * The call has ended (mid-call) because the device's battery is too low.
+ */
+ public static final int LOW_BATTERY = 61;
+
+ /**
+ * A call was not dialed because the device's battery is too low.
+ */
+ public static final int DIAL_LOW_BATTERY = 62;
+
+ /**
+ * Emergency call failed with a temporary fail cause and can be redialed on this slot.
+ * {@hide}
+ */
+ public static final int EMERGENCY_TEMP_FAILURE = 63;
+
+ /**
+ * Emergency call failed with a permanent fail cause and should not be redialed on this
+ * slot.
+ * {@hide}
+ */
+ public static final int EMERGENCY_PERM_FAILURE = 64;
+ //*********************************************************************************************
+ // When adding a disconnect type:
+ // 1) Update toString() with the newly added disconnect type.
+ // 2) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
+ //*********************************************************************************************
+
+ /** Private constructor to avoid class instantiation. */
+ private DisconnectCause() {
+ // Do nothing.
+ }
+
+ /** Returns descriptive string for the specified disconnect cause. */
+ public static String toString(int cause) {
+ switch (cause) {
+ case NOT_DISCONNECTED:
+ return "NOT_DISCONNECTED";
+ case INCOMING_MISSED:
+ return "INCOMING_MISSED";
+ case NORMAL:
+ return "NORMAL";
+ case LOCAL:
+ return "LOCAL";
+ case BUSY:
+ return "BUSY";
+ case CONGESTION:
+ return "CONGESTION";
+ case INVALID_NUMBER:
+ return "INVALID_NUMBER";
+ case NUMBER_UNREACHABLE:
+ return "NUMBER_UNREACHABLE";
+ case SERVER_UNREACHABLE:
+ return "SERVER_UNREACHABLE";
+ case INVALID_CREDENTIALS:
+ return "INVALID_CREDENTIALS";
+ case OUT_OF_NETWORK:
+ return "OUT_OF_NETWORK";
+ case SERVER_ERROR:
+ return "SERVER_ERROR";
+ case TIMED_OUT:
+ return "TIMED_OUT";
+ case LOST_SIGNAL:
+ return "LOST_SIGNAL";
+ case LIMIT_EXCEEDED:
+ return "LIMIT_EXCEEDED";
+ case INCOMING_REJECTED:
+ return "INCOMING_REJECTED";
+ case POWER_OFF:
+ return "POWER_OFF";
+ case OUT_OF_SERVICE:
+ return "OUT_OF_SERVICE";
+ case ICC_ERROR:
+ return "ICC_ERROR";
+ case CALL_BARRED:
+ return "CALL_BARRED";
+ case FDN_BLOCKED:
+ return "FDN_BLOCKED";
+ case CS_RESTRICTED:
+ return "CS_RESTRICTED";
+ case CS_RESTRICTED_NORMAL:
+ return "CS_RESTRICTED_NORMAL";
+ case CS_RESTRICTED_EMERGENCY:
+ return "CS_RESTRICTED_EMERGENCY";
+ case UNOBTAINABLE_NUMBER:
+ return "UNOBTAINABLE_NUMBER";
+ case CDMA_LOCKED_UNTIL_POWER_CYCLE:
+ return "CDMA_LOCKED_UNTIL_POWER_CYCLE";
+ case CDMA_DROP:
+ return "CDMA_DROP";
+ case CDMA_INTERCEPT:
+ return "CDMA_INTERCEPT";
+ case CDMA_REORDER:
+ return "CDMA_REORDER";
+ case CDMA_SO_REJECT:
+ return "CDMA_SO_REJECT";
+ case CDMA_RETRY_ORDER:
+ return "CDMA_RETRY_ORDER";
+ case CDMA_ACCESS_FAILURE:
+ return "CDMA_ACCESS_FAILURE";
+ case CDMA_PREEMPTED:
+ return "CDMA_PREEMPTED";
+ case CDMA_NOT_EMERGENCY:
+ return "CDMA_NOT_EMERGENCY";
+ case CDMA_ACCESS_BLOCKED:
+ return "CDMA_ACCESS_BLOCKED";
+ case EMERGENCY_ONLY:
+ return "EMERGENCY_ONLY";
+ case NO_PHONE_NUMBER_SUPPLIED:
+ return "NO_PHONE_NUMBER_SUPPLIED";
+ case DIALED_MMI:
+ return "DIALED_MMI";
+ case VOICEMAIL_NUMBER_MISSING:
+ return "VOICEMAIL_NUMBER_MISSING";
+ case CDMA_CALL_LOST:
+ return "CDMA_CALL_LOST";
+ case EXITED_ECM:
+ return "EXITED_ECM";
+ case DIAL_MODIFIED_TO_USSD:
+ return "DIAL_MODIFIED_TO_USSD";
+ case DIAL_MODIFIED_TO_SS:
+ return "DIAL_MODIFIED_TO_SS";
+ case DIAL_MODIFIED_TO_DIAL:
+ return "DIAL_MODIFIED_TO_DIAL";
+ case ERROR_UNSPECIFIED:
+ return "ERROR_UNSPECIFIED";
+ case OUTGOING_FAILURE:
+ return "OUTGOING_FAILURE";
+ case OUTGOING_CANCELED:
+ return "OUTGOING_CANCELED";
+ case IMS_MERGED_SUCCESSFULLY:
+ return "IMS_MERGED_SUCCESSFULLY";
+ case CDMA_ALREADY_ACTIVATED:
+ return "CDMA_ALREADY_ACTIVATED";
+ case VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
+ return "VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED";
+ case CALL_PULLED:
+ return "CALL_PULLED";
+ case ANSWERED_ELSEWHERE:
+ return "ANSWERED_ELSEWHERE";
+ case MAXIMUM_NUMBER_OF_CALLS_REACHED:
+ return "MAXIMUM_NUMER_OF_CALLS_REACHED";
+ case DATA_DISABLED:
+ return "DATA_DISABLED";
+ case DATA_LIMIT_REACHED:
+ return "DATA_LIMIT_REACHED";
+ case DIALED_CALL_FORWARDING_WHILE_ROAMING:
+ return "DIALED_CALL_FORWARDING_WHILE_ROAMING";
+ case IMEI_NOT_ACCEPTED:
+ return "IMEI_NOT_ACCEPTED";
+ case WIFI_LOST:
+ return "WIFI_LOST";
+ case IMS_ACCESS_BLOCKED:
+ return "IMS_ACCESS_BLOCKED";
+ case LOW_BATTERY:
+ return "LOW_BATTERY";
+ case DIAL_LOW_BATTERY:
+ return "DIAL_LOW_BATTERY";
+ case EMERGENCY_TEMP_FAILURE:
+ return "EMERGENCY_TEMP_FAILURE";
+ case EMERGENCY_PERM_FAILURE:
+ return "EMERGENCY_PERM_FAILURE";
+ default:
+ return "INVALID: " + cause;
+ }
+ }
+}
diff --git a/android/telephony/IccOpenLogicalChannelResponse.java b/android/telephony/IccOpenLogicalChannelResponse.java
new file mode 100644
index 00000000..4621f917
--- /dev/null
+++ b/android/telephony/IccOpenLogicalChannelResponse.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+/**
+ * Response to the {@link TelephonyManager#iccOpenLogicalChannel} command.
+ */
+public class IccOpenLogicalChannelResponse implements Parcelable {
+ /**
+ * Indicates an invalid channel.
+ */
+ public static final int INVALID_CHANNEL = -1;
+
+ /**
+ * Possible status values returned by open channel command.
+ *
+ * STATUS_NO_ERROR: Open channel command returned successfully.
+ * STATUS_MISSING_RESOURCE: No logical channels available.
+ * STATUS_NO_SUCH_ELEMENT: AID not found on UICC.
+ * STATUS_UNKNOWN_ERROR: Unknown error in open channel command.
+ */
+ public static final int STATUS_NO_ERROR = 1;
+ public static final int STATUS_MISSING_RESOURCE = 2;
+ public static final int STATUS_NO_SUCH_ELEMENT = 3;
+ public static final int STATUS_UNKNOWN_ERROR = 4;
+
+ private final int mChannel;
+ private final int mStatus;
+ private final byte[] mSelectResponse;
+
+ /**
+ * Constructor.
+ *
+ * @hide
+ */
+ public IccOpenLogicalChannelResponse(int channel, int status, byte[] selectResponse) {
+ mChannel = channel;
+ mStatus = status;
+ mSelectResponse = selectResponse;
+ }
+
+ /**
+ * Construct a IccOpenLogicalChannelResponse from a given parcel.
+ */
+ private IccOpenLogicalChannelResponse(Parcel in) {
+ mChannel = in.readInt();
+ mStatus = in.readInt();
+ int arrayLength = in.readInt();
+ if (arrayLength > 0) {
+ mSelectResponse = new byte[arrayLength];
+ in.readByteArray(mSelectResponse);
+ } else {
+ mSelectResponse = null;
+ }
+ }
+
+ /**
+ * @return the channel id.
+ */
+ public int getChannel() {
+ return mChannel;
+ }
+
+ /**
+ * @return the status of the command.
+ */
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * @return the select response.
+ */
+ public byte[] getSelectResponse() {
+ return mSelectResponse;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mChannel);
+ out.writeInt(mStatus);
+ if (mSelectResponse != null && mSelectResponse.length > 0) {
+ out.writeInt(mSelectResponse.length);
+ out.writeByteArray(mSelectResponse);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ public static final Parcelable.Creator<IccOpenLogicalChannelResponse> CREATOR
+ = new Parcelable.Creator<IccOpenLogicalChannelResponse>() {
+
+ @Override
+ public IccOpenLogicalChannelResponse createFromParcel(Parcel in) {
+ return new IccOpenLogicalChannelResponse(in);
+ }
+
+ public IccOpenLogicalChannelResponse[] newArray(int size) {
+ return new IccOpenLogicalChannelResponse[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "Channel: " + mChannel + " Status: " + mStatus;
+ }
+}
diff --git a/android/telephony/ImsiEncryptionInfo.java b/android/telephony/ImsiEncryptionInfo.java
new file mode 100644
index 00000000..d2680ad6
--- /dev/null
+++ b/android/telephony/ImsiEncryptionInfo.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.Date;
+import android.util.Log;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * Class to represent information sent by the carrier, which will be used to encrypt
+ * the IMSI + IMPI. The ecryption is being done by WLAN, and the modem.
+ *
+ * @hide
+ */
+public final class ImsiEncryptionInfo implements Parcelable {
+
+ private static final String LOG_TAG = "ImsiEncryptionInfo";
+
+
+ private final String mcc;
+ private final String mnc;
+ private final PublicKey publicKey;
+ private final String keyIdentifier;
+ private final int keyType;
+ //Date-Time in UTC when the key will expire.
+ private final Date expirationTime;
+
+ public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
+ byte[] key, Date expirationTime) {
+ this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime);
+ }
+
+ public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
+ PublicKey publicKey, Date expirationTime) {
+ // todo need to validate that ImsiEncryptionInfo is being created with the correct params.
+ // Including validating that the public key is in "X.509" format. This will be done in
+ // a subsequent CL.
+ this.mcc = mcc;
+ this.mnc = mnc;
+ this.keyType = keyType;
+ this.publicKey = publicKey;
+ this.keyIdentifier = keyIdentifier;
+ this.expirationTime = expirationTime;
+ }
+
+ public ImsiEncryptionInfo(Parcel in) {
+ int length = in.readInt();
+ byte b[] = new byte[length];
+ in.readByteArray(b);
+ publicKey = makeKeyObject(b);
+ mcc = in.readString();
+ mnc = in.readString();
+ keyIdentifier = in.readString();
+ keyType = in.readInt();
+ expirationTime = new Date(in.readLong());
+ }
+
+ public String getMnc() {
+ return this.mnc;
+ }
+
+ public String getMcc() {
+ return this.mcc;
+ }
+
+ public String getKeyIdentifier() {
+ return this.keyIdentifier;
+ }
+
+ public int getKeyType() {
+ return this.keyType;
+ }
+
+ public PublicKey getPublicKey() {
+ return this.publicKey;
+ }
+
+ public Date getExpirationTime() {
+ return this.expirationTime;
+ }
+
+ private static PublicKey makeKeyObject(byte[] publicKeyBytes) {
+ try {
+ X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyBytes);
+ return KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException ex) {
+ Log.e(LOG_TAG, "Error makeKeyObject: unable to convert into PublicKey", ex);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ImsiEncryptionInfo> CREATOR =
+ new Parcelable.Creator<ImsiEncryptionInfo>() {
+ @Override
+ public ImsiEncryptionInfo createFromParcel(Parcel in) {
+ return new ImsiEncryptionInfo(in);
+ }
+
+ @Override
+ public ImsiEncryptionInfo[] newArray(int size) {
+ return new ImsiEncryptionInfo[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ byte[] b = publicKey.getEncoded();
+ dest.writeInt(b.length);
+ dest.writeByteArray(b);
+ dest.writeString(mcc);
+ dest.writeString(mnc);
+ dest.writeString(keyIdentifier);
+ dest.writeInt(keyType);
+ dest.writeLong(expirationTime.getTime());
+ }
+
+ @Override
+ public String toString(){
+ return "[ImsiEncryptionInfo "
+ + "mcc=" + mcc
+ + "mnc=" + mnc
+ + "publicKey=" + publicKey
+ + ", keyIdentifier=" + keyIdentifier
+ + ", keyType=" + keyType
+ + ", expirationTime=" + expirationTime
+ + "]";
+ }
+}
diff --git a/android/telephony/JapanesePhoneNumberFormatter.java b/android/telephony/JapanesePhoneNumberFormatter.java
new file mode 100644
index 00000000..f5e53ef1
--- /dev/null
+++ b/android/telephony/JapanesePhoneNumberFormatter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.text.Editable;
+
+/*
+ * Japanese Phone number formatting rule is a bit complicated.
+ * Here are some valid examples:
+ *
+ * 022-229-1234 0223-23-1234 022-301-9876 015-482-7849 0154-91-3478
+ * 01547-5-4534 090-1234-1234 080-0123-6789
+ * 050-0000-0000 060-0000-0000
+ * 0800-000-9999 0570-000-000 0276-00-0000
+ *
+ * As you can see, there is no straight-forward rule here.
+ * In order to handle this, a big array is prepared.
+ */
+/* package */ class JapanesePhoneNumberFormatter {
+ private static short FORMAT_MAP[] = {
+ -100, 10, 220, -15, 410, 530, 1200, 670, 780, 1060,
+ -100, -25, 20, 40, 70, 100, 150, 190, 200, 210,
+ -36, -100, -100, -35, -35, -35, 30, -100, -100, -100,
+ -35, -35, -35, -35, -35, -35, -35, -45, -35, -35,
+ -100, -100, -100, -35, -35, -35, -35, 50, -35, 60,
+ -35, -35, -45, -35, -45, -35, -35, -45, -35, -35,
+ -35, -35, -45, -35, -35, -35, -35, -45, -45, -35,
+ -100, -100, -35, -35, -35, 80, 90, -100, -100, -100,
+ -35, -35, -35, -35, -35, -35, -45, -45, -35, -35,
+ -35, -35, -35, -35, -35, -35, -45, -35, -35, -35,
+ -25, -25, -35, -35, 110, 120, 130, -35, 140, -25,
+ -35, -25, -35, -35, -35, -35, -35, -45, -25, -35,
+ -35, -25, -35, -35, -35, -35, -35, -25, -45, -35,
+ -35, -35, -35, -35, -45, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -45, -45, -35, -35,
+ -100, -100, -35, 160, 170, 180, -35, -35, -100, -100,
+ -35, -35, -45, -35, -45, -45, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -45, -35,
+ -35, -35, -35, -35, -45, -45, -45, -35, -45, -35,
+ -25, -25, -35, -35, -35, -35, -35, -25, -35, -35,
+ -25, -25, -35, -35, -35, -35, -35, -35, -25, -25,
+ -25, -35, -35, -35, -35, -35, -25, -35, -35, -25,
+ -100, -100, 230, 250, 260, 270, 320, 340, 360, 390,
+ -35, -25, -25, 240, -35, -35, -35, -25, -35, -35,
+ -25, -35, -35, -35, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -35, -35, -35, -25, -35, -35, -25,
+ -35, -35, -35, -35, -35, -25, -35, -35, -35, -25,
+ -35, -25, -25, -25, -35, 280, 290, 300, 310, -35,
+ -25, -25, -25, -25, -25, -25, -25, -35, -35, -25,
+ -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -25, -25, -35, -35, -35, -25, -25, -25, -25, -25,
+ -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -25, -35, 330, -35, -35, -35, -35, -35,
+ -25, -35, -35, -35, -35, -35, -25, -25, -25, -25,
+ -35, -25, -25, -25, -35, -25, -35, -35, 350, -35,
+ -25, -35, -35, -35, -35, -35, -35, -35, -25, -25,
+ -35, -25, -35, 370, -35, -35, -25, -35, -35, 380,
+ -25, -35, -35, -25, -25, -35, -35, -35, -35, -35,
+ -25, -35, -25, -25, -25, -25, -35, -35, -35, -35,
+ -25, -35, -25, 400, -35, -35, -35, -35, -25, -35,
+ -25, -35, -35, -35, -35, -25, -25, -25, -25, -25,
+ -15, -15, 420, 460, -25, -25, 470, 480, 500, 510,
+ -15, -25, 430, -25, -25, -25, -25, -25, 440, 450,
+ -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -25, -25, -35, -35, -25, -25, -25, -35, -35, -35,
+ -15, -25, -15, -15, -15, -15, -15, -25, -25, -15,
+ -25, -25, -25, -25, -25, -25, -35, -25, -35, -35,
+ -35, -25, -25, -35, -25, -35, -35, -35, -25, -25,
+ 490, -15, -25, -25, -25, -35, -35, -25, -35, -35,
+ -15, -35, -35, -35, -35, -35, -35, -35, -35, -15,
+ -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -35, -35, -35, -25, -25, -25, 520,
+ -100, -100, -45, -100, -45, -100, -45, -100, -45, -100,
+ -26, -100, -25, 540, 580, 590, 600, 610, 630, 640,
+ -25, -35, -35, -35, -25, -25, -35, -35, -35, 550,
+ -35, -35, -25, -25, -25, -25, 560, 570, -25, -35,
+ -35, -35, -35, -35, -25, -25, -25, -25, -25, -25,
+ -25, -25, -25, -25, -35, -25, -25, -35, -25, -25,
+ -25, -25, -25, -25, -35, -35, -25, -35, -35, -25,
+ -35, -35, -25, -35, -35, -35, -35, -35, -35, -25,
+ -100, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -36, -100, -35, -35, -35, -35, 620, -35, -35, -100,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -45,
+ -25, -35, -25, -25, -35, -35, -35, -35, -25, -25,
+ -25, -25, -25, -25, -35, -35, -35, 650, -35, 660,
+ -35, -35, -35, -35, -45, -35, -35, -35, -35, -45,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -25,
+ -26, -100, 680, 690, 700, -25, 720, 730, -25, 740,
+ -25, -35, -25, -25, -25, -35, -25, -25, -25, -25,
+ -25, -25, -25, -25, -25, -35, -35, -35, -35, -35,
+ -35, -100, -35, -35, -35, -35, 710, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -45, -35,
+ -25, -35, -25, -35, -25, -35, -35, -35, -35, -25,
+ -35, -35, -35, -35, -35, -25, -35, -25, -35, -35,
+ -35, -35, -25, -25, 750, 760, 770, -35, -35, -35,
+ -25, -35, -25, -25, -25, -25, -35, -35, -35, -25,
+ -25, -35, -35, -35, -35, -25, -25, -35, -35, -25,
+ -25, -35, -35, -35, -35, -35, -25, -25, -35, -35,
+ 790, -100, 800, 850, 900, 920, 940, 1030, 1040, 1050,
+ -36, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -35, -25, -25, -35, 810, -25, -35, -35, -25, 820,
+ -25, -35, -25, -25, -35, -35, -35, -35, -35, -25,
+ -25, -35, 830, -35, 840, -35, -25, -35, -35, -25,
+ -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -100, -25, -25, -25, -100, -100, -100, -100, -100, -100,
+ -25, -25, -35, -35, -35, -35, 860, -35, 870, 880,
+ -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, -35, -35, -35, -35, -35, -35, -45, -45, -35,
+ -100, -100, -100, -100, -100, -100, 890, -100, -100, -100,
+ -25, -45, -45, -25, -45, -45, -25, -45, -45, -45,
+ -25, -25, -25, -25, -25, -35, -35, 910, -35, -25,
+ -35, -35, -35, -35, -35, -35, -35, -45, -35, -35,
+ -100, 930, -35, -35, -35, -35, -35, -35, -35, -35,
+ -100, -100, -45, -100, -45, -100, -100, -100, -100, -100,
+ -25, -25, -25, 950, -25, 970, 990, -35, 1000, 1010,
+ -35, -35, -35, -35, -35, -35, 960, -35, -35, -35,
+ -45, -45, -45, -45, -45, -45, -35, -45, -45, -45,
+ -35, -35, -25, -35, -35, 980, -35, -35, -35, -35,
+ -100, -100, -25, -25, -100, -100, -100, -100, -100, -100,
+ -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -25, -35, -35, -35, -35, -35, -35, -35, -35, -25,
+ -25, -35, -35, -35, -25, -25, -35, -35, -35, 1020,
+ -45, -45, -35, -35, -45, -45, -45, -45, -45, -45,
+ -25, -25, -25, -25, -25, -35, -25, -35, -25, -35,
+ -35, -25, -25, -35, -35, -35, -25, -35, -25, -35,
+ -25, -25, -35, -35, -35, -35, -35, -35, -35, -25,
+ -26, -100, 1070, 1080, 1090, 1110, 1120, 1130, 1140, 1160,
+ -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+ -35, -100, -35, -35, -35, -100, -35, -35, -35, 1100,
+ -35, -35, -35, -35, -35, -35, -45, -35, -35, -35,
+ -35, -25, -35, -25, -35, -35, -35, -35, -25, -35,
+ -25, -25, -25, -25, -35, -35, -35, -35, -35, -35,
+ -25, -25, -35, -35, -35, -25, -25, -35, -35, -35,
+ 1150, -25, -35, -35, -35, -35, -35, -35, -25, -25,
+ -35, -35, -45, -35, -35, -35, -35, -35, -35, -35,
+ -35, 1170, -25, -35, 1180, -35, 1190, -35, -25, -25,
+ -100, -100, -45, -45, -100, -100, -100, -100, -100, -100,
+ -25, -35, -35, -35, -35, -35, -35, -25, -25, -35,
+ -35, -35, -35, -35, -35, -35, -35, -35, -35, -45,
+ -26, -15, -15, -15, -15, -15, -15, -15, -15, -15};
+
+ public static void format(Editable text) {
+ // Here, "root" means the position of "'":
+ // 0'3, 0'90, and +81'-90
+ // (dash will be deleted soon, so it is actually +81'90).
+ int rootIndex = 1;
+ int length = text.length();
+ if (length > 3
+ && text.subSequence(0, 3).toString().equals("+81")) {
+ rootIndex = 3;
+ } else if (length < 1 || text.charAt(0) != '0') {
+ return;
+ }
+
+ CharSequence saved = text.subSequence(0, length);
+
+ // Strip the dashes first, as we're going to add them back
+ int i = 0;
+ while (i < text.length()) {
+ if (text.charAt(i) == '-') {
+ text.delete(i, i + 1);
+ } else {
+ i++;
+ }
+ }
+
+ length = text.length();
+ int dashposition;
+
+ i = rootIndex;
+ int base = 0;
+ while (i < length) {
+ char ch = text.charAt(i);
+ if (!Character.isDigit(ch)) {
+ text.replace(0, length, saved);
+ return;
+ }
+ short value = FORMAT_MAP[base + ch - '0'];
+ if (value < 0) {
+ if (value <= -100) {
+ text.replace(0, length, saved);
+ return;
+ }
+ int dashPos2 = rootIndex + (Math.abs(value) % 10);
+ if (length > dashPos2) {
+ text.insert(dashPos2, "-");
+ }
+ int dashPos1 = rootIndex + (Math.abs(value) / 10);
+ if (length > dashPos1) {
+ text.insert(dashPos1, "-");
+ }
+ break;
+ } else {
+ base = value;
+ i++;
+ }
+ }
+
+ if (length > 3 && rootIndex == 3) {
+ text.insert(rootIndex, "-");
+ }
+ }
+} \ No newline at end of file
diff --git a/android/telephony/MbmsDownloadManager.java b/android/telephony/MbmsDownloadManager.java
new file mode 100644
index 00000000..1e8cf185
--- /dev/null
+++ b/android/telephony/MbmsDownloadManager.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.MbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsTempFileProvider;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsDownloadService;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+/** @hide */
+public class MbmsDownloadManager {
+ private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName();
+
+ /** @hide */
+ // TODO: systemapi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
+ "android.telephony.action.EmbmsDownload";
+
+ /**
+ * Integer extra indicating the result code of the download. One of
+ * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
+ */
+ public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
+
+ /**
+ * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
+ * is for. Must not be null.
+ */
+ public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
+
+ /**
+ * Extra containing a single {@link Uri} indicating the location of the successfully
+ * downloaded file. Set on the intent provided via
+ * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
+ * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
+ * {@link #RESULT_SUCCESSFUL}.
+ */
+ public static final String EXTRA_COMPLETED_FILE_URI =
+ "android.telephony.mbms.extra.COMPLETED_FILE_URI";
+
+ public static final int RESULT_SUCCESSFUL = 1;
+ public static final int RESULT_CANCELLED = 2;
+ public static final int RESULT_EXPIRED = 3;
+ public static final int RESULT_IO_ERROR = 4;
+ // TODO - more results!
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
+ STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
+ public @interface DownloadStatus {}
+
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
+ public static final int STATUS_PENDING_DOWNLOAD = 2;
+ public static final int STATUS_PENDING_REPAIR = 3;
+ public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+ private final Context mContext;
+ private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+ }
+ };
+
+ private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
+ private final MbmsDownloadManagerCallback mCallback;
+
+ private MbmsDownloadManager(Context context, MbmsDownloadManagerCallback callback, int subId) {
+ mContext = context;
+ mCallback = callback;
+ mSubscriptionId = subId;
+ }
+
+ /**
+ * Create a new MbmsDownloadManager using the system default data subscription ID.
+ * See {@link #create(Context, MbmsDownloadManagerCallback, int)}
+ *
+ * @hide
+ */
+ public static MbmsDownloadManager create(Context context,
+ MbmsDownloadManagerCallback listener)
+ throws MbmsException {
+ return create(context, listener, SubscriptionManager.getDefaultSubscriptionId());
+ }
+
+ /**
+ * Create a new MbmsDownloadManager using the given subscription ID.
+ *
+ * Note that this call will bind a remote service and that may take a bit. The instance of
+ * {@link MbmsDownloadManager} that is returned will not be ready for use until
+ * {@link MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
+ * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
+ *
+ * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+ *
+ * You may only have one instance of {@link MbmsDownloadManager} per UID. If you call this
+ * method while there is an active instance of {@link MbmsDownloadManager} in your process
+ * (in other words, one that has not had {@link #dispose()} called on it), this method will
+ * throw an {@link MbmsException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsDownloadManagerCallback#error(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call dispose() on the instance of
+ * {@link MbmsDownloadManager} that you received before calling this method again.
+ *
+ * @param context The instance of {@link Context} to use
+ * @param listener A callback to get asynchronous error messages and file service updates.
+ * @param subscriptionId The data subscription ID to use
+ * @hide
+ */
+ public static MbmsDownloadManager create(Context context,
+ MbmsDownloadManagerCallback listener, int subscriptionId)
+ throws MbmsException {
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+ }
+ MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
+ try {
+ mdm.bindAndInitialize();
+ } catch (MbmsException e) {
+ sIsInitialized.set(false);
+ throw e;
+ }
+ return mdm;
+ }
+
+ private void bindAndInitialize() throws MbmsException {
+ MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsDownloadService downloadService =
+ IMbmsDownloadService.Stub.asInterface(service);
+ int result;
+ try {
+ result = downloadService.initialize(mSubscriptionId, mCallback);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ sendErrorToApp(
+ MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result != MbmsException.SUCCESS) {
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(downloadService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
+ });
+ }
+
+ /**
+ * An inspection API to retrieve the list of available
+ * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
+ * The results are returned asynchronously via a call to
+ * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)}
+ *
+ * The serviceClasses argument lets the app filter on types of programming and is opaque data
+ * negotiated beforehand between the app and the carrier.
+ *
+ * This may throw an {@link MbmsException} containing one of the following errors:
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+ * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+ *
+ * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
+ * callback can include any of the errors except:
+ * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
+ *
+ * @param classList A list of service classes which the app wishes to receive
+ * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
+ * about. Subsequent calls to this method will replace this list of service
+ * classes (i.e. the middleware will no longer send updates for services
+ * matching classes only in the old list).
+ */
+ public void getFileServices(List<String> classList) throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+ try {
+ int returnCode = downloadService.getFileServices(mSubscriptionId, classList);
+ if (returnCode != MbmsException.SUCCESS) {
+ throw new MbmsException(returnCode);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Sets the temp file root for downloads.
+ * All temp files created for the middleware to write to will be contained in the specified
+ * directory. Applications that wish to specify a location only need to call this method once
+ * as long their data is persisted in storage -- the argument will be stored both in a
+ * local instance of {@link android.content.SharedPreferences} and by the middleware.
+ *
+ * If this method is not called at least once before calling
+ * {@link #download(DownloadRequest, DownloadProgressListener)}, the framework
+ * will default to a directory formed by the concatenation of the app's files directory and
+ * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
+ *
+ * Before calling this method, the app must cancel all of its pending
+ * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
+ * an {@link MbmsException} will be thrown with code
+ * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
+ * provided directory is the same as what has been previously configured.
+ *
+ * The {@link File} supplied as a root temp file directory must already exist. If not, an
+ * {@link IllegalArgumentException} will be thrown.
+ * @param tempFileRootDirectory A directory to place temp files in.
+ */
+ public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
+ throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+ if (!tempFileRootDirectory.exists()) {
+ throw new IllegalArgumentException("Provided directory does not exist");
+ }
+ if (!tempFileRootDirectory.isDirectory()) {
+ throw new IllegalArgumentException("Provided File is not a directory");
+ }
+ String filePath;
+ try {
+ filePath = tempFileRootDirectory.getCanonicalPath();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
+ }
+
+ try {
+ int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+ if (result != MbmsException.SUCCESS) {
+ throw new MbmsException(result);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
+ }
+
+ /**
+ * Retrieves the currently configured temp file root directory. Returns the file that was
+ * configured via {@link #setTempFileRootDirectory(File)} or the default directory
+ * {@link #download(DownloadRequest, DownloadProgressListener)} was called without ever setting
+ * the temp file root. If neither method has been called since the last time the app's shared
+ * preferences were reset, returns null.
+ *
+ * @return A {@link File} pointing to the configured temp file directory, or null if not yet
+ * configured.
+ */
+ public @Nullable File getTempFileRootDirectory() {
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
+ if (path != null) {
+ return new File(path);
+ }
+ return null;
+ }
+
+ /**
+ * Requests a download of a file that is available via multicast.
+ *
+ * downloadListener is an optional callback object which can be used to get progress reports
+ * of a currently occuring download. Note this can only run while the calling app
+ * is running, so future downloads will simply result in resultIntents being sent
+ * for completed or errored-out downloads. A NULL indicates no callbacks are needed.
+ *
+ * May throw an {@link IllegalArgumentException}
+ *
+ * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
+ * this method will create a directory at the default location defined at
+ * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+ * file root directory.
+ *
+ * Asynchronous errors through the listener include any of the errors
+ *
+ * @param request The request that specifies what should be downloaded
+ * @param progressListener Optional listener that will be provided progress updates
+ * if the app is running.
+ */
+ public void download(DownloadRequest request, DownloadProgressListener progressListener)
+ throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ // Check to see whether the app's set a temp root dir yet, and set it if not.
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
+ File tempRootDirectory = new File(mContext.getFilesDir(),
+ MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
+ tempRootDirectory.mkdirs();
+ setTempFileRootDirectory(tempRootDirectory);
+ }
+
+ checkValidDownloadDestination(request);
+ writeDownloadRequestToken(request);
+ try {
+ downloadService.download(request, progressListener);
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Returns a list of pending {@link DownloadRequest}s that originated from this application.
+ * A pending request is one that was issued via
+ * {@link #download(DownloadRequest, DownloadProgressListener)} but not cancelled through
+ * {@link #cancelDownload(DownloadRequest)}.
+ * @return A list, possibly empty, of {@link DownloadRequest}s
+ */
+ public @NonNull List<DownloadRequest> listPendingDownloads() throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ try {
+ return downloadService.listPendingDownloads(mSubscriptionId);
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Attempts to cancel the specified {@link DownloadRequest}.
+ *
+ * If the middleware is not aware of the specified download request, an MbmsException will be
+ * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+ *
+ * If this method returns without throwing an exception, you may assume that cancellation
+ * was successful.
+ * @param downloadRequest The download request that you wish to cancel.
+ */
+ public void cancelDownload(DownloadRequest downloadRequest) throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ try {
+ int result = downloadService.cancelDownload(downloadRequest);
+ if (result != MbmsException.SUCCESS) {
+ throw new MbmsException(result);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ deleteDownloadRequestToken(downloadRequest);
+ }
+
+ /**
+ * Gets information about the status of a file pending download.
+ *
+ * If the middleware has not yet been properly initialized or if it has no records of the
+ * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+ * {@link #STATUS_UNKNOWN} will be returned.
+ *
+ * @param downloadRequest The download request to query.
+ * @param fileInfo The particular file within the request to get information on.
+ * @return The status of the download.
+ */
+ @DownloadStatus
+ public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+ throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ try {
+ return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Resets the middleware's knowledge of previously-downloaded files in this download request.
+ *
+ * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
+ * files whose server-reported hash matches one of the already-downloaded files. This means
+ * that if the file is accidentally deleted by the user or by the app, the middleware will
+ * not try to download it again.
+ * This method will reset the middleware's cache of hashes for the provided
+ * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
+ * when available.
+ * This will not interrupt in-progress downloads.
+ *
+ * If the middleware is not aware of the specified download request, an MbmsException will be
+ * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+ *
+ * May throw a {@link MbmsException} with error code
+ * @param downloadRequest The request to re-download files for.
+ */
+ public void resetDownloadKnowledge(DownloadRequest downloadRequest) throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ try {
+ int result = downloadService.resetDownloadKnowledge(downloadRequest);
+ if (result != MbmsException.SUCCESS) {
+ throw new MbmsException(result);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ public void dispose() {
+ try {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ Log.i(LOG_TAG, "Service already dead");
+ return;
+ }
+ downloadService.dispose(mSubscriptionId);
+ } catch (RemoteException e) {
+ // Ignore
+ Log.i(LOG_TAG, "Remote exception while disposing of service");
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
+ }
+ }
+
+ private void writeDownloadRequestToken(DownloadRequest request) {
+ File token = getDownloadRequestTokenPath(request);
+ if (!token.getParentFile().exists()) {
+ token.getParentFile().mkdirs();
+ }
+ if (token.exists()) {
+ Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
+ return;
+ }
+ try {
+ if (!token.createNewFile()) {
+ throw new RuntimeException("Failed to create download token for request "
+ + request);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to create download token for request " + request
+ + " due to IOException " + e);
+ }
+ }
+
+ private void deleteDownloadRequestToken(DownloadRequest request) {
+ File token = getDownloadRequestTokenPath(request);
+ if (!token.isFile()) {
+ Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
+ return;
+ }
+ if (!token.delete()) {
+ Log.w(LOG_TAG, "Couldn't delete download token at " + token);
+ }
+ }
+
+ private File getDownloadRequestTokenPath(DownloadRequest request) {
+ File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
+ request.getFileServiceId());
+ String downloadTokenFileName = request.getHash()
+ + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
+ return new File(tempFileLocation, downloadTokenFileName);
+ }
+
+ /**
+ * Verifies the following:
+ * If a request is multi-part,
+ * 1. Destination Uri must exist and be a directory
+ * 2. Directory specified must contain no files.
+ * Otherwise
+ * 1. The file specified by the destination Uri must not exist.
+ */
+ private void checkValidDownloadDestination(DownloadRequest request) {
+ File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
+ if (request.isMultipartDownload()) {
+ if (!toFile.isDirectory()) {
+ throw new IllegalArgumentException("Multipart download must specify valid " +
+ "destination directory.");
+ }
+ if (toFile.listFiles().length > 0) {
+ throw new IllegalArgumentException("Destination directory must be clear of all " +
+ "files.");
+ }
+ } else {
+ if (toFile.exists()) {
+ throw new IllegalArgumentException("Destination file must not exist.");
+ }
+ }
+ }
+
+ private void sendErrorToApp(int errorCode, String message) {
+ try {
+ mCallback.error(errorCode, message);
+ } catch (RemoteException e) {
+ // Ignore, should not happen locally.
+ }
+ }
+}
diff --git a/android/telephony/MbmsStreamingManager.java b/android/telephony/MbmsStreamingManager.java
new file mode 100644
index 00000000..b6b253ec
--- /dev/null
+++ b/android/telephony/MbmsStreamingManager.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.mbms.InternalStreamingManagerCallback;
+import android.telephony.mbms.InternalStreamingServiceCallback;
+import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+/**
+ * This class provides functionality for streaming media over MBMS.
+ */
+public class MbmsStreamingManager {
+ private static final String LOG_TAG = "MbmsStreamingManager";
+
+ /**
+ * Service action which must be handled by the middleware implementing the MBMS streaming
+ * interface.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String MBMS_STREAMING_SERVICE_ACTION =
+ "android.telephony.action.EmbmsStreaming";
+
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+ private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sIsInitialized.set(false);
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+ }
+ };
+
+ private InternalStreamingManagerCallback mInternalCallback;
+
+ private final Context mContext;
+ private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+
+ /** @hide */
+ private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback,
+ int subscriptionId, Handler handler) {
+ mContext = context;
+ mSubscriptionId = subscriptionId;
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+ mInternalCallback = new InternalStreamingManagerCallback(callback, handler);
+ }
+
+ /**
+ * Create a new MbmsStreamingManager using the given subscription ID.
+ *
+ * Note that this call will bind a remote service. You may not call this method on your app's
+ * main thread. This may throw an {@link MbmsException}, indicating errors that may happen
+ * during the initialization or binding process.
+ *
+ *
+ * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this
+ * method while there is an active instance of {@link MbmsStreamingManager} in your process
+ * (in other words, one that has not had {@link #dispose()} called on it), this method will
+ * throw an {@link MbmsException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsStreamingManagerCallback#onError(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call dispose() on the instance of
+ * {@link MbmsStreamingManager} that you received before calling this method again.
+ *
+ * @param context The {@link Context} to use.
+ * @param callback A callback object on which you wish to receive results of asynchronous
+ * operations.
+ * @param subscriptionId The subscription ID to use.
+ * @param handler The handler you wish to receive callbacks on. If null, callbacks will be
+ * processed on the main looper (in other words, the looper returned from
+ * {@link Looper#getMainLooper()}).
+ */
+ public static MbmsStreamingManager create(Context context,
+ MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler)
+ throws MbmsException {
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+ }
+ MbmsStreamingManager manager = new MbmsStreamingManager(context, callback,
+ subscriptionId, handler);
+ try {
+ manager.bindAndInitialize();
+ } catch (MbmsException e) {
+ sIsInitialized.set(false);
+ throw e;
+ }
+ return manager;
+ }
+
+ /**
+ * Create a new MbmsStreamingManager using the system default data subscription ID.
+ * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+ */
+ public static MbmsStreamingManager create(Context context,
+ MbmsStreamingManagerCallback callback, Handler handler)
+ throws MbmsException {
+ return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+ }
+
+ /**
+ * Create a new MbmsStreamingManager using the system default data subscription ID and
+ * default {@link Handler}.
+ * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+ */
+ public static MbmsStreamingManager create(Context context,
+ MbmsStreamingManagerCallback callback)
+ throws MbmsException {
+ return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
+ }
+
+ /**
+ * Terminates this instance, ending calls to the registered listener. Also terminates
+ * any streaming services spawned from this instance.
+ *
+ * May throw an {@link IllegalStateException}
+ */
+ public void dispose() {
+ try {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
+ // Ignore and return, assume already disposed.
+ return;
+ }
+ streamingService.dispose(mSubscriptionId);
+ } catch (RemoteException e) {
+ // Ignore for now
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
+ }
+ }
+
+ /**
+ * An inspection API to retrieve the list of streaming media currently be advertised.
+ * The results are returned asynchronously through the previously registered callback.
+ * serviceClasses lets the app filter on types of programming and is opaque data between
+ * the app and the carrier.
+ *
+ * Multiple calls replace the list of serviceClasses of interest.
+ *
+ * This may throw an {@link MbmsException} containing any error in
+ * {@link android.telephony.mbms.MbmsException.GeneralErrors},
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
+ * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
+ *
+ * May also throw an unchecked {@link IllegalArgumentException} or an
+ * {@link IllegalStateException}
+ *
+ * @param classList A list of streaming service classes that the app would like updates on.
+ */
+ public void getStreamingServices(List<String> classList) throws MbmsException {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+ try {
+ int returnCode = streamingService.getStreamingServices(mSubscriptionId, classList);
+ if (returnCode != MbmsException.SUCCESS) {
+ throw new MbmsException(returnCode);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ sIsInitialized.set(false);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Starts streaming a requested service, reporting status to the indicated callback.
+ * Returns an object used to control that stream. The stream may not be ready for consumption
+ * immediately upon return from this method -- wait until the streaming state has been
+ * reported via
+ * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
+ *
+ * May throw an
+ * {@link MbmsException} containing any of the error codes in
+ * {@link android.telephony.mbms.MbmsException.GeneralErrors},
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
+ * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
+ *
+ * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * Asynchronous errors through the callback include any of the errors in
+ * {@link android.telephony.mbms.MbmsException.GeneralErrors} or
+ * {@link android.telephony.mbms.MbmsException.StreamingErrors}.
+ *
+ * @param serviceInfo The information about the service to stream.
+ * @param callback A callback that'll be called when something about the stream changes.
+ * @param handler A handler that calls to {@code callback} should be called on. If null,
+ * defaults to the handler provided via
+ * {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+ * @return An instance of {@link StreamingService} through which the stream can be controlled.
+ */
+ public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
+ StreamingServiceCallback callback, Handler handler) throws MbmsException {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
+ callback, handler == null ? mInternalCallback.getHandler() : handler);
+
+ StreamingService serviceForApp = new StreamingService(
+ mSubscriptionId, streamingService, serviceInfo, serviceCallback);
+
+ try {
+ int returnCode = streamingService.startStreaming(
+ mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
+ if (returnCode != MbmsException.SUCCESS) {
+ throw new MbmsException(returnCode);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ sIsInitialized.set(false);
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+
+ return serviceForApp;
+ }
+
+ private void bindAndInitialize() throws MbmsException {
+ MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsStreamingService streamingService =
+ IMbmsStreamingService.Stub.asInterface(service);
+ int result;
+ try {
+ result = streamingService.initialize(mInternalCallback,
+ mSubscriptionId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ sendErrorToApp(
+ MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ sendErrorToApp(
+ MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result != MbmsException.SUCCESS) {
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(streamingService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
+ });
+ }
+
+ private void sendErrorToApp(int errorCode, String message) {
+ try {
+ mInternalCallback.error(errorCode, message);
+ } catch (RemoteException e) {
+ // Ignore, should not happen locally.
+ }
+ }
+}
diff --git a/android/telephony/ModemActivityInfo.java b/android/telephony/ModemActivityInfo.java
new file mode 100644
index 00000000..03ce2d8e
--- /dev/null
+++ b/android/telephony/ModemActivityInfo.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Reports modem activity information
+ * @hide
+ */
+public class ModemActivityInfo implements Parcelable {
+ /**
+ * Tx power index
+ * index 0 = tx_power < 0dBm
+ * index 1 = 0dBm < tx_power < 5dBm
+ * index 2 = 5dBm < tx_power < 15dBm
+ * index 3 = 15dBm < tx_power < 20dBm
+ * index 4 = tx_power > 20dBm
+ */
+ public static final int TX_POWER_LEVELS = 5;
+
+ private final long mTimestamp;
+ private final int mSleepTimeMs;
+ private final int mIdleTimeMs;
+ private final int [] mTxTimeMs = new int[TX_POWER_LEVELS];
+ private final int mRxTimeMs;
+ private final int mEnergyUsed;
+
+ public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+ int[] txTimeMs, int rxTimeMs, int energyUsed) {
+ mTimestamp = timestamp;
+ mSleepTimeMs = sleepTimeMs;
+ mIdleTimeMs = idleTimeMs;
+ if (txTimeMs != null) {
+ System.arraycopy(txTimeMs, 0, mTxTimeMs, 0, Math.min(txTimeMs.length, TX_POWER_LEVELS));
+ }
+ mRxTimeMs = rxTimeMs;
+ mEnergyUsed = energyUsed;
+ }
+
+ @Override
+ public String toString() {
+ return "ModemActivityInfo{"
+ + " mTimestamp=" + mTimestamp
+ + " mSleepTimeMs=" + mSleepTimeMs
+ + " mIdleTimeMs=" + mIdleTimeMs
+ + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
+ + " mRxTimeMs=" + mRxTimeMs
+ + " mEnergyUsed=" + mEnergyUsed
+ + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ModemActivityInfo> CREATOR =
+ new Parcelable.Creator<ModemActivityInfo>() {
+ public ModemActivityInfo createFromParcel(Parcel in) {
+ long timestamp = in.readLong();
+ int sleepTimeMs = in.readInt();
+ int idleTimeMs = in.readInt();
+ int[] txTimeMs = new int[TX_POWER_LEVELS];
+ for (int i = 0; i < TX_POWER_LEVELS; i++) {
+ txTimeMs[i] = in.readInt();
+ }
+ int rxTimeMs = in.readInt();
+ int energyUsed = in.readInt();
+ return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
+ txTimeMs, rxTimeMs, energyUsed);
+ }
+
+ public ModemActivityInfo[] newArray(int size) {
+ return new ModemActivityInfo[size];
+ }
+ };
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ dest.writeInt(mSleepTimeMs);
+ dest.writeInt(mIdleTimeMs);
+ for (int i = 0; i < TX_POWER_LEVELS; i++) {
+ dest.writeInt(mTxTimeMs[i]);
+ }
+ dest.writeInt(mRxTimeMs);
+ dest.writeInt(mEnergyUsed);
+ }
+
+ /**
+ * @return timestamp of record creation
+ */
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * @return tx time in ms. It's an array of tx times
+ * with each index...
+ */
+ public int [] getTxTimeMillis() {
+ return mTxTimeMs;
+ }
+
+ /**
+ * @return sleep time in ms.
+ */
+ public int getSleepTimeMillis() {
+ return mSleepTimeMs;
+ }
+
+ /**
+ * @return idle time in ms.
+ */
+ public int getIdleTimeMillis() {
+ return mIdleTimeMs;
+ }
+
+ /**
+ * @return rx time in ms.
+ */
+ public int getRxTimeMillis() {
+ return mRxTimeMs;
+ }
+
+ /**
+ * product of current(mA), voltage(V) and time(ms)
+ * @return energy used
+ */
+ public int getEnergyUsed () {
+ return mEnergyUsed;
+ }
+
+ /**
+ * @return if the record is valid
+ */
+ public boolean isValid() {
+ for (int txVal : getTxTimeMillis()) {
+ if(txVal < 0) {
+ return false;
+ }
+ }
+
+ return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
+ && (getRxTimeMillis() >= 0) && (getEnergyUsed() >= 0) && !isEmpty());
+ }
+
+ private boolean isEmpty() {
+ for (int txVal : getTxTimeMillis()) {
+ if(txVal != 0) {
+ return false;
+ }
+ }
+
+ return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
+ && (getRxTimeMillis() == 0) && (getEnergyUsed() == 0));
+ }
+}
diff --git a/android/telephony/NeighboringCellInfo.java b/android/telephony/NeighboringCellInfo.java
new file mode 100644
index 00000000..25851e32
--- /dev/null
+++ b/android/telephony/NeighboringCellInfo.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_GPRS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSDPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSUPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSPA;
+
+
+
+/**
+ * Represents the neighboring cell information, including
+ * Received Signal Strength and Cell ID location.
+ */
+public class NeighboringCellInfo implements Parcelable
+{
+ /**
+ * Signal strength is not available
+ */
+ static final public int UNKNOWN_RSSI = 99;
+ /**
+ * Cell location is not available
+ */
+ static final public int UNKNOWN_CID = -1;
+
+ /**
+ * In GSM, mRssi is the Received RSSI;
+ * In UMTS, mRssi is the Level index of CPICH Received Signal Code Power
+ */
+ private int mRssi;
+ /**
+ * CID in 16 bits format in GSM. Return UNKNOWN_CID in UMTS and CMDA.
+ */
+ private int mCid;
+ /**
+ * LAC in 16 bits format in GSM. Return UNKNOWN_CID in UMTS and CMDA.
+ */
+ private int mLac;
+ /**
+ * Primary Scrambling Code in 9 bits format in UMTS
+ * Return UNKNOWN_CID in GSM and CMDA.
+ */
+ private int mPsc;
+ /**
+ * Radio network type, value is one of following
+ * TelephonyManager.NETWORK_TYPE_XXXXXX.
+ */
+ private int mNetworkType;
+
+ /**
+ * Empty constructor. Initializes the RSSI and CID.
+ *
+ * NeighboringCellInfo is one time shot for the neighboring cells based on
+ * the radio network type at that moment. Its constructor needs radio network
+ * type.
+ *
+ * @deprecated by {@link #NeighboringCellInfo(int, String, int)}
+ */
+ @Deprecated
+ public NeighboringCellInfo() {
+ mRssi = UNKNOWN_RSSI;
+ mLac = UNKNOWN_CID;
+ mCid = UNKNOWN_CID;
+ mPsc = UNKNOWN_CID;
+ mNetworkType = NETWORK_TYPE_UNKNOWN;
+ }
+
+ /**
+ * Initialize the object from rssi and cid.
+ *
+ * NeighboringCellInfo is one time shot for the neighboring cells based on
+ * the radio network type at that moment. Its constructor needs radio network
+ * type.
+ *
+ * @deprecated by {@link #NeighboringCellInfo(int, String, int)}
+ */
+ @Deprecated
+ public NeighboringCellInfo(int rssi, int cid) {
+ mRssi = rssi;
+ mCid = cid;
+ }
+
+ /**
+ * Initialize the object from rssi, location string, and radioType
+ * radioType is one of following
+ * {@link TelephonyManager#NETWORK_TYPE_GPRS TelephonyManager.NETWORK_TYPE_GPRS},
+ * {@link TelephonyManager#NETWORK_TYPE_EDGE TelephonyManager.NETWORK_TYPE_EDGE},
+ * {@link TelephonyManager#NETWORK_TYPE_UMTS TelephonyManager.NETWORK_TYPE_UMTS},
+ * {@link TelephonyManager#NETWORK_TYPE_HSDPA TelephonyManager.NETWORK_TYPE_HSDPA},
+ * {@link TelephonyManager#NETWORK_TYPE_HSUPA TelephonyManager.NETWORK_TYPE_HSUPA},
+ * and {@link TelephonyManager#NETWORK_TYPE_HSPA TelephonyManager.NETWORK_TYPE_HSPA}.
+ */
+ public NeighboringCellInfo(int rssi, String location, int radioType) {
+ // set default value
+ mRssi = rssi;
+ mNetworkType = NETWORK_TYPE_UNKNOWN;
+ mPsc = UNKNOWN_CID;
+ mLac = UNKNOWN_CID;
+ mCid = UNKNOWN_CID;
+
+
+ // pad location string with leading "0"
+ int l = location.length();
+ if (l > 8) return;
+ if (l < 8) {
+ for (int i = 0; i < (8-l); i++) {
+ location = "0" + location;
+ }
+ }
+ // TODO - handle LTE and eHRPD (or find they can't be supported)
+ try {// set LAC/CID or PSC based on radioType
+ switch (radioType) {
+ case NETWORK_TYPE_GPRS:
+ case NETWORK_TYPE_EDGE:
+ mNetworkType = radioType;
+ // check if 0xFFFFFFFF for UNKNOWN_CID
+ if (!location.equalsIgnoreCase("FFFFFFFF")) {
+ mCid = Integer.parseInt(location.substring(4), 16);
+ mLac = Integer.parseInt(location.substring(0, 4), 16);
+ }
+ break;
+ case NETWORK_TYPE_UMTS:
+ case NETWORK_TYPE_HSDPA:
+ case NETWORK_TYPE_HSUPA:
+ case NETWORK_TYPE_HSPA:
+ mNetworkType = radioType;
+ mPsc = Integer.parseInt(location, 16);
+ break;
+ }
+ } catch (NumberFormatException e) {
+ // parsing location error
+ mPsc = UNKNOWN_CID;
+ mLac = UNKNOWN_CID;
+ mCid = UNKNOWN_CID;
+ mNetworkType = NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Initialize the object from a parcel.
+ */
+ public NeighboringCellInfo(Parcel in) {
+ mRssi = in.readInt();
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mPsc = in.readInt();
+ mNetworkType = in.readInt();
+ }
+
+ /**
+ * @return received signal strength or UNKNOWN_RSSI if unknown
+ *
+ * For GSM, it is in "asu" ranging from 0 to 31 (dBm = -113 + 2*asu)
+ * 0 means "-113 dBm or less" and 31 means "-51 dBm or greater"
+ * For UMTS, it is the Level index of CPICH RSCP defined in TS 25.125
+ */
+ public int getRssi() {
+ return mRssi;
+ }
+
+ /**
+ * @return LAC in GSM, 0xffff max legal value
+ * UNKNOWN_CID if in UMTS or CMDA or unknown
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * @return cell id in GSM, 0xffff max legal value
+ * UNKNOWN_CID if in UMTS or CDMA or unknown
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ /**
+ * @return Primary Scrambling Code in 9 bits format in UMTS, 0x1ff max value
+ * UNKNOWN_CID if in GSM or CMDA or unknown
+ */
+ public int getPsc() {
+ return mPsc;
+ }
+
+ /**
+ * @return Radio network type while neighboring cell location is stored.
+ *
+ * Return {@link TelephonyManager#NETWORK_TYPE_UNKNOWN TelephonyManager.NETWORK_TYPE_UNKNOWN}
+ * means that the location information is unavailable.
+ *
+ * Return {@link TelephonyManager#NETWORK_TYPE_GPRS TelephonyManager.NETWORK_TYPE_GPRS} or
+ * {@link TelephonyManager#NETWORK_TYPE_EDGE TelephonyManager.NETWORK_TYPE_EDGE}
+ * means that Neighboring Cell information is stored for GSM network, in
+ * which {@link NeighboringCellInfo#getLac NeighboringCellInfo.getLac} and
+ * {@link NeighboringCellInfo#getCid NeighboringCellInfo.getCid} should be
+ * called to access location.
+ *
+ * Return {@link TelephonyManager#NETWORK_TYPE_UMTS TelephonyManager.NETWORK_TYPE_UMTS},
+ * {@link TelephonyManager#NETWORK_TYPE_HSDPA TelephonyManager.NETWORK_TYPE_HSDPA},
+ * {@link TelephonyManager#NETWORK_TYPE_HSUPA TelephonyManager.NETWORK_TYPE_HSUPA},
+ * or {@link TelephonyManager#NETWORK_TYPE_HSPA TelephonyManager.NETWORK_TYPE_HSPA}
+ * means that Neighboring Cell information is stored for UMTS network, in
+ * which {@link NeighboringCellInfo#getPsc NeighboringCellInfo.getPsc}
+ * should be called to access location.
+ */
+ public int getNetworkType() {
+ return mNetworkType;
+ }
+ /**
+ * Set the cell id.
+ *
+ * NeighboringCellInfo is a one time shot for the neighboring cells based on
+ * the radio network type at that moment. It shouldn't be changed after
+ * creation.
+ *
+ * @deprecated cid value passed as in location parameter passed to constructor
+ * {@link #NeighboringCellInfo(int, String, int)}
+ */
+ @Deprecated
+ public void setCid(int cid) {
+ mCid = cid;
+ }
+
+ /**
+ * Set the signal strength of the cell.
+ *
+ * NeighboringCellInfo is a one time shot for the neighboring cells based on
+ * the radio network type at that moment. It shouldn't be changed after
+ * creation.
+ *
+ * @deprecated initial rssi value passed as parameter to constructor
+ * {@link #NeighboringCellInfo(int, String, int)}
+ */
+ @Deprecated
+ public void setRssi(int rssi) {
+ mRssi = rssi;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("[");
+ if (mPsc != UNKNOWN_CID) {
+ sb.append(Integer.toHexString(mPsc))
+ .append("@").append(((mRssi == UNKNOWN_RSSI)? "-" : mRssi));
+ } else if(mLac != UNKNOWN_CID && mCid != UNKNOWN_CID) {
+ sb.append(Integer.toHexString(mLac))
+ .append(Integer.toHexString(mCid))
+ .append("@").append(((mRssi == UNKNOWN_RSSI)? "-" : mRssi));
+ }
+ sb.append("]");
+
+ return sb.toString();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mRssi);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ dest.writeInt(mPsc);
+ dest.writeInt(mNetworkType);
+ }
+
+ public static final Parcelable.Creator<NeighboringCellInfo> CREATOR
+ = new Parcelable.Creator<NeighboringCellInfo>() {
+ public NeighboringCellInfo createFromParcel(Parcel in) {
+ return new NeighboringCellInfo(in);
+ }
+
+ public NeighboringCellInfo[] newArray(int size) {
+ return new NeighboringCellInfo[size];
+ }
+ };
+}
diff --git a/android/telephony/NetworkScan.java b/android/telephony/NetworkScan.java
new file mode 100644
index 00000000..f15fde8f
--- /dev/null
+++ b/android/telephony/NetworkScan.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * Allows applications to request the system to perform a network scan.
+ *
+ * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} will
+ * receive a NetworkScan which contains the callback method to stop the scan requested.
+ * @hide
+ */
+public class NetworkScan {
+
+ public static final String TAG = "NetworkScan";
+
+ // Below errors are mapped from RadioError which is returned from RIL. We will consolidate
+ // RadioErrors during the mapping if those RadioErrors mean no difference to the users.
+ public static final int SUCCESS = 0; // RadioError:NONE
+ public static final int ERROR_MODEM_ERROR = 1; // RadioError:RADIO_NOT_AVAILABLE
+ // RadioError:NO_MEMORY
+ // RadioError:INTERNAL_ERR
+ // RadioError:MODEM_ERR
+ // RadioError:OPERATION_NOT_ALLOWED
+ public static final int ERROR_INVALID_SCAN = 2; // RadioError:INVALID_ARGUMENTS
+ public static final int ERROR_MODEM_BUSY = 3; // RadioError:DEVICE_IN_USE
+ public static final int ERROR_UNSUPPORTED = 4; // RadioError:REQUEST_NOT_SUPPORTED
+
+ // Below errors are generated at the Telephony.
+ public static final int ERROR_RIL_ERROR = 10000; // Nothing or only exception is
+ // returned from RIL.
+ public static final int ERROR_INVALID_SCANID = 10001; // The scanId is invalid. The user is
+ // either trying to stop a scan which
+ // does not exist or started by others.
+ public static final int ERROR_INTERRUPTED = 10002; // Scan was interrupted by another scan
+ // with higher priority.
+ private final int mScanId;
+ private final int mSubId;
+
+ /**
+ * Stops the network scan
+ *
+ * This is the callback method to stop an ongoing scan. When user requests a new scan,
+ * a NetworkScan object will be returned, and the user can stop the scan by calling this
+ * method.
+ */
+ public void stop() throws RemoteException {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.stopNetworkScan(mSubId, mScanId);
+ } else {
+ throw new RemoteException("Failed to get the ITelephony instance.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "stopNetworkScan RemoteException", ex);
+ throw new RemoteException("Failed to stop the network scan with id " + mScanId);
+ }
+ }
+
+ /**
+ * Creates a new NetworkScan with scanId
+ *
+ * @param scanId The id of the scan
+ * @param subId the id of the subscription
+ * @hide
+ */
+ public NetworkScan(int scanId, int subId) {
+ mScanId = scanId;
+ mSubId = subId;
+ }
+
+ private ITelephony getITelephony() {
+ return ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ }
+}
diff --git a/android/telephony/NetworkScanRequest.java b/android/telephony/NetworkScanRequest.java
new file mode 100644
index 00000000..d2aef200
--- /dev/null
+++ b/android/telephony/NetworkScanRequest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Defines a request to peform a network scan.
+ *
+ * This class defines whether the network scan will be performed only once or periodically until
+ * cancelled, when the scan is performed periodically, the time interval is not controlled by the
+ * user but defined by the modem vendor.
+ * @hide
+ */
+public final class NetworkScanRequest implements Parcelable {
+
+ // Below size limits for RAN/Band/Channel are for pre-treble modems and will be removed later.
+ /** @hide */
+ public static final int MAX_RADIO_ACCESS_NETWORKS = 8;
+ /** @hide */
+ public static final int MAX_BANDS = 8;
+ /** @hide */
+ public static final int MAX_CHANNELS = 32;
+
+ /** Performs the scan only once */
+ public static final int SCAN_TYPE_ONE_SHOT = 0;
+ /**
+ * Performs the scan periodically until cancelled
+ *
+ * The modem will start new scans periodically, and the interval between two scans is usually
+ * multiple minutes.
+ * */
+ public static final int SCAN_TYPE_PERIODIC = 1;
+
+ /** Defines the type of the scan. */
+ public int scanType;
+
+ /** Describes the radio access technologies with bands or channels that need to be scanned. */
+ public RadioAccessSpecifier[] specifiers;
+
+ /**
+ * Creates a new NetworkScanRequest with scanType and network specifiers
+ *
+ * @param scanType The type of the scan
+ * @param specifiers the radio network with bands / channels to be scanned
+ */
+ public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers) {
+ this.scanType = scanType;
+ this.specifiers = specifiers;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(scanType);
+ dest.writeParcelableArray(specifiers, flags);
+ }
+
+ private NetworkScanRequest(Parcel in) {
+ scanType = in.readInt();
+ specifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
+ Object.class.getClassLoader(),
+ RadioAccessSpecifier.class);
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ NetworkScanRequest nsr;
+
+ try {
+ nsr = (NetworkScanRequest) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (scanType == nsr.scanType
+ && Arrays.equals(specifiers, nsr.specifiers));
+ }
+
+ @Override
+ public int hashCode () {
+ return ((scanType * 31)
+ + (Arrays.hashCode(specifiers)) * 37);
+ }
+
+ public static final Creator<NetworkScanRequest> CREATOR =
+ new Creator<NetworkScanRequest>() {
+ @Override
+ public NetworkScanRequest createFromParcel(Parcel in) {
+ return new NetworkScanRequest(in);
+ }
+
+ @Override
+ public NetworkScanRequest[] newArray(int size) {
+ return new NetworkScanRequest[size];
+ }
+ };
+}
diff --git a/android/telephony/PcoData.java b/android/telephony/PcoData.java
new file mode 100644
index 00000000..3e735e74
--- /dev/null
+++ b/android/telephony/PcoData.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains Carrier-specific (and opaque) Protocol configuration Option
+ * Data. In general this is only passed on to carrier-specific applications
+ * for interpretation.
+ *
+ * @hide
+ */
+public class PcoData implements Parcelable {
+
+ public final int cid;
+ public final String bearerProto;
+ public final int pcoId;
+ public final byte[] contents;
+
+ public PcoData(int cid, String bearerProto, int pcoId, byte[]contents) {
+ this.cid = cid;
+ this.bearerProto = bearerProto;
+ this.pcoId = pcoId;
+ this.contents = contents;
+ }
+
+ public PcoData(Parcel in) {
+ cid = in.readInt();
+ bearerProto = in.readString();
+ pcoId = in.readInt();
+ contents = in.createByteArray();
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(cid);
+ out.writeString(bearerProto);
+ out.writeInt(pcoId);
+ out.writeByteArray(contents);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable.Creator}
+ *
+ * @hide
+ */
+ public static final Parcelable.Creator<PcoData> CREATOR = new Parcelable.Creator() {
+ public PcoData createFromParcel(Parcel in) {
+ return new PcoData(in);
+ }
+
+ public PcoData[] newArray(int size) {
+ return new PcoData[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
+ contents.length + "])";
+ }
+}
diff --git a/android/telephony/PhoneNumberFormattingTextWatcher.java b/android/telephony/PhoneNumberFormattingTextWatcher.java
new file mode 100644
index 00000000..f7dee99f
--- /dev/null
+++ b/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import com.android.i18n.phonenumbers.AsYouTypeFormatter;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+
+import android.telephony.PhoneNumberUtils;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.TextWatcher;
+
+import java.util.Locale;
+
+/**
+ * Watches a {@link android.widget.TextView} and if a phone number is entered
+ * will format it.
+ * <p>
+ * Stop formatting when the user
+ * <ul>
+ * <li>Inputs non-dialable characters</li>
+ * <li>Removes the separator in the middle of string.</li>
+ * </ul>
+ * <p>
+ * The formatting will be restarted once the text is cleared.
+ */
+public class PhoneNumberFormattingTextWatcher implements TextWatcher {
+
+ /**
+ * Indicates the change was caused by ourselves.
+ */
+ private boolean mSelfChange = false;
+
+ /**
+ * Indicates the formatting has been stopped.
+ */
+ private boolean mStopFormatting;
+
+ private AsYouTypeFormatter mFormatter;
+
+ /**
+ * The formatting is based on the current system locale and future locale changes
+ * may not take effect on this instance.
+ */
+ public PhoneNumberFormattingTextWatcher() {
+ this(Locale.getDefault().getCountry());
+ }
+
+ /**
+ * The formatting is based on the given <code>countryCode</code>.
+ *
+ * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
+ * where the phone number is being entered.
+ */
+ public PhoneNumberFormattingTextWatcher(String countryCode) {
+ if (countryCode == null) throw new IllegalArgumentException();
+ mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ if (mSelfChange || mStopFormatting) {
+ return;
+ }
+ // If the user manually deleted any non-dialable characters, stop formatting
+ if (count > 0 && hasSeparator(s, start, count)) {
+ stopFormatting();
+ }
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ if (mSelfChange || mStopFormatting) {
+ return;
+ }
+ // If the user inserted any non-dialable characters, stop formatting
+ if (count > 0 && hasSeparator(s, start, count)) {
+ stopFormatting();
+ }
+ }
+
+ @Override
+ public synchronized void afterTextChanged(Editable s) {
+ if (mStopFormatting) {
+ // Restart the formatting when all texts were clear.
+ mStopFormatting = !(s.length() == 0);
+ return;
+ }
+ if (mSelfChange) {
+ // Ignore the change caused by s.replace().
+ return;
+ }
+ String formatted = reformat(s, Selection.getSelectionEnd(s));
+ if (formatted != null) {
+ int rememberedPos = mFormatter.getRememberedPosition();
+ mSelfChange = true;
+ s.replace(0, s.length(), formatted, 0, formatted.length());
+ // The text could be changed by other TextWatcher after we changed it. If we found the
+ // text is not the one we were expecting, just give up calling setSelection().
+ if (formatted.equals(s.toString())) {
+ Selection.setSelection(s, rememberedPos);
+ }
+ mSelfChange = false;
+ }
+ PhoneNumberUtils.ttsSpanAsPhoneNumber(s, 0, s.length());
+ }
+
+ /**
+ * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
+ * nearest dialable char to the left. For instance, if the number is (650) 123-45678 and '4' is
+ * removed then the cursor should be behind '3' instead of '-'.
+ */
+ private String reformat(CharSequence s, int cursor) {
+ // The index of char to the leftward of the cursor.
+ int curIndex = cursor - 1;
+ String formatted = null;
+ mFormatter.clear();
+ char lastNonSeparator = 0;
+ boolean hasCursor = false;
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (PhoneNumberUtils.isNonSeparator(c)) {
+ if (lastNonSeparator != 0) {
+ formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+ hasCursor = false;
+ }
+ lastNonSeparator = c;
+ }
+ if (i == curIndex) {
+ hasCursor = true;
+ }
+ }
+ if (lastNonSeparator != 0) {
+ formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+ }
+ return formatted;
+ }
+
+ private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
+ return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
+ : mFormatter.inputDigit(lastNonSeparator);
+ }
+
+ private void stopFormatting() {
+ mStopFormatting = true;
+ mFormatter.clear();
+ }
+
+ private boolean hasSeparator(final CharSequence s, final int start, final int count) {
+ for (int i = start; i < start + count; i++) {
+ char c = s.charAt(i);
+ if (!PhoneNumberUtils.isNonSeparator(c)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/android/telephony/PhoneNumberUtils.java b/android/telephony/PhoneNumberUtils.java
new file mode 100644
index 00000000..d5ff1adb
--- /dev/null
+++ b/android/telephony/PhoneNumberUtils.java
@@ -0,0 +1,3167 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.i18n.phonenumbers.ShortNumberInfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.location.CountryDetector;
+import android.net.Uri;
+import android.os.SystemProperties;
+import android.os.PersistableBundle;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.telecom.PhoneAccount;
+import android.text.Editable;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.TtsSpan;
+import android.util.SparseIntArray;
+
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Various utilities for dealing with phone number strings.
+ */
+public class PhoneNumberUtils
+{
+ /*
+ * Special characters
+ *
+ * (See "What is a phone number?" doc)
+ * 'p' --- GSM pause character, same as comma
+ * 'n' --- GSM wild character
+ * 'w' --- GSM wait character
+ */
+ public static final char PAUSE = ',';
+ public static final char WAIT = ';';
+ public static final char WILD = 'N';
+
+ /*
+ * Calling Line Identification Restriction (CLIR)
+ */
+ private static final String CLIR_ON = "*31#";
+ private static final String CLIR_OFF = "#31#";
+
+ /*
+ * TOA = TON + NPI
+ * See TS 24.008 section 10.5.4.7 for details.
+ * These are the only really useful TOA values
+ */
+ public static final int TOA_International = 0x91;
+ public static final int TOA_Unknown = 0x81;
+
+ static final String LOG_TAG = "PhoneNumberUtils";
+ private static final boolean DBG = false;
+
+ /*
+ * global-phone-number = ["+"] 1*( DIGIT / written-sep )
+ * written-sep = ("-"/".")
+ */
+ private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
+ Pattern.compile("[\\+]?[0-9.-]+");
+
+ /** True if c is ISO-LATIN characters 0-9 */
+ public static boolean
+ isISODigit (char c) {
+ return c >= '0' && c <= '9';
+ }
+
+ /** True if c is ISO-LATIN characters 0-9, *, # */
+ public final static boolean
+ is12Key(char c) {
+ return (c >= '0' && c <= '9') || c == '*' || c == '#';
+ }
+
+ /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */
+ public final static boolean
+ isDialable(char c) {
+ return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
+ }
+
+ /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD) */
+ public final static boolean
+ isReallyDialable(char c) {
+ return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
+ }
+
+ /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE */
+ public final static boolean
+ isNonSeparator(char c) {
+ return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
+ || c == WILD || c == WAIT || c == PAUSE;
+ }
+
+ /** This any anything to the right of this char is part of the
+ * post-dial string (eg this is PAUSE or WAIT)
+ */
+ public final static boolean
+ isStartsPostDial (char c) {
+ return c == PAUSE || c == WAIT;
+ }
+
+ private static boolean
+ isPause (char c){
+ return c == 'p'||c == 'P';
+ }
+
+ private static boolean
+ isToneWait (char c){
+ return c == 'w'||c == 'W';
+ }
+
+
+ /** Returns true if ch is not dialable or alpha char */
+ private static boolean isSeparator(char ch) {
+ return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
+ }
+
+ /** Extracts the phone number from an Intent.
+ *
+ * @param intent the intent to get the number of
+ * @param context a context to use for database access
+ *
+ * @return the phone number that would be called by the intent, or
+ * <code>null</code> if the number cannot be found.
+ */
+ public static String getNumberFromIntent(Intent intent, Context context) {
+ String number = null;
+
+ Uri uri = intent.getData();
+
+ if (uri == null) {
+ return null;
+ }
+
+ String scheme = uri.getScheme();
+
+ if (scheme.equals("tel") || scheme.equals("sip")) {
+ return uri.getSchemeSpecificPart();
+ }
+
+ if (context == null) {
+ return null;
+ }
+
+ String type = intent.resolveType(context);
+ String phoneColumn = null;
+
+ // Correctly read out the phone entry based on requested provider
+ final String authority = uri.getAuthority();
+ if (Contacts.AUTHORITY.equals(authority)) {
+ phoneColumn = Contacts.People.Phones.NUMBER;
+ } else if (ContactsContract.AUTHORITY.equals(authority)) {
+ phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
+ }
+
+ Cursor c = null;
+ try {
+ c = context.getContentResolver().query(uri, new String[] { phoneColumn },
+ null, null, null);
+ if (c != null) {
+ if (c.moveToFirst()) {
+ number = c.getString(c.getColumnIndex(phoneColumn));
+ }
+ }
+ } catch (RuntimeException e) {
+ Rlog.e(LOG_TAG, "Error getting phone number.", e);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ return number;
+ }
+
+ /** Extracts the network address portion and canonicalizes
+ * (filters out separators.)
+ * Network address portion is everything up to DTMF control digit
+ * separators (pause or wait), but without non-dialable characters.
+ *
+ * Please note that the GSM wild character is allowed in the result.
+ * This must be resolved before dialing.
+ *
+ * Returns null if phoneNumber == null
+ */
+ public static String
+ extractNetworkPortion(String phoneNumber) {
+ if (phoneNumber == null) {
+ return null;
+ }
+
+ int len = phoneNumber.length();
+ StringBuilder ret = new StringBuilder(len);
+
+ for (int i = 0; i < len; i++) {
+ char c = phoneNumber.charAt(i);
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ ret.append(digit);
+ } else if (c == '+') {
+ // Allow '+' as first character or after CLIR MMI prefix
+ String prefix = ret.toString();
+ if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
+ ret.append(c);
+ }
+ } else if (isDialable(c)) {
+ ret.append(c);
+ } else if (isStartsPostDial (c)) {
+ break;
+ }
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Extracts the network address portion and canonicalize.
+ *
+ * This function is equivalent to extractNetworkPortion(), except
+ * for allowing the PLUS character to occur at arbitrary positions
+ * in the address portion, not just the first position.
+ *
+ * @hide
+ */
+ public static String extractNetworkPortionAlt(String phoneNumber) {
+ if (phoneNumber == null) {
+ return null;
+ }
+
+ int len = phoneNumber.length();
+ StringBuilder ret = new StringBuilder(len);
+ boolean haveSeenPlus = false;
+
+ for (int i = 0; i < len; i++) {
+ char c = phoneNumber.charAt(i);
+ if (c == '+') {
+ if (haveSeenPlus) {
+ continue;
+ }
+ haveSeenPlus = true;
+ }
+ if (isDialable(c)) {
+ ret.append(c);
+ } else if (isStartsPostDial (c)) {
+ break;
+ }
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Strips separators from a phone number string.
+ * @param phoneNumber phone number to strip.
+ * @return phone string stripped of separators.
+ */
+ public static String stripSeparators(String phoneNumber) {
+ if (phoneNumber == null) {
+ return null;
+ }
+ int len = phoneNumber.length();
+ StringBuilder ret = new StringBuilder(len);
+
+ for (int i = 0; i < len; i++) {
+ char c = phoneNumber.charAt(i);
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ ret.append(digit);
+ } else if (isNonSeparator(c)) {
+ ret.append(c);
+ }
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Translates keypad letters to actual digits (e.g. 1-800-GOOG-411 will
+ * become 1-800-4664-411), and then strips all separators (e.g. 1-800-4664-411 will become
+ * 18004664411).
+ *
+ * @see #convertKeypadLettersToDigits(String)
+ * @see #stripSeparators(String)
+ *
+ * @hide
+ */
+ public static String convertAndStrip(String phoneNumber) {
+ return stripSeparators(convertKeypadLettersToDigits(phoneNumber));
+ }
+
+ /**
+ * Converts pause and tonewait pause characters
+ * to Android representation.
+ * RFC 3601 says pause is 'p' and tonewait is 'w'.
+ * @hide
+ */
+ public static String convertPreDial(String phoneNumber) {
+ if (phoneNumber == null) {
+ return null;
+ }
+ int len = phoneNumber.length();
+ StringBuilder ret = new StringBuilder(len);
+
+ for (int i = 0; i < len; i++) {
+ char c = phoneNumber.charAt(i);
+
+ if (isPause(c)) {
+ c = PAUSE;
+ } else if (isToneWait(c)) {
+ c = WAIT;
+ }
+ ret.append(c);
+ }
+ return ret.toString();
+ }
+
+ /** or -1 if both are negative */
+ static private int
+ minPositive (int a, int b) {
+ if (a >= 0 && b >= 0) {
+ return (a < b) ? a : b;
+ } else if (a >= 0) { /* && b < 0 */
+ return a;
+ } else if (b >= 0) { /* && a < 0 */
+ return b;
+ } else { /* a < 0 && b < 0 */
+ return -1;
+ }
+ }
+
+ private static void log(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ /** index of the last character of the network portion
+ * (eg anything after is a post-dial string)
+ */
+ static private int
+ indexOfLastNetworkChar(String a) {
+ int pIndex, wIndex;
+ int origLength;
+ int trimIndex;
+
+ origLength = a.length();
+
+ pIndex = a.indexOf(PAUSE);
+ wIndex = a.indexOf(WAIT);
+
+ trimIndex = minPositive(pIndex, wIndex);
+
+ if (trimIndex < 0) {
+ return origLength - 1;
+ } else {
+ return trimIndex - 1;
+ }
+ }
+
+ /**
+ * Extracts the post-dial sequence of DTMF control digits, pauses, and
+ * waits. Strips separators. This string may be empty, but will not be null
+ * unless phoneNumber == null.
+ *
+ * Returns null if phoneNumber == null
+ */
+
+ public static String
+ extractPostDialPortion(String phoneNumber) {
+ if (phoneNumber == null) return null;
+
+ int trimIndex;
+ StringBuilder ret = new StringBuilder();
+
+ trimIndex = indexOfLastNetworkChar (phoneNumber);
+
+ for (int i = trimIndex + 1, s = phoneNumber.length()
+ ; i < s; i++
+ ) {
+ char c = phoneNumber.charAt(i);
+ if (isNonSeparator(c)) {
+ ret.append(c);
+ }
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
+ */
+ public static boolean compare(String a, String b) {
+ // We've used loose comparation at least Eclair, which may change in the future.
+
+ return compare(a, b, false);
+ }
+
+ /**
+ * Compare phone numbers a and b, and return true if they're identical
+ * enough for caller ID purposes. Checks a resource to determine whether
+ * to use a strict or loose comparison algorithm.
+ */
+ public static boolean compare(Context context, String a, String b) {
+ boolean useStrict = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_use_strict_phone_number_comparation);
+ return compare(a, b, useStrict);
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public static boolean compare(String a, String b, boolean useStrictComparation) {
+ return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
+ }
+
+ /**
+ * Compare phone numbers a and b, return true if they're identical
+ * enough for caller ID purposes.
+ *
+ * - Compares from right to left
+ * - requires MIN_MATCH (7) characters to match
+ * - handles common trunk prefixes and international prefixes
+ * (basically, everything except the Russian trunk prefix)
+ *
+ * Note that this method does not return false even when the two phone numbers
+ * are not exactly same; rather; we can call this method "similar()", not "equals()".
+ *
+ * @hide
+ */
+ public static boolean
+ compareLoosely(String a, String b) {
+ int ia, ib;
+ int matched;
+ int numNonDialableCharsInA = 0;
+ int numNonDialableCharsInB = 0;
+
+ if (a == null || b == null) return a == b;
+
+ if (a.length() == 0 || b.length() == 0) {
+ return false;
+ }
+
+ ia = indexOfLastNetworkChar (a);
+ ib = indexOfLastNetworkChar (b);
+ matched = 0;
+
+ while (ia >= 0 && ib >=0) {
+ char ca, cb;
+ boolean skipCmp = false;
+
+ ca = a.charAt(ia);
+
+ if (!isDialable(ca)) {
+ ia--;
+ skipCmp = true;
+ numNonDialableCharsInA++;
+ }
+
+ cb = b.charAt(ib);
+
+ if (!isDialable(cb)) {
+ ib--;
+ skipCmp = true;
+ numNonDialableCharsInB++;
+ }
+
+ if (!skipCmp) {
+ if (cb != ca && ca != WILD && cb != WILD) {
+ break;
+ }
+ ia--; ib--; matched++;
+ }
+ }
+
+ if (matched < MIN_MATCH) {
+ int effectiveALen = a.length() - numNonDialableCharsInA;
+ int effectiveBLen = b.length() - numNonDialableCharsInB;
+
+
+ // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH,
+ // treat them as equal (i.e. 404-04 and 40404)
+ if (effectiveALen == effectiveBLen && effectiveALen == matched) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // At least one string has matched completely;
+ if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
+ return true;
+ }
+
+ /*
+ * Now, what remains must be one of the following for a
+ * match:
+ *
+ * - a '+' on one and a '00' or a '011' on the other
+ * - a '0' on one and a (+,00)<country code> on the other
+ * (for this, a '0' and a '00' prefix would have succeeded above)
+ */
+
+ if (matchIntlPrefix(a, ia + 1)
+ && matchIntlPrefix (b, ib +1)
+ ) {
+ return true;
+ }
+
+ if (matchTrunkPrefix(a, ia + 1)
+ && matchIntlPrefixAndCC(b, ib +1)
+ ) {
+ return true;
+ }
+
+ if (matchTrunkPrefix(b, ib + 1)
+ && matchIntlPrefixAndCC(a, ia +1)
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean
+ compareStrictly(String a, String b) {
+ return compareStrictly(a, b, true);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean
+ compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
+ if (a == null || b == null) {
+ return a == b;
+ } else if (a.length() == 0 && b.length() == 0) {
+ return false;
+ }
+
+ int forwardIndexA = 0;
+ int forwardIndexB = 0;
+
+ CountryCallingCodeAndNewIndex cccA =
+ tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
+ CountryCallingCodeAndNewIndex cccB =
+ tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
+ boolean bothHasCountryCallingCode = false;
+ boolean okToIgnorePrefix = true;
+ boolean trunkPrefixIsOmittedA = false;
+ boolean trunkPrefixIsOmittedB = false;
+ if (cccA != null && cccB != null) {
+ if (cccA.countryCallingCode != cccB.countryCallingCode) {
+ // Different Country Calling Code. Must be different phone number.
+ return false;
+ }
+ // When both have ccc, do not ignore trunk prefix. Without this,
+ // "+81123123" becomes same as "+810123123" (+81 == Japan)
+ okToIgnorePrefix = false;
+ bothHasCountryCallingCode = true;
+ forwardIndexA = cccA.newIndex;
+ forwardIndexB = cccB.newIndex;
+ } else if (cccA == null && cccB == null) {
+ // When both do not have ccc, do not ignore trunk prefix. Without this,
+ // "123123" becomes same as "0123123"
+ okToIgnorePrefix = false;
+ } else {
+ if (cccA != null) {
+ forwardIndexA = cccA.newIndex;
+ } else {
+ int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
+ if (tmp >= 0) {
+ forwardIndexA = tmp;
+ trunkPrefixIsOmittedA = true;
+ }
+ }
+ if (cccB != null) {
+ forwardIndexB = cccB.newIndex;
+ } else {
+ int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
+ if (tmp >= 0) {
+ forwardIndexB = tmp;
+ trunkPrefixIsOmittedB = true;
+ }
+ }
+ }
+
+ int backwardIndexA = a.length() - 1;
+ int backwardIndexB = b.length() - 1;
+ while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
+ boolean skip_compare = false;
+ final char chA = a.charAt(backwardIndexA);
+ final char chB = b.charAt(backwardIndexB);
+ if (isSeparator(chA)) {
+ backwardIndexA--;
+ skip_compare = true;
+ }
+ if (isSeparator(chB)) {
+ backwardIndexB--;
+ skip_compare = true;
+ }
+
+ if (!skip_compare) {
+ if (chA != chB) {
+ return false;
+ }
+ backwardIndexA--;
+ backwardIndexB--;
+ }
+ }
+
+ if (okToIgnorePrefix) {
+ if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
+ !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
+ if (acceptInvalidCCCPrefix) {
+ // Maybe the code handling the special case for Thailand makes the
+ // result garbled, so disable the code and try again.
+ // e.g. "16610001234" must equal to "6610001234", but with
+ // Thailand-case handling code, they become equal to each other.
+ //
+ // Note: we select simplicity rather than adding some complicated
+ // logic here for performance(like "checking whether remaining
+ // numbers are just 66 or not"), assuming inputs are small
+ // enough.
+ return compare(a, b, false);
+ } else {
+ return false;
+ }
+ }
+ if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
+ !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
+ if (acceptInvalidCCCPrefix) {
+ return compare(a, b, false);
+ } else {
+ return false;
+ }
+ }
+ } else {
+ // In the US, 1-650-555-1234 must be equal to 650-555-1234,
+ // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
+ // This request exists just in US (with 1 trunk (NDD) prefix).
+ // In addition, "011 11 7005554141" must not equal to "+17005554141",
+ // while "011 1 7005554141" must equal to "+17005554141"
+ //
+ // In this comparison, we ignore the prefix '1' just once, when
+ // - at least either does not have CCC, or
+ // - the remaining non-separator number is 1
+ boolean maybeNamp = !bothHasCountryCallingCode;
+ while (backwardIndexA >= forwardIndexA) {
+ final char chA = a.charAt(backwardIndexA);
+ if (isDialable(chA)) {
+ if (maybeNamp && tryGetISODigit(chA) == 1) {
+ maybeNamp = false;
+ } else {
+ return false;
+ }
+ }
+ backwardIndexA--;
+ }
+ while (backwardIndexB >= forwardIndexB) {
+ final char chB = b.charAt(backwardIndexB);
+ if (isDialable(chB)) {
+ if (maybeNamp && tryGetISODigit(chB) == 1) {
+ maybeNamp = false;
+ } else {
+ return false;
+ }
+ }
+ backwardIndexB--;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the rightmost MIN_MATCH (5) characters in the network portion
+ * in *reversed* order
+ *
+ * This can be used to do a database lookup against the column
+ * that stores getStrippedReversed()
+ *
+ * Returns null if phoneNumber == null
+ */
+ public static String
+ toCallerIDMinMatch(String phoneNumber) {
+ String np = extractNetworkPortionAlt(phoneNumber);
+ return internalGetStrippedReversed(np, MIN_MATCH);
+ }
+
+ /**
+ * Returns the network portion reversed.
+ * This string is intended to go into an index column for a
+ * database lookup.
+ *
+ * Returns null if phoneNumber == null
+ */
+ public static String
+ getStrippedReversed(String phoneNumber) {
+ String np = extractNetworkPortionAlt(phoneNumber);
+
+ if (np == null) return null;
+
+ return internalGetStrippedReversed(np, np.length());
+ }
+
+ /**
+ * Returns the last numDigits of the reversed phone number
+ * Returns null if np == null
+ */
+ private static String
+ internalGetStrippedReversed(String np, int numDigits) {
+ if (np == null) return null;
+
+ StringBuilder ret = new StringBuilder(numDigits);
+ int length = np.length();
+
+ for (int i = length - 1, s = length
+ ; i >= 0 && (s - i) <= numDigits ; i--
+ ) {
+ char c = np.charAt(i);
+
+ ret.append(c);
+ }
+
+ return ret.toString();
+ }
+
+ /**
+ * Basically: makes sure there's a + in front of a
+ * TOA_International number
+ *
+ * Returns null if s == null
+ */
+ public static String
+ stringFromStringAndTOA(String s, int TOA) {
+ if (s == null) return null;
+
+ if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
+ return "+" + s;
+ }
+
+ return s;
+ }
+
+ /**
+ * Returns the TOA for the given dial string
+ * Basically, returns TOA_International if there's a + prefix
+ */
+
+ public static int
+ toaFromString(String s) {
+ if (s != null && s.length() > 0 && s.charAt(0) == '+') {
+ return TOA_International;
+ }
+
+ return TOA_Unknown;
+ }
+
+ /**
+ * 3GPP TS 24.008 10.5.4.7
+ * Called Party BCD Number
+ *
+ * See Also TS 51.011 10.5.1 "dialing number/ssc string"
+ * and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
+ *
+ * @param bytes the data buffer
+ * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
+ * @param length is the number of bytes including TOA byte
+ * and must be at least 2
+ *
+ * @return partial string on invalid decode
+ *
+ * FIXME(mkf) support alphanumeric address type
+ * currently implemented in SMSMessage.getAddress()
+ */
+ public static String
+ calledPartyBCDToString (byte[] bytes, int offset, int length) {
+ boolean prependPlus = false;
+ StringBuilder ret = new StringBuilder(1 + length * 2);
+
+ if (length < 2) {
+ return "";
+ }
+
+ //Only TON field should be taken in consideration
+ if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
+ prependPlus = true;
+ }
+
+ internalCalledPartyBCDFragmentToString(
+ ret, bytes, offset + 1, length - 1);
+
+ if (prependPlus && ret.length() == 0) {
+ // If the only thing there is a prepended plus, return ""
+ return "";
+ }
+
+ if (prependPlus) {
+ // This is an "international number" and should have
+ // a plus prepended to the dialing number. But there
+ // can also be GSM MMI codes as defined in TS 22.030 6.5.2
+ // so we need to handle those also.
+ //
+ // http://web.telia.com/~u47904776/gsmkode.htm
+ // has a nice list of some of these GSM codes.
+ //
+ // Examples are:
+ // **21*+886988171479#
+ // **21*8311234567#
+ // *21#
+ // #21#
+ // *#21#
+ // *31#+11234567890
+ // #31#+18311234567
+ // #31#8311234567
+ // 18311234567
+ // +18311234567#
+ // +18311234567
+ // Odd ball cases that some phones handled
+ // where there is no dialing number so they
+ // append the "+"
+ // *21#+
+ // **21#+
+ String retString = ret.toString();
+ Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
+ Matcher m = p.matcher(retString);
+ if (m.matches()) {
+ if ("".equals(m.group(2))) {
+ // Started with two [#*] ends with #
+ // So no dialing number and we'll just
+ // append a +, this handles **21#+
+ ret = new StringBuilder();
+ ret.append(m.group(1));
+ ret.append(m.group(3));
+ ret.append(m.group(4));
+ ret.append(m.group(5));
+ ret.append("+");
+ } else {
+ // Starts with [#*] and ends with #
+ // Assume group 4 is a dialing number
+ // such as *21*+1234554#
+ ret = new StringBuilder();
+ ret.append(m.group(1));
+ ret.append(m.group(2));
+ ret.append(m.group(3));
+ ret.append("+");
+ ret.append(m.group(4));
+ ret.append(m.group(5));
+ }
+ } else {
+ p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
+ m = p.matcher(retString);
+ if (m.matches()) {
+ // Starts with [#*] and only one other [#*]
+ // Assume the data after last [#*] is dialing
+ // number (i.e. group 4) such as *31#+11234567890.
+ // This also includes the odd ball *21#+
+ ret = new StringBuilder();
+ ret.append(m.group(1));
+ ret.append(m.group(2));
+ ret.append(m.group(3));
+ ret.append("+");
+ ret.append(m.group(4));
+ } else {
+ // Does NOT start with [#*] just prepend '+'
+ ret = new StringBuilder();
+ ret.append('+');
+ ret.append(retString);
+ }
+ }
+ }
+
+ return ret.toString();
+ }
+
+ private static void
+ internalCalledPartyBCDFragmentToString(
+ StringBuilder sb, byte [] bytes, int offset, int length) {
+ for (int i = offset ; i < length + offset ; i++) {
+ byte b;
+ char c;
+
+ c = bcdToChar((byte)(bytes[i] & 0xf));
+
+ if (c == 0) {
+ return;
+ }
+ sb.append(c);
+
+ // FIXME(mkf) TS 23.040 9.1.2.3 says
+ // "if a mobile receives 1111 in a position prior to
+ // the last semi-octet then processing shall commence with
+ // the next semi-octet and the intervening
+ // semi-octet shall be ignored"
+ // How does this jive with 24.008 10.5.4.7
+
+ b = (byte)((bytes[i] >> 4) & 0xf);
+
+ if (b == 0xf && i + 1 == length + offset) {
+ //ignore final 0xf
+ break;
+ }
+
+ c = bcdToChar(b);
+ if (c == 0) {
+ return;
+ }
+
+ sb.append(c);
+ }
+
+ }
+
+ /**
+ * Like calledPartyBCDToString, but field does not start with a
+ * TOA byte. For example: SIM ADN extension fields
+ */
+
+ public static String
+ calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
+ StringBuilder ret = new StringBuilder(length * 2);
+
+ internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
+
+ return ret.toString();
+ }
+
+ /** returns 0 on invalid value */
+ private static char
+ bcdToChar(byte b) {
+ if (b < 0xa) {
+ return (char)('0' + b);
+ } else switch (b) {
+ case 0xa: return '*';
+ case 0xb: return '#';
+ case 0xc: return PAUSE;
+ case 0xd: return WILD;
+
+ default: return 0;
+ }
+ }
+
+ private static int
+ charToBCD(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c == '*') {
+ return 0xa;
+ } else if (c == '#') {
+ return 0xb;
+ } else if (c == PAUSE) {
+ return 0xc;
+ } else if (c == WILD) {
+ return 0xd;
+ } else if (c == WAIT) {
+ return 0xe;
+ } else {
+ throw new RuntimeException ("invalid char for BCD " + c);
+ }
+ }
+
+ /**
+ * Return true iff the network portion of <code>address</code> is,
+ * as far as we can tell on the device, suitable for use as an SMS
+ * destination address.
+ */
+ public static boolean isWellFormedSmsAddress(String address) {
+ String networkPortion =
+ PhoneNumberUtils.extractNetworkPortion(address);
+
+ return (!(networkPortion.equals("+")
+ || TextUtils.isEmpty(networkPortion)))
+ && isDialable(networkPortion);
+ }
+
+ public static boolean isGlobalPhoneNumber(String phoneNumber) {
+ if (TextUtils.isEmpty(phoneNumber)) {
+ return false;
+ }
+
+ Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
+ return match.matches();
+ }
+
+ private static boolean isDialable(String address) {
+ for (int i = 0, count = address.length(); i < count; i++) {
+ if (!isDialable(address.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isNonSeparator(String address) {
+ for (int i = 0, count = address.length(); i < count; i++) {
+ if (!isNonSeparator(address.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ /**
+ * Note: calls extractNetworkPortion(), so do not use for
+ * SIM EF[ADN] style records
+ *
+ * Returns null if network portion is empty.
+ */
+ public static byte[]
+ networkPortionToCalledPartyBCD(String s) {
+ String networkPortion = extractNetworkPortion(s);
+ return numberToCalledPartyBCDHelper(networkPortion, false);
+ }
+
+ /**
+ * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
+ * one-byte length prefix.
+ */
+ public static byte[]
+ networkPortionToCalledPartyBCDWithLength(String s) {
+ String networkPortion = extractNetworkPortion(s);
+ return numberToCalledPartyBCDHelper(networkPortion, true);
+ }
+
+ /**
+ * Convert a dialing number to BCD byte array
+ *
+ * @param number dialing number string
+ * if the dialing number starts with '+', set to international TOA
+ * @return BCD byte array
+ */
+ public static byte[]
+ numberToCalledPartyBCD(String number) {
+ return numberToCalledPartyBCDHelper(number, false);
+ }
+
+ /**
+ * If includeLength is true, prepend a one-byte length value to
+ * the return array.
+ */
+ private static byte[]
+ numberToCalledPartyBCDHelper(String number, boolean includeLength) {
+ int numberLenReal = number.length();
+ int numberLenEffective = numberLenReal;
+ boolean hasPlus = number.indexOf('+') != -1;
+ if (hasPlus) numberLenEffective--;
+
+ if (numberLenEffective == 0) return null;
+
+ int resultLen = (numberLenEffective + 1) / 2; // Encoded numbers require only 4 bits each.
+ int extraBytes = 1; // Prepended TOA byte.
+ if (includeLength) extraBytes++; // Optional prepended length byte.
+ resultLen += extraBytes;
+
+ byte[] result = new byte[resultLen];
+
+ int digitCount = 0;
+ for (int i = 0; i < numberLenReal; i++) {
+ char c = number.charAt(i);
+ if (c == '+') continue;
+ int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+ result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+ digitCount++;
+ }
+
+ // 1-fill any trailing odd nibble/quartet.
+ if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
+
+ int offset = 0;
+ if (includeLength) result[offset++] = (byte)(resultLen - 1);
+ result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
+
+ return result;
+ }
+
+ //================ Number formatting =========================
+
+ /** The current locale is unknown, look for a country code or don't format */
+ public static final int FORMAT_UNKNOWN = 0;
+ /** NANP formatting */
+ public static final int FORMAT_NANP = 1;
+ /** Japanese formatting */
+ public static final int FORMAT_JAPAN = 2;
+
+ /** List of country codes for countries that use the NANP */
+ private static final String[] NANP_COUNTRIES = new String[] {
+ "US", // United States
+ "CA", // Canada
+ "AS", // American Samoa
+ "AI", // Anguilla
+ "AG", // Antigua and Barbuda
+ "BS", // Bahamas
+ "BB", // Barbados
+ "BM", // Bermuda
+ "VG", // British Virgin Islands
+ "KY", // Cayman Islands
+ "DM", // Dominica
+ "DO", // Dominican Republic
+ "GD", // Grenada
+ "GU", // Guam
+ "JM", // Jamaica
+ "PR", // Puerto Rico
+ "MS", // Montserrat
+ "MP", // Northern Mariana Islands
+ "KN", // Saint Kitts and Nevis
+ "LC", // Saint Lucia
+ "VC", // Saint Vincent and the Grenadines
+ "TT", // Trinidad and Tobago
+ "TC", // Turks and Caicos Islands
+ "VI", // U.S. Virgin Islands
+ };
+
+ private static final String KOREA_ISO_COUNTRY_CODE = "KR";
+
+ private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
+
+ /**
+ * Breaks the given number down and formats it according to the rules
+ * for the country the number is from.
+ *
+ * @param source The phone number to format
+ * @return A locally acceptable formatting of the input, or the raw input if
+ * formatting rules aren't known for the number
+ *
+ * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+ */
+ @Deprecated
+ public static String formatNumber(String source) {
+ SpannableStringBuilder text = new SpannableStringBuilder(source);
+ formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
+ return text.toString();
+ }
+
+ /**
+ * Formats the given number with the given formatting type. Currently
+ * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
+ *
+ * @param source the phone number to format
+ * @param defaultFormattingType The default formatting rules to apply if the number does
+ * not begin with +[country_code]
+ * @return The phone number formatted with the given formatting type.
+ *
+ * @hide
+ * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+ */
+ @Deprecated
+ public static String formatNumber(String source, int defaultFormattingType) {
+ SpannableStringBuilder text = new SpannableStringBuilder(source);
+ formatNumber(text, defaultFormattingType);
+ return text.toString();
+ }
+
+ /**
+ * Returns the phone number formatting type for the given locale.
+ *
+ * @param locale The locale of interest, usually {@link Locale#getDefault()}
+ * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
+ * rules are not known for the given locale
+ *
+ * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+ */
+ @Deprecated
+ public static int getFormatTypeForLocale(Locale locale) {
+ String country = locale.getCountry();
+
+ return getFormatTypeFromCountryCode(country);
+ }
+
+ /**
+ * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
+ * is supported as a second argument.
+ *
+ * @param text The number to be formatted, will be modified with the formatting
+ * @param defaultFormattingType The default formatting rules to apply if the number does
+ * not begin with +[country_code]
+ *
+ * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+ */
+ @Deprecated
+ public static void formatNumber(Editable text, int defaultFormattingType) {
+ int formatType = defaultFormattingType;
+
+ if (text.length() > 2 && text.charAt(0) == '+') {
+ if (text.charAt(1) == '1') {
+ formatType = FORMAT_NANP;
+ } else if (text.length() >= 3 && text.charAt(1) == '8'
+ && text.charAt(2) == '1') {
+ formatType = FORMAT_JAPAN;
+ } else {
+ formatType = FORMAT_UNKNOWN;
+ }
+ }
+
+ switch (formatType) {
+ case FORMAT_NANP:
+ formatNanpNumber(text);
+ return;
+ case FORMAT_JAPAN:
+ formatJapaneseNumber(text);
+ return;
+ case FORMAT_UNKNOWN:
+ removeDashes(text);
+ return;
+ }
+ }
+
+ private static final int NANP_STATE_DIGIT = 1;
+ private static final int NANP_STATE_PLUS = 2;
+ private static final int NANP_STATE_ONE = 3;
+ private static final int NANP_STATE_DASH = 4;
+
+ /**
+ * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
+ * as:
+ *
+ * <p><code>
+ * xxxxx
+ * xxx-xxxx
+ * xxx-xxx-xxxx
+ * 1-xxx-xxx-xxxx
+ * +1-xxx-xxx-xxxx
+ * </code></p>
+ *
+ * @param text the number to be formatted, will be modified with the formatting
+ *
+ * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+ */
+ @Deprecated
+ public static void formatNanpNumber(Editable text) {
+ int length = text.length();
+ if (length > "+1-nnn-nnn-nnnn".length()) {
+ // The string is too long to be formatted
+ return;
+ } else if (length <= 5) {
+ // The string is either a shortcode or too short to be formatted
+ return;
+ }
+
+ CharSequence saved = text.subSequence(0, length);
+
+ // Strip the dashes first, as we're going to add them back
+ removeDashes(text);
+ length = text.length();
+
+ // When scanning the number we record where dashes need to be added,
+ // if they're non-0 at the end of the scan the dashes will be added in
+ // the proper places.
+ int dashPositions[] = new int[3];
+ int numDashes = 0;
+
+ int state = NANP_STATE_DIGIT;
+ int numDigits = 0;
+ for (int i = 0; i < length; i++) {
+ char c = text.charAt(i);
+ switch (c) {
+ case '1':
+ if (numDigits == 0 || state == NANP_STATE_PLUS) {
+ state = NANP_STATE_ONE;
+ break;
+ }
+ // fall through
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ if (state == NANP_STATE_PLUS) {
+ // Only NANP number supported for now
+ text.replace(0, length, saved);
+ return;
+ } else if (state == NANP_STATE_ONE) {
+ // Found either +1 or 1, follow it up with a dash
+ dashPositions[numDashes++] = i;
+ } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
+ // Found a digit that should be after a dash that isn't
+ dashPositions[numDashes++] = i;
+ }
+ state = NANP_STATE_DIGIT;
+ numDigits++;
+ break;
+
+ case '-':
+ state = NANP_STATE_DASH;
+ break;
+
+ case '+':
+ if (i == 0) {
+ // Plus is only allowed as the first character
+ state = NANP_STATE_PLUS;
+ break;
+ }
+ // Fall through
+ default:
+ // Unknown character, bail on formatting
+ text.replace(0, length, saved);
+ return;
+ }
+ }
+
+ if (numDigits == 7) {
+ // With 7 digits we want xxx-xxxx, not xxx-xxx-x
+ numDashes--;
+ }
+
+ // Actually put the dashes in place
+ for (int i = 0; i < numDashes; i++) {
+ int pos = dashPositions[i];
+ text.replace(pos + i, pos + i, "-");
+ }
+
+ // Remove trailing dashes
+ int len = text.length();
+ while (len > 0) {
+ if (text.charAt(len - 1) == '-') {
+ text.delete(len - 1, len);
+ len--;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Formats a phone number in-place using the Japanese formatting rules.
+ * Numbers will be formatted as:
+ *
+ * <p><code>
+ * 03-xxxx-xxxx
+ * 090-xxxx-xxxx
+ * 0120-xxx-xxx
+ * +81-3-xxxx-xxxx
+ * +81-90-xxxx-xxxx
+ * </code></p>
+ *
+ * @param text the number to be formatted, will be modified with
+ * the formatting
+ *
+ * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+ */
+ @Deprecated
+ public static void formatJapaneseNumber(Editable text) {
+ JapanesePhoneNumberFormatter.format(text);
+ }
+
+ /**
+ * Removes all dashes from the number.
+ *
+ * @param text the number to clear from dashes
+ */
+ private static void removeDashes(Editable text) {
+ int p = 0;
+ while (p < text.length()) {
+ if (text.charAt(p) == '-') {
+ text.delete(p, p + 1);
+ } else {
+ p++;
+ }
+ }
+ }
+
+ /**
+ * Formats the specified {@code phoneNumber} to the E.164 representation.
+ *
+ * @param phoneNumber the phone number to format.
+ * @param defaultCountryIso the ISO 3166-1 two letters country code.
+ * @return the E.164 representation, or null if the given phone number is not valid.
+ */
+ public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
+ return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
+ }
+
+ /**
+ * Formats the specified {@code phoneNumber} to the RFC3966 representation.
+ *
+ * @param phoneNumber the phone number to format.
+ * @param defaultCountryIso the ISO 3166-1 two letters country code.
+ * @return the RFC3966 representation, or null if the given phone number is not valid.
+ */
+ public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
+ return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
+ }
+
+ /**
+ * Formats the raw phone number (string) using the specified {@code formatIdentifier}.
+ * <p>
+ * The given phone number must have an area code and could have a country code.
+ * <p>
+ * The defaultCountryIso is used to validate the given number and generate the formatted number
+ * if the specified number doesn't have a country code.
+ *
+ * @param rawPhoneNumber The phone number to format.
+ * @param defaultCountryIso The ISO 3166-1 two letters country code.
+ * @param formatIdentifier The (enum) identifier of the desired format.
+ * @return the formatted representation, or null if the specified number is not valid.
+ */
+ private static String formatNumberInternal(
+ String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
+
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ try {
+ PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
+ if (util.isValidNumber(phoneNumber)) {
+ return util.format(phoneNumber, formatIdentifier);
+ }
+ } catch (NumberParseException ignored) { }
+
+ return null;
+ }
+
+ /**
+ * Determines if a {@param phoneNumber} is international if dialed from
+ * {@param defaultCountryIso}.
+ *
+ * @param phoneNumber The phone number.
+ * @param defaultCountryIso The current country ISO.
+ * @return {@code true} if the number is international, {@code false} otherwise.
+ * @hide
+ */
+ public static boolean isInternationalNumber(String phoneNumber, String defaultCountryIso) {
+ // If no phone number is provided, it can't be international.
+ if (TextUtils.isEmpty(phoneNumber)) {
+ return false;
+ }
+
+ // If it starts with # or * its not international.
+ if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
+ return false;
+ }
+
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ try {
+ PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+ return pn.getCountryCode() != util.getCountryCodeForRegion(defaultCountryIso);
+ } catch (NumberParseException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Format a phone number.
+ * <p>
+ * If the given number doesn't have the country code, the phone will be
+ * formatted to the default country's convention.
+ *
+ * @param phoneNumber
+ * the number to be formatted.
+ * @param defaultCountryIso
+ * the ISO 3166-1 two letters country code whose convention will
+ * be used if the given number doesn't have the country code.
+ * @return the formatted number, or null if the given number is not valid.
+ */
+ public static String formatNumber(String phoneNumber, String defaultCountryIso) {
+ // Do not attempt to format numbers that start with a hash or star symbol.
+ if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
+ return phoneNumber;
+ }
+
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ String result = null;
+ try {
+ PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+
+ if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+ (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
+ (pn.getCountryCodeSource() ==
+ PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+ /**
+ * Need to reformat any local Korean phone numbers (when the user is in Korea) with
+ * country code to corresponding national format which would replace the leading
+ * +82 with 0.
+ */
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+ pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
+ (pn.getCountryCodeSource() ==
+ PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+ /**
+ * Need to reformat Japanese phone numbers (when user is in Japan) with the national
+ * dialing format.
+ */
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else {
+ result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ }
+ } catch (NumberParseException e) {
+ }
+ return result;
+ }
+
+ /**
+ * Format the phone number only if the given number hasn't been formatted.
+ * <p>
+ * The number which has only dailable character is treated as not being
+ * formatted.
+ *
+ * @param phoneNumber
+ * the number to be formatted.
+ * @param phoneNumberE164
+ * the E164 format number whose country code is used if the given
+ * phoneNumber doesn't have the country code.
+ * @param defaultCountryIso
+ * the ISO 3166-1 two letters country code whose convention will
+ * be used if the phoneNumberE164 is null or invalid, or if phoneNumber
+ * contains IDD.
+ * @return the formatted number if the given number has been formatted,
+ * otherwise, return the given number.
+ */
+ public static String formatNumber(
+ String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
+ int len = phoneNumber.length();
+ for (int i = 0; i < len; i++) {
+ if (!isDialable(phoneNumber.charAt(i))) {
+ return phoneNumber;
+ }
+ }
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ // Get the country code from phoneNumberE164
+ if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
+ && phoneNumberE164.charAt(0) == '+') {
+ try {
+ // The number to be parsed is in E164 format, so the default region used doesn't
+ // matter.
+ PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
+ String regionCode = util.getRegionCodeForNumber(pn);
+ if (!TextUtils.isEmpty(regionCode) &&
+ // This makes sure phoneNumber doesn't contain an IDD
+ normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
+ defaultCountryIso = regionCode;
+ }
+ } catch (NumberParseException e) {
+ }
+ }
+ String result = formatNumber(phoneNumber, defaultCountryIso);
+ return result != null ? result : phoneNumber;
+ }
+
+ /**
+ * Normalize a phone number by removing the characters other than digits. If
+ * the given number has keypad letters, the letters will be converted to
+ * digits first.
+ *
+ * @param phoneNumber the number to be normalized.
+ * @return the normalized number.
+ */
+ public static String normalizeNumber(String phoneNumber) {
+ if (TextUtils.isEmpty(phoneNumber)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ int len = phoneNumber.length();
+ for (int i = 0; i < len; i++) {
+ char c = phoneNumber.charAt(i);
+ // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ sb.append(digit);
+ } else if (sb.length() == 0 && c == '+') {
+ sb.append(c);
+ } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
+ *
+ * @param number the number to perform the replacement on.
+ * @return the replaced number.
+ */
+ public static String replaceUnicodeDigits(String number) {
+ StringBuilder normalizedDigits = new StringBuilder(number.length());
+ for (char c : number.toCharArray()) {
+ int digit = Character.digit(c, 10);
+ if (digit != -1) {
+ normalizedDigits.append(digit);
+ } else {
+ normalizedDigits.append(c);
+ }
+ }
+ return normalizedDigits.toString();
+ }
+
+ // Three and four digit phone numbers for either special services,
+ // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
+ // not match.
+ //
+ // This constant used to be 5, but SMS short codes has increased in length and
+ // can be easily 6 digits now days. Most countries have SMS short code length between
+ // 3 to 6 digits. The exceptions are
+ //
+ // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
+ // followed by an additional four or six digits and two.
+ // Czechia: Codes are seven digits in length for MO and five (not billed) or
+ // eight (billed) for MT direction
+ //
+ // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
+ //
+ // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match
+ // to 7.
+ static final int MIN_MATCH = 7;
+
+ /**
+ * Checks a given number against the list of
+ * emergency numbers provided by the RIL and SIM card.
+ *
+ * @param number the number to look up.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, otherwise return false.
+ */
+ public static boolean isEmergencyNumber(String number) {
+ return isEmergencyNumber(getDefaultVoiceSubId(), number);
+ }
+
+ /**
+ * Checks a given number against the list of
+ * emergency numbers provided by the RIL and SIM card.
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, otherwise return false.
+ * @hide
+ */
+ public static boolean isEmergencyNumber(int subId, String number) {
+ // Return true only if the specified number *exactly* matches
+ // one of the emergency numbers listed by the RIL / SIM.
+ return isEmergencyNumberInternal(subId, number, true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if given number might *potentially* result in
+ * a call to an emergency service on the current network.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number according to the list managed by the RIL or
+ * SIM, *or* if the specified number simply starts with the same
+ * digits as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, *or* if the number starts with the
+ * same digits as any of those emergency numbers.
+ *
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(String number) {
+ return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
+ }
+
+ /**
+ * Checks if given number might *potentially* result in
+ * a call to an emergency service on the current network.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number according to the list managed by the RIL or
+ * SIM, *or* if the specified number simply starts with the same
+ * digits as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / SIM, *or* if the number starts with the
+ * same digits as any of those emergency numbers.
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(int subId, String number) {
+ // Check against the emergency numbers listed by the RIL / SIM,
+ // and *don't* require an exact match.
+ return isEmergencyNumberInternal(subId, number, false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String) and
+ * isPotentialEmergencyNumber(String).
+ *
+ * @param number the number to look up.
+ *
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ * (Setting useExactMatch to false allows you to identify
+ * number that could *potentially* result in emergency calls
+ * since many networks will actually ignore trailing digits
+ * after a valid emergency number.)
+ *
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / sim, otherwise return false.
+ */
+ private static boolean isEmergencyNumberInternal(String number, boolean useExactMatch) {
+ return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, useExactMatch);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String) and
+ * isPotentialEmergencyNumber(String).
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ *
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ * (Setting useExactMatch to false allows you to identify
+ * number that could *potentially* result in emergency calls
+ * since many networks will actually ignore trailing digits
+ * after a valid emergency number.)
+ *
+ * @return true if the number is in the list of emergency numbers
+ * listed in the RIL / sim, otherwise return false.
+ */
+ private static boolean isEmergencyNumberInternal(int subId, String number,
+ boolean useExactMatch) {
+ return isEmergencyNumberInternal(subId, number, null, useExactMatch);
+ }
+
+ /**
+ * Checks if a given number is an emergency number for a specific country.
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @return if the number is an emergency number for the specific country, then return true,
+ * otherwise false
+ *
+ * @hide
+ */
+ public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
+ return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
+ }
+
+ /**
+ * Checks if a given number is an emergency number for a specific country.
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @return if the number is an emergency number for the specific country, then return true,
+ * otherwise false
+ * @hide
+ */
+ public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
+ return isEmergencyNumberInternal(subId, number,
+ defaultCountryIso,
+ true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for a specific country.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the specified country, *or* if the number
+ * simply starts with the same digits as any emergency number for that
+ * country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @return true if the number is an emergency number for the specific
+ * country, *or* if the number starts with the same digits as
+ * any of those emergency numbers.
+ *
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
+ return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for a specific country.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the specified country, *or* if the number
+ * simply starts with the same digits as any emergency number for that
+ * country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @return true if the number is an emergency number for the specific
+ * country, *or* if the number starts with the same digits as
+ * any of those emergency numbers.
+ * @hide
+ */
+ public static boolean isPotentialEmergencyNumber(int subId, String number,
+ String defaultCountryIso) {
+ return isEmergencyNumberInternal(subId, number,
+ defaultCountryIso,
+ false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String, String) and
+ * isPotentialEmergencyNumber(String, String).
+ *
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the number is an emergency number for the specified country.
+ */
+ private static boolean isEmergencyNumberInternal(String number,
+ String defaultCountryIso,
+ boolean useExactMatch) {
+ return isEmergencyNumberInternal(getDefaultVoiceSubId(), number, defaultCountryIso,
+ useExactMatch);
+ }
+
+ /**
+ * Helper function for isEmergencyNumber(String, String) and
+ * isPotentialEmergencyNumber(String, String).
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @param defaultCountryIso the specific country which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the number is an emergency number for the specified country.
+ * @hide
+ */
+ private static boolean isEmergencyNumberInternal(int subId, String number,
+ String defaultCountryIso,
+ boolean useExactMatch) {
+ // If the number passed in is null, just return false:
+ if (number == null) return false;
+
+ // If the number passed in is a SIP address, return false, since the
+ // concept of "emergency numbers" is only meaningful for calls placed
+ // over the cell network.
+ // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
+ // since the whole point of extractNetworkPortionAlt() is to filter out
+ // any non-dialable characters (which would turn 'abc911def@example.com'
+ // into '911', for example.))
+ if (isUriNumber(number)) {
+ return false;
+ }
+
+ // Strip the separators from the number before comparing it
+ // to the list.
+ number = extractNetworkPortionAlt(number);
+
+ String emergencyNumbers = "";
+ int slotId = SubscriptionManager.getSlotIndex(subId);
+
+ // retrieve the list of emergency numbers
+ // check read-write ecclist property first
+ String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
+
+ emergencyNumbers = SystemProperties.get(ecclist, "");
+
+ Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
+ + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers);
+
+ if (TextUtils.isEmpty(emergencyNumbers)) {
+ // then read-only ecclist property since old RIL only uses this
+ emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+ }
+
+ if (!TextUtils.isEmpty(emergencyNumbers)) {
+ // searches through the comma-separated list for a match,
+ // return true if one is found.
+ for (String emergencyNum : emergencyNumbers.split(",")) {
+ // It is not possible to append additional digits to an emergency number to dial
+ // the number in Brazil - it won't connect.
+ if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
+ }
+ }
+ // no matches found against the list!
+ return false;
+ }
+
+ Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
+ + " Use embedded logic for determining ones.");
+
+ // If slot id is invalid, means that there is no sim card.
+ // According spec 3GPP TS22.101, the following numbers should be
+ // ECC numbers when SIM/USIM is not present.
+ emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
+
+ for (String emergencyNum : emergencyNumbers.split(",")) {
+ if (useExactMatch) {
+ if (number.equals(emergencyNum)) {
+ return true;
+ }
+ } else {
+ if (number.startsWith(emergencyNum)) {
+ return true;
+ }
+ }
+ }
+
+ // No ecclist system property, so use our own list.
+ if (defaultCountryIso != null) {
+ ShortNumberInfo info = ShortNumberInfo.getInstance();
+ if (useExactMatch) {
+ return info.isEmergencyNumber(number, defaultCountryIso);
+ } else {
+ return info.connectsToEmergencyNumber(number, defaultCountryIso);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if a given number is an emergency number for the country that the user is in.
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @return true if the specified number is an emergency number for the country the user
+ * is currently in.
+ */
+ public static boolean isLocalEmergencyNumber(Context context, String number) {
+ return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
+ }
+
+ /**
+ * Checks if a given number is an emergency number for the country that the user is in.
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @return true if the specified number is an emergency number for the country the user
+ * is currently in.
+ * @hide
+ */
+ public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
+ return isLocalEmergencyNumberInternal(subId, number,
+ context,
+ true /* useExactMatch */);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for the country that the user is in. The current
+ * country is determined using the CountryDetector.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the current country, *or* if the number
+ * simply starts with the same digits as any emergency number for the
+ * current country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @return true if the specified number is an emergency number for a local country, based on the
+ * CountryDetector.
+ *
+ * @see android.location.CountryDetector
+ * @hide
+ */
+ public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
+ return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
+ }
+
+ /**
+ * Checks if a given number might *potentially* result in a call to an
+ * emergency service, for the country that the user is in. The current
+ * country is determined using the CountryDetector.
+ *
+ * Specifically, this method will return true if the specified number
+ * is an emergency number in the current country, *or* if the number
+ * simply starts with the same digits as any emergency number for the
+ * current country.
+ *
+ * This method is intended for internal use by the phone app when
+ * deciding whether to allow ACTION_CALL intents from 3rd party apps
+ * (where we're required to *not* allow emergency calls to be placed.)
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @return true if the specified number is an emergency number for a local country, based on the
+ * CountryDetector.
+ *
+ * @hide
+ */
+ public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
+ String number) {
+ return isLocalEmergencyNumberInternal(subId, number,
+ context,
+ false /* useExactMatch */);
+ }
+
+ /**
+ * Helper function for isLocalEmergencyNumber() and
+ * isPotentialLocalEmergencyNumber().
+ *
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the specified number is an emergency number for a
+ * local country, based on the CountryDetector.
+ *
+ * @see android.location.CountryDetector
+ * @hide
+ */
+ private static boolean isLocalEmergencyNumberInternal(String number,
+ Context context,
+ boolean useExactMatch) {
+ return isLocalEmergencyNumberInternal(getDefaultVoiceSubId(), number, context,
+ useExactMatch);
+ }
+
+ /**
+ * Helper function for isLocalEmergencyNumber() and
+ * isPotentialLocalEmergencyNumber().
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @param context the specific context which the number should be checked against
+ * @param useExactMatch if true, consider a number to be an emergency
+ * number only if it *exactly* matches a number listed in
+ * the RIL / SIM. If false, a number is considered to be an
+ * emergency number if it simply starts with the same digits
+ * as any of the emergency numbers listed in the RIL / SIM.
+ *
+ * @return true if the specified number is an emergency number for a
+ * local country, based on the CountryDetector.
+ * @hide
+ */
+ private static boolean isLocalEmergencyNumberInternal(int subId, String number,
+ Context context,
+ boolean useExactMatch) {
+ String countryIso;
+ CountryDetector detector = (CountryDetector) context.getSystemService(
+ Context.COUNTRY_DETECTOR);
+ if (detector != null && detector.detectCountry() != null) {
+ countryIso = detector.detectCountry().getCountryIso();
+ } else {
+ Locale locale = context.getResources().getConfiguration().locale;
+ countryIso = locale.getCountry();
+ Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
+ + countryIso);
+ }
+ return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
+ }
+
+ /**
+ * isVoiceMailNumber: checks a given number against the voicemail
+ * number provided by the RIL and SIM card. The caller must have
+ * the READ_PHONE_STATE credential.
+ *
+ * @param number the number to look up.
+ * @return true if the number is in the list of voicemail. False
+ * otherwise, including if the caller does not have the permission
+ * to read the VM number.
+ */
+ public static boolean isVoiceMailNumber(String number) {
+ return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
+ }
+
+ /**
+ * isVoiceMailNumber: checks a given number against the voicemail
+ * number provided by the RIL and SIM card. The caller must have
+ * the READ_PHONE_STATE credential.
+ *
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @return true if the number is in the list of voicemail. False
+ * otherwise, including if the caller does not have the permission
+ * to read the VM number.
+ * @hide
+ */
+ public static boolean isVoiceMailNumber(int subId, String number) {
+ return isVoiceMailNumber(null, subId, number);
+ }
+
+ /**
+ * isVoiceMailNumber: checks a given number against the voicemail
+ * number provided by the RIL and SIM card. The caller must have
+ * the READ_PHONE_STATE credential.
+ *
+ * @param context {@link Context}.
+ * @param subId the subscription id of the SIM.
+ * @param number the number to look up.
+ * @return true if the number is in the list of voicemail. False
+ * otherwise, including if the caller does not have the permission
+ * to read the VM number.
+ * @hide
+ */
+ public static boolean isVoiceMailNumber(Context context, int subId, String number) {
+ String vmNumber, mdn;
+ try {
+ final TelephonyManager tm;
+ if (context == null) {
+ tm = TelephonyManager.getDefault();
+ if (DBG) log("isVoiceMailNumber: default tm");
+ } else {
+ tm = TelephonyManager.from(context);
+ if (DBG) log("isVoiceMailNumber: tm from context");
+ }
+ vmNumber = tm.getVoiceMailNumber(subId);
+ mdn = tm.getLine1Number(subId);
+ if (DBG) log("isVoiceMailNumber: mdn=" + mdn + ", vmNumber=" + vmNumber
+ + ", number=" + number);
+ } catch (SecurityException ex) {
+ if (DBG) log("isVoiceMailNumber: SecurityExcpetion caught");
+ return false;
+ }
+ // Strip the separators from the number before comparing it
+ // to the list.
+ number = extractNetworkPortionAlt(number);
+ if (TextUtils.isEmpty(number)) {
+ if (DBG) log("isVoiceMailNumber: number is empty after stripping");
+ return false;
+ }
+
+ // check if the carrier considers MDN to be an additional voicemail number
+ boolean compareWithMdn = false;
+ if (context != null) {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfigForSubId(subId);
+ if (b != null) {
+ compareWithMdn = b.getBoolean(CarrierConfigManager.
+ KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL);
+ if (DBG) log("isVoiceMailNumber: compareWithMdn=" + compareWithMdn);
+ }
+ }
+ }
+
+ if (compareWithMdn) {
+ if (DBG) log("isVoiceMailNumber: treating mdn as additional vm number");
+ return compare(number, vmNumber) || compare(number, mdn);
+ } else {
+ if (DBG) log("isVoiceMailNumber: returning regular compare");
+ return compare(number, vmNumber);
+ }
+ }
+
+ /**
+ * Translates any alphabetic letters (i.e. [A-Za-z]) in the
+ * specified phone number into the equivalent numeric digits,
+ * according to the phone keypad letter mapping described in
+ * ITU E.161 and ISO/IEC 9995-8.
+ *
+ * @return the input string, with alpha letters converted to numeric
+ * digits using the phone keypad letter mapping. For example,
+ * an input of "1-800-GOOG-411" will return "1-800-4664-411".
+ */
+ public static String convertKeypadLettersToDigits(String input) {
+ if (input == null) {
+ return input;
+ }
+ int len = input.length();
+ if (len == 0) {
+ return input;
+ }
+
+ char[] out = input.toCharArray();
+
+ for (int i = 0; i < len; i++) {
+ char c = out[i];
+ // If this char isn't in KEYPAD_MAP at all, just leave it alone.
+ out[i] = (char) KEYPAD_MAP.get(c, c);
+ }
+
+ return new String(out);
+ }
+
+ /**
+ * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
+ * TODO: This should come from a resource.
+ */
+ private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
+ static {
+ KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
+ KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
+
+ KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
+ KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
+
+ KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
+ KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
+
+ KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
+ KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
+
+ KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
+ KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
+
+ KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
+ KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
+
+ KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
+ KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
+
+ KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
+ KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
+ }
+
+ //================ Plus Code formatting =========================
+ private static final char PLUS_SIGN_CHAR = '+';
+ private static final String PLUS_SIGN_STRING = "+";
+ private static final String NANP_IDP_STRING = "011";
+ private static final int NANP_LENGTH = 10;
+
+ /**
+ * This function checks if there is a plus sign (+) in the passed-in dialing number.
+ * If there is, it processes the plus sign based on the default telephone
+ * numbering plan of the system when the phone is activated and the current
+ * telephone numbering plan of the system that the phone is camped on.
+ * Currently, we only support the case that the default and current telephone
+ * numbering plans are North American Numbering Plan(NANP).
+ *
+ * The passed-in dialStr should only contain the valid format as described below,
+ * 1) the 1st character in the dialStr should be one of the really dialable
+ * characters listed below
+ * ISO-LATIN characters 0-9, *, # , +
+ * 2) the dialStr should already strip out the separator characters,
+ * every character in the dialStr should be one of the non separator characters
+ * listed below
+ * ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
+ *
+ * Otherwise, this function returns the dial string passed in
+ *
+ * @param dialStr the original dial string
+ * @return the converted dial string if the current/default countries belong to NANP,
+ * and if there is the "+" in the original dial string. Otherwise, the original dial
+ * string returns.
+ *
+ * This API is for CDMA only
+ *
+ * @hide TODO: pending API Council approval
+ */
+ public static String cdmaCheckAndProcessPlusCode(String dialStr) {
+ if (!TextUtils.isEmpty(dialStr)) {
+ if (isReallyDialable(dialStr.charAt(0)) &&
+ isNonSeparator(dialStr)) {
+ String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
+ String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
+ if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
+ return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
+ getFormatTypeFromCountryCode(currIso),
+ getFormatTypeFromCountryCode(defaultIso));
+ }
+ }
+ }
+ return dialStr;
+ }
+
+ /**
+ * Process phone number for CDMA, converting plus code using the home network number format.
+ * This is used for outgoing SMS messages.
+ *
+ * @param dialStr the original dial string
+ * @return the converted dial string
+ * @hide for internal use
+ */
+ public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
+ if (!TextUtils.isEmpty(dialStr)) {
+ if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
+ String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
+ if (!TextUtils.isEmpty(defaultIso)) {
+ int format = getFormatTypeFromCountryCode(defaultIso);
+ return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
+ }
+ }
+ }
+ return dialStr;
+ }
+
+ /**
+ * This function should be called from checkAndProcessPlusCode only
+ * And it is used for test purpose also.
+ *
+ * It checks the dial string by looping through the network portion,
+ * post dial portion 1, post dial porting 2, etc. If there is any
+ * plus sign, then process the plus sign.
+ * Currently, this function supports the plus sign conversion within NANP only.
+ * Specifically, it handles the plus sign in the following ways:
+ * 1)+1NANP,remove +, e.g.
+ * +18475797000 is converted to 18475797000,
+ * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
+ * +8475797000 is converted to 0118475797000,
+ * +11875767800 is converted to 01111875767800
+ * 3)+1NANP in post dial string(s), e.g.
+ * 8475797000;+18475231753 is converted to 8475797000;18475231753
+ *
+ *
+ * @param dialStr the original dial string
+ * @param currFormat the numbering system of the current country that the phone is camped on
+ * @param defaultFormat the numbering system of the country that the phone is activated on
+ * @return the converted dial string if the current/default countries belong to NANP,
+ * and if there is the "+" in the original dial string. Otherwise, the original dial
+ * string returns.
+ *
+ * @hide
+ */
+ public static String
+ cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
+ String retStr = dialStr;
+
+ boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
+
+ // Checks if the plus sign character is in the passed-in dial string
+ if (dialStr != null &&
+ dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
+
+ // Handle case where default and current telephone numbering plans are NANP.
+ String postDialStr = null;
+ String tempDialStr = dialStr;
+
+ // Sets the retStr to null since the conversion will be performed below.
+ retStr = null;
+ if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
+ // This routine is to process the plus sign in the dial string by loop through
+ // the network portion, post dial portion 1, post dial portion 2... etc. if
+ // applied
+ do {
+ String networkDialStr;
+ // Format the string based on the rules for the country the number is from,
+ // and the current country the phone is camped
+ if (useNanp) {
+ networkDialStr = extractNetworkPortion(tempDialStr);
+ } else {
+ networkDialStr = extractNetworkPortionAlt(tempDialStr);
+
+ }
+
+ networkDialStr = processPlusCode(networkDialStr, useNanp);
+
+ // Concatenates the string that is converted from network portion
+ if (!TextUtils.isEmpty(networkDialStr)) {
+ if (retStr == null) {
+ retStr = networkDialStr;
+ } else {
+ retStr = retStr.concat(networkDialStr);
+ }
+ } else {
+ // This should never happen since we checked the if dialStr is null
+ // and if it contains the plus sign in the beginning of this function.
+ // The plus sign is part of the network portion.
+ Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
+ return dialStr;
+ }
+ postDialStr = extractPostDialPortion(tempDialStr);
+ if (!TextUtils.isEmpty(postDialStr)) {
+ int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
+
+ // dialableIndex should always be greater than 0
+ if (dialableIndex >= 1) {
+ retStr = appendPwCharBackToOrigDialStr(dialableIndex,
+ retStr,postDialStr);
+ // Skips the P/W character, extracts the dialable portion
+ tempDialStr = postDialStr.substring(dialableIndex);
+ } else {
+ // Non-dialable character such as P/W should not be at the end of
+ // the dial string after P/W processing in GsmCdmaConnection.java
+ // Set the postDialStr to "" to break out of the loop
+ if (dialableIndex < 0) {
+ postDialStr = "";
+ }
+ Rlog.e("wrong postDialStr=", postDialStr);
+ }
+ }
+ if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
+ } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
+ }
+ return retStr;
+ }
+
+ /**
+ * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
+ * containing a phone number in its entirety.
+ *
+ * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
+ * @return A {@code CharSequence} with appropriate annotations.
+ */
+ public static CharSequence createTtsSpannable(CharSequence phoneNumber) {
+ if (phoneNumber == null) {
+ return null;
+ }
+ Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
+ PhoneNumberUtils.addTtsSpan(spannable, 0, spannable.length());
+ return spannable;
+ }
+
+ /**
+ * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
+ * annotating that location as containing a phone number.
+ *
+ * @param s A {@code Spannable} to annotate.
+ * @param start The starting character position of the phone number in {@code s}.
+ * @param endExclusive The position after the ending character in the phone number {@code s}.
+ */
+ public static void addTtsSpan(Spannable s, int start, int endExclusive) {
+ s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()),
+ start,
+ endExclusive,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ /**
+ * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
+ * containing a phone number in its entirety.
+ *
+ * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
+ * @return A {@code CharSequence} with appropriate annotations.
+ * @deprecated Renamed {@link #createTtsSpannable}.
+ *
+ * @hide
+ */
+ @Deprecated
+ public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
+ return createTtsSpannable(phoneNumber);
+ }
+
+ /**
+ * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
+ * annotating that location as containing a phone number.
+ *
+ * @param s A {@code Spannable} to annotate.
+ * @param start The starting character position of the phone number in {@code s}.
+ * @param end The ending character position of the phone number in {@code s}.
+ *
+ * @deprecated Renamed {@link #addTtsSpan}.
+ *
+ * @hide
+ */
+ @Deprecated
+ public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
+ addTtsSpan(s, start, end);
+ }
+
+ /**
+ * Create a {@code TtsSpan} for the supplied {@code String}.
+ *
+ * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
+ * @return A {@code TtsSpan} for {@param phoneNumberString}.
+ */
+ public static TtsSpan createTtsSpan(String phoneNumberString) {
+ if (phoneNumberString == null) {
+ return null;
+ }
+
+ // Parse the phone number
+ final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+ PhoneNumber phoneNumber = null;
+ try {
+ // Don't supply a defaultRegion so this fails for non-international numbers because
+ // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
+ // present
+ phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
+ } catch (NumberParseException ignored) {
+ }
+
+ // Build a telephone tts span
+ final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
+ if (phoneNumber == null) {
+ // Strip separators otherwise TalkBack will be silent
+ // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
+ builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
+ } else {
+ if (phoneNumber.hasCountryCode()) {
+ builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
+ }
+ builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
+ }
+ return builder.build();
+ }
+
+ // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
+ // a digit or the characters * and #, to produce a result like "20 123 456#".
+ private static String splitAtNonNumerics(CharSequence number) {
+ StringBuilder sb = new StringBuilder(number.length());
+ for (int i = 0; i < number.length(); i++) {
+ sb.append(PhoneNumberUtils.is12Key(number.charAt(i))
+ ? number.charAt(i)
+ : " ");
+ }
+ // It is very important to remove extra spaces. At time of writing, any leading or trailing
+ // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS
+ // span to be non-functional!
+ return sb.toString().replaceAll(" +", " ").trim();
+ }
+
+ private static String getCurrentIdp(boolean useNanp) {
+ String ps = null;
+ if (useNanp) {
+ ps = NANP_IDP_STRING;
+ } else {
+ // in case, there is no IDD is found, we shouldn't convert it.
+ ps = SystemProperties.get(PROPERTY_OPERATOR_IDP_STRING, PLUS_SIGN_STRING);
+ }
+ return ps;
+ }
+
+ private static boolean isTwoToNine (char c) {
+ if (c >= '2' && c <= '9') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static int getFormatTypeFromCountryCode (String country) {
+ // Check for the NANP countries
+ int length = NANP_COUNTRIES.length;
+ for (int i = 0; i < length; i++) {
+ if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
+ return FORMAT_NANP;
+ }
+ }
+ if ("jp".compareToIgnoreCase(country) == 0) {
+ return FORMAT_JAPAN;
+ }
+ return FORMAT_UNKNOWN;
+ }
+
+ /**
+ * This function checks if the passed in string conforms to the NANP format
+ * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
+ * @hide
+ */
+ public static boolean isNanp (String dialStr) {
+ boolean retVal = false;
+ if (dialStr != null) {
+ if (dialStr.length() == NANP_LENGTH) {
+ if (isTwoToNine(dialStr.charAt(0)) &&
+ isTwoToNine(dialStr.charAt(3))) {
+ retVal = true;
+ for (int i=1; i<NANP_LENGTH; i++ ) {
+ char c=dialStr.charAt(i);
+ if (!PhoneNumberUtils.isISODigit(c)) {
+ retVal = false;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ Rlog.e("isNanp: null dialStr passed in", dialStr);
+ }
+ return retVal;
+ }
+
+ /**
+ * This function checks if the passed in string conforms to 1-NANP format
+ */
+ private static boolean isOneNanp(String dialStr) {
+ boolean retVal = false;
+ if (dialStr != null) {
+ String newDialStr = dialStr.substring(1);
+ if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
+ retVal = true;
+ }
+ } else {
+ Rlog.e("isOneNanp: null dialStr passed in", dialStr);
+ }
+ return retVal;
+ }
+
+ /**
+ * Determines if the specified number is actually a URI
+ * (i.e. a SIP address) rather than a regular PSTN phone number,
+ * based on whether or not the number contains an "@" character.
+ *
+ * @hide
+ * @param number
+ * @return true if number contains @
+ */
+ public static boolean isUriNumber(String number) {
+ // Note we allow either "@" or "%40" to indicate a URI, in case
+ // the passed-in string is URI-escaped. (Neither "@" nor "%40"
+ // will ever be found in a legal PSTN number.)
+ return number != null && (number.contains("@") || number.contains("%40"));
+ }
+
+ /**
+ * @return the "username" part of the specified SIP address,
+ * i.e. the part before the "@" character (or "%40").
+ *
+ * @param number SIP address of the form "username@domainname"
+ * (or the URI-escaped equivalent "username%40domainname")
+ * @see #isUriNumber
+ *
+ * @hide
+ */
+ public static String getUsernameFromUriNumber(String number) {
+ // The delimiter between username and domain name can be
+ // either "@" or "%40" (the URI-escaped equivalent.)
+ int delimiterIndex = number.indexOf('@');
+ if (delimiterIndex < 0) {
+ delimiterIndex = number.indexOf("%40");
+ }
+ if (delimiterIndex < 0) {
+ Rlog.w(LOG_TAG,
+ "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
+ delimiterIndex = number.length();
+ }
+ return number.substring(0, delimiterIndex);
+ }
+
+ /**
+ * Given a {@link Uri} with a {@code sip} scheme, attempts to build an equivalent {@code tel}
+ * scheme {@link Uri}. If the source {@link Uri} does not contain a valid number, or is not
+ * using the {@code sip} scheme, the original {@link Uri} is returned.
+ *
+ * @param source The {@link Uri} to convert.
+ * @return The equivalent {@code tel} scheme {@link Uri}.
+ *
+ * @hide
+ */
+ public static Uri convertSipUriToTelUri(Uri source) {
+ // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+ // Per RFC3261, the "user" can be a telephone number.
+ // For example: sip:1650555121;phone-context=blah.com@host.com
+ // In this case, the phone number is in the user field of the URI, and the parameters can be
+ // ignored.
+ //
+ // A SIP URI can also specify a phone number in a format similar to:
+ // sip:+1-212-555-1212@something.com;user=phone
+ // In this case, the phone number is again in user field and the parameters can be ignored.
+ // We can get the user field in these instances by splitting the string on the @, ;, or :
+ // and looking at the first found item.
+
+ String scheme = source.getScheme();
+
+ if (!PhoneAccount.SCHEME_SIP.equals(scheme)) {
+ // Not a sip URI, bail.
+ return source;
+ }
+
+ String number = source.getSchemeSpecificPart();
+ String numberParts[] = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ // Number not found, bail.
+ return source;
+ }
+ number = numberParts[0];
+
+ return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
+ }
+
+ /**
+ * This function handles the plus code conversion
+ * If the number format is
+ * 1)+1NANP,remove +,
+ * 2)other than +1NANP, any + numbers,replace + with the current IDP
+ */
+ private static String processPlusCode(String networkDialStr, boolean useNanp) {
+ String retStr = networkDialStr;
+
+ if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
+ + "for NANP = " + useNanp);
+ // If there is a plus sign at the beginning of the dial string,
+ // Convert the plus sign to the default IDP since it's an international number
+ if (networkDialStr != null &&
+ networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
+ networkDialStr.length() > 1) {
+ String newStr = networkDialStr.substring(1);
+ // TODO: for nonNanp, should the '+' be removed if following number is country code
+ if (useNanp && isOneNanp(newStr)) {
+ // Remove the leading plus sign
+ retStr = newStr;
+ } else {
+ // Replaces the plus sign with the default IDP
+ retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
+ }
+ }
+ if (DBG) log("processPlusCode, retStr=" + retStr);
+ return retStr;
+ }
+
+ // This function finds the index of the dialable character(s)
+ // in the post dial string
+ private static int findDialableIndexFromPostDialStr(String postDialStr) {
+ for (int index = 0;index < postDialStr.length();index++) {
+ char c = postDialStr.charAt(index);
+ if (isReallyDialable(c)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ // This function appends the non-dialable P/W character to the original
+ // dial string based on the dialable index passed in
+ private static String
+ appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
+ String retStr;
+
+ // There is only 1 P/W character before the dialable characters
+ if (dialableIndex == 1) {
+ StringBuilder ret = new StringBuilder(origStr);
+ ret = ret.append(dialStr.charAt(0));
+ retStr = ret.toString();
+ } else {
+ // It means more than 1 P/W characters in the post dial string,
+ // appends to retStr
+ String nonDigitStr = dialStr.substring(0,dialableIndex);
+ retStr = origStr.concat(nonDigitStr);
+ }
+ return retStr;
+ }
+
+ //===== Beginning of utility methods used in compareLoosely() =====
+
+ /**
+ * Phone numbers are stored in "lookup" form in the database
+ * as reversed strings to allow for caller ID lookup
+ *
+ * This method takes a phone number and makes a valid SQL "LIKE"
+ * string that will match the lookup form
+ *
+ */
+ /** all of a up to len must be an international prefix or
+ * separators/non-dialing digits
+ */
+ private static boolean
+ matchIntlPrefix(String a, int len) {
+ /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
+ /* 0 1 2 3 45 */
+
+ int state = 0;
+ for (int i = 0 ; i < len ; i++) {
+ char c = a.charAt(i);
+
+ switch (state) {
+ case 0:
+ if (c == '+') state = 1;
+ else if (c == '0') state = 2;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ case 2:
+ if (c == '0') state = 3;
+ else if (c == '1') state = 4;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ case 4:
+ if (c == '1') state = 5;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ default:
+ if (isNonSeparator(c)) return false;
+ break;
+
+ }
+ }
+
+ return state == 1 || state == 3 || state == 5;
+ }
+
+ /** all of 'a' up to len must be a (+|00|011)country code)
+ * We're fast and loose with the country code. Any \d{1,3} matches */
+ private static boolean
+ matchIntlPrefixAndCC(String a, int len) {
+ /* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
+ /* 0 1 2 3 45 6 7 8 */
+
+ int state = 0;
+ for (int i = 0 ; i < len ; i++ ) {
+ char c = a.charAt(i);
+
+ switch (state) {
+ case 0:
+ if (c == '+') state = 1;
+ else if (c == '0') state = 2;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ case 2:
+ if (c == '0') state = 3;
+ else if (c == '1') state = 4;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ case 4:
+ if (c == '1') state = 5;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ case 1:
+ case 3:
+ case 5:
+ if (isISODigit(c)) state = 6;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ case 6:
+ case 7:
+ if (isISODigit(c)) state++;
+ else if (isNonSeparator(c)) return false;
+ break;
+
+ default:
+ if (isNonSeparator(c)) return false;
+ }
+ }
+
+ return state == 6 || state == 7 || state == 8;
+ }
+
+ /** all of 'a' up to len must match non-US trunk prefix ('0') */
+ private static boolean
+ matchTrunkPrefix(String a, int len) {
+ boolean found;
+
+ found = false;
+
+ for (int i = 0 ; i < len ; i++) {
+ char c = a.charAt(i);
+
+ if (c == '0' && !found) {
+ found = true;
+ } else if (isNonSeparator(c)) {
+ return false;
+ }
+ }
+
+ return found;
+ }
+
+ //===== End of utility methods used only in compareLoosely() =====
+
+ //===== Beginning of utility methods used only in compareStrictly() ====
+
+ /*
+ * If true, the number is country calling code.
+ */
+ private static final boolean COUNTRY_CALLING_CALL[] = {
+ true, true, false, false, false, false, false, true, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ true, false, false, false, false, false, false, true, true, false,
+ true, true, true, true, true, false, true, false, false, true,
+ true, false, false, true, true, true, true, true, true, true,
+ false, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, true, true, true, true, false, true, false, false, true,
+ true, true, true, true, true, true, false, false, true, false,
+ };
+ private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
+
+ /**
+ * @return true when input is valid Country Calling Code.
+ */
+ private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
+ return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
+ COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
+ }
+
+ /**
+ * Returns integer corresponding to the input if input "ch" is
+ * ISO-LATIN characters 0-9.
+ * Returns -1 otherwise
+ */
+ private static int tryGetISODigit(char ch) {
+ if ('0' <= ch && ch <= '9') {
+ return ch - '0';
+ } else {
+ return -1;
+ }
+ }
+
+ private static class CountryCallingCodeAndNewIndex {
+ public final int countryCallingCode;
+ public final int newIndex;
+ public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
+ this.countryCallingCode = countryCode;
+ this.newIndex = newIndex;
+ }
+ }
+
+ /*
+ * Note that this function does not strictly care the country calling code with
+ * 3 length (like Morocco: +212), assuming it is enough to use the first two
+ * digit to compare two phone numbers.
+ */
+ private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
+ String str, boolean acceptThailandCase) {
+ // Rough regexp:
+ // ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
+ // 0 1 2 3 45 6 7 89
+ //
+ // In all the states, this function ignores separator characters.
+ // "166" is the special case for the call from Thailand to the US. Uguu!
+ int state = 0;
+ int ccc = 0;
+ final int length = str.length();
+ for (int i = 0 ; i < length ; i++ ) {
+ char ch = str.charAt(i);
+ switch (state) {
+ case 0:
+ if (ch == '+') state = 1;
+ else if (ch == '0') state = 2;
+ else if (ch == '1') {
+ if (acceptThailandCase) {
+ state = 8;
+ } else {
+ return null;
+ }
+ } else if (isDialable(ch)) {
+ return null;
+ }
+ break;
+
+ case 2:
+ if (ch == '0') state = 3;
+ else if (ch == '1') state = 4;
+ else if (isDialable(ch)) {
+ return null;
+ }
+ break;
+
+ case 4:
+ if (ch == '1') state = 5;
+ else if (isDialable(ch)) {
+ return null;
+ }
+ break;
+
+ case 1:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ {
+ int ret = tryGetISODigit(ch);
+ if (ret > 0) {
+ ccc = ccc * 10 + ret;
+ if (ccc >= 100 || isCountryCallingCode(ccc)) {
+ return new CountryCallingCodeAndNewIndex(ccc, i + 1);
+ }
+ if (state == 1 || state == 3 || state == 5) {
+ state = 6;
+ } else {
+ state++;
+ }
+ } else if (isDialable(ch)) {
+ return null;
+ }
+ }
+ break;
+ case 8:
+ if (ch == '6') state = 9;
+ else if (isDialable(ch)) {
+ return null;
+ }
+ break;
+ case 9:
+ if (ch == '6') {
+ return new CountryCallingCodeAndNewIndex(66, i + 1);
+ } else {
+ return null;
+ }
+ default:
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Currently this function simply ignore the first digit assuming it is
+ * trunk prefix. Actually trunk prefix is different in each country.
+ *
+ * e.g.
+ * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
+ * "+33123456789" equals "0123456789" (French trunk digit is 0)
+ *
+ */
+ private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
+ int length = str.length();
+ for (int i = currentIndex ; i < length ; i++) {
+ final char ch = str.charAt(i);
+ if (tryGetISODigit(ch) >= 0) {
+ return i + 1;
+ } else if (isDialable(ch)) {
+ return -1;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
+ * that "str" has only one digit and separator characters. The one digit is
+ * assumed to be trunk prefix.
+ */
+ private static boolean checkPrefixIsIgnorable(final String str,
+ int forwardIndex, int backwardIndex) {
+ boolean trunk_prefix_was_read = false;
+ while (backwardIndex >= forwardIndex) {
+ if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
+ if (trunk_prefix_was_read) {
+ // More than one digit appeared, meaning that "a" and "b"
+ // is different.
+ return false;
+ } else {
+ // Ignore just one digit, assuming it is trunk prefix.
+ trunk_prefix_was_read = true;
+ }
+ } else if (isDialable(str.charAt(backwardIndex))) {
+ // Trunk prefix is a digit, not "*", "#"...
+ return false;
+ }
+ backwardIndex--;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns Default voice subscription Id.
+ */
+ private static int getDefaultVoiceSubId() {
+ return SubscriptionManager.getDefaultVoiceSubscriptionId();
+ }
+ //==== End of utility methods used only in compareStrictly() =====
+
+
+ /*
+ * The config held calling number conversion map, expected to convert to emergency number.
+ */
+ private static String[] sConvertToEmergencyMap = null;
+
+ /**
+ * Converts to emergency number based on the conversion map.
+ * The conversion map is declared as config_convert_to_emergency_number_map.
+ *
+ * @param context a context to use for accessing resources
+ * @return The converted emergency number if the number matches conversion map,
+ * otherwise original number.
+ *
+ * @hide
+ */
+ public static String convertToEmergencyNumber(Context context, String number) {
+ if (context == null || TextUtils.isEmpty(number)) {
+ return number;
+ }
+
+ String normalizedNumber = normalizeNumber(number);
+
+ // The number is already emergency number. Skip conversion.
+ if (isEmergencyNumber(normalizedNumber)) {
+ return number;
+ }
+
+ if (sConvertToEmergencyMap == null) {
+ sConvertToEmergencyMap = context.getResources().getStringArray(
+ com.android.internal.R.array.config_convert_to_emergency_number_map);
+ }
+
+ // The conversion map is not defined (this is default). Skip conversion.
+ if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) {
+ return number;
+ }
+
+ for (String convertMap : sConvertToEmergencyMap) {
+ if (DBG) log("convertToEmergencyNumber: " + convertMap);
+ String[] entry = null;
+ String[] filterNumbers = null;
+ String convertedNumber = null;
+ if (!TextUtils.isEmpty(convertMap)) {
+ entry = convertMap.split(":");
+ }
+ if (entry != null && entry.length == 2) {
+ convertedNumber = entry[1];
+ if (!TextUtils.isEmpty(entry[0])) {
+ filterNumbers = entry[0].split(",");
+ }
+ }
+ // Skip if the format of entry is invalid
+ if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
+ || filterNumbers.length == 0) {
+ continue;
+ }
+
+ for (String filterNumber : filterNumbers) {
+ if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
+ + ", convertedNumber = " + convertedNumber);
+ if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
+ if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
+ + convertedNumber);
+ return convertedNumber;
+ }
+ }
+ }
+ return number;
+ }
+}
diff --git a/android/telephony/PhoneStateListener.java b/android/telephony/PhoneStateListener.java
new file mode 100644
index 00000000..afff6d54
--- /dev/null
+++ b/android/telephony/PhoneStateListener.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.telephony.IPhoneStateListener;
+
+import java.util.List;
+import java.lang.ref.WeakReference;
+
+/**
+ * A listener class for monitoring changes in specific telephony states
+ * on the device, including service state, signal strength, message
+ * waiting indicator (voicemail), and others.
+ * <p>
+ * Override the methods for the state that you wish to receive updates for, and
+ * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
+ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}.
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application won't receive updates for protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * appropriate LISTEN_ flags.
+ */
+public class PhoneStateListener {
+ private static final String LOG_TAG = "PhoneStateListener";
+ private static final boolean DBG = false; // STOPSHIP if true
+
+ /**
+ * Stop listening for updates.
+ */
+ public static final int LISTEN_NONE = 0;
+
+ /**
+ * Listen for changes to the network service state (cellular).
+ *
+ * @see #onServiceStateChanged
+ * @see ServiceState
+ */
+ public static final int LISTEN_SERVICE_STATE = 0x00000001;
+
+ /**
+ * Listen for changes to the network signal strength (cellular).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+ * READ_PHONE_STATE}
+ * <p>
+ *
+ * @see #onSignalStrengthChanged
+ *
+ * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS}
+ */
+ @Deprecated
+ public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002;
+
+ /**
+ * Listen for changes to the message-waiting indicator.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+ * READ_PHONE_STATE}
+ * <p>
+ * Example: The status bar uses this to determine when to display the
+ * voicemail icon.
+ *
+ * @see #onMessageWaitingIndicatorChanged
+ */
+ public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
+
+ /**
+ * Listen for changes to the call-forwarding indicator.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+ * READ_PHONE_STATE}
+ * @see #onCallForwardingIndicatorChanged
+ */
+ public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
+
+ /**
+ * Listen for changes to the device's cell location. Note that
+ * this will result in frequent callbacks to the listener.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION
+ * ACCESS_COARSE_LOCATION}
+ * <p>
+ * If you need regular location updates but want more control over
+ * the update interval or location precision, you can set up a listener
+ * through the {@link android.location.LocationManager location manager}
+ * instead.
+ *
+ * @see #onCellLocationChanged
+ */
+ public static final int LISTEN_CELL_LOCATION = 0x00000010;
+
+ /**
+ * Listen for changes to the device call state.
+ * {@more}
+ *
+ * @see #onCallStateChanged
+ */
+ public static final int LISTEN_CALL_STATE = 0x00000020;
+
+ /**
+ * Listen for changes to the data connection state (cellular).
+ *
+ * @see #onDataConnectionStateChanged
+ */
+ public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
+
+ /**
+ * Listen for changes to the direction of data traffic on the data
+ * connection (cellular).
+ * {@more}
+ * Example: The status bar uses this to display the appropriate
+ * data-traffic icon.
+ *
+ * @see #onDataActivity
+ */
+ public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
+
+ /**
+ * Listen for changes to the network signal strengths (cellular).
+ * <p>
+ * Example: The status bar uses this to control the signal-strength
+ * icon.
+ *
+ * @see #onSignalStrengthsChanged
+ */
+ public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
+
+ /**
+ * Listen for changes to OTASP mode.
+ *
+ * @see #onOtaspChanged
+ * @hide
+ */
+ public static final int LISTEN_OTASP_CHANGED = 0x00000200;
+
+ /**
+ * Listen for changes to observed cell info.
+ *
+ * @see #onCellInfoChanged
+ */
+ public static final int LISTEN_CELL_INFO = 0x00000400;
+
+ /**
+ * Listen for precise changes and fails to the device calls (cellular).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @hide
+ */
+ public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
+
+ /**
+ * Listen for precise changes and fails on the data connection (cellular).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @see #onPreciseDataConnectionStateChanged
+ * @hide
+ */
+ public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
+
+ /**
+ * Listen for real time info for all data connections (cellular)).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+ *
+ * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
+ * @hide
+ */
+ @Deprecated
+ public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 0x00002000;
+
+ /**
+ * Listen for changes to LTE network state
+ *
+ * @see #onLteNetworkStateChanged
+ * @hide
+ */
+ public static final int LISTEN_VOLTE_STATE = 0x00004000;
+
+ /**
+ * Listen for OEM hook raw event
+ *
+ * @see #onOemHookRawEvent
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
+
+ /**
+ * Listen for carrier network changes indicated by a carrier app.
+ *
+ * @see #onCarrierNetworkRequest
+ * @see TelephonyManager#notifyCarrierNetworkChange(boolean)
+ * @hide
+ */
+ public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
+
+ /**
+ * Listen for changes to the sim voice activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+ * fully activated
+ *
+ * @see #onVoiceActivationStateChanged
+ * @hide
+ */
+ public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000;
+
+ /**
+ * Listen for changes to the sim data activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+ * fully activated
+ *
+ * @see #onDataActivationStateChanged
+ * @hide
+ */
+ public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
+
+ /*
+ * Subscription used to listen to the phone state changes
+ * @hide
+ */
+ /** @hide */
+ protected Integer mSubId;
+
+ private final Handler mHandler;
+
+ /**
+ * Create a PhoneStateListener for the Phone with the default subscription.
+ * This class requires Looper.myLooper() not return null.
+ */
+ public PhoneStateListener() {
+ this(null, Looper.myLooper());
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone with the default subscription
+ * using a particular non-null Looper.
+ * @hide
+ */
+ public PhoneStateListener(Looper looper) {
+ this(null, looper);
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone using the specified subscription.
+ * This class requires Looper.myLooper() not return null. To supply your
+ * own non-null Looper use PhoneStateListener(int subId, Looper looper) below.
+ * @hide
+ */
+ public PhoneStateListener(Integer subId) {
+ this(subId, Looper.myLooper());
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone using the specified subscription
+ * and non-null Looper.
+ * @hide
+ */
+ public PhoneStateListener(Integer subId, Looper looper) {
+ if (DBG) log("ctor: subId=" + subId + " looper=" + looper);
+ mSubId = subId;
+ mHandler = new Handler(looper) {
+ public void handleMessage(Message msg) {
+ if (DBG) {
+ log("mSubId=" + mSubId + " what=0x" + Integer.toHexString(msg.what)
+ + " msg=" + msg);
+ }
+ switch (msg.what) {
+ case LISTEN_SERVICE_STATE:
+ PhoneStateListener.this.onServiceStateChanged((ServiceState)msg.obj);
+ break;
+ case LISTEN_SIGNAL_STRENGTH:
+ PhoneStateListener.this.onSignalStrengthChanged(msg.arg1);
+ break;
+ case LISTEN_MESSAGE_WAITING_INDICATOR:
+ PhoneStateListener.this.onMessageWaitingIndicatorChanged(msg.arg1 != 0);
+ break;
+ case LISTEN_CALL_FORWARDING_INDICATOR:
+ PhoneStateListener.this.onCallForwardingIndicatorChanged(msg.arg1 != 0);
+ break;
+ case LISTEN_CELL_LOCATION:
+ PhoneStateListener.this.onCellLocationChanged((CellLocation)msg.obj);
+ break;
+ case LISTEN_CALL_STATE:
+ PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj);
+ break;
+ case LISTEN_DATA_CONNECTION_STATE:
+ PhoneStateListener.this.onDataConnectionStateChanged(msg.arg1, msg.arg2);
+ PhoneStateListener.this.onDataConnectionStateChanged(msg.arg1);
+ break;
+ case LISTEN_DATA_ACTIVITY:
+ PhoneStateListener.this.onDataActivity(msg.arg1);
+ break;
+ case LISTEN_SIGNAL_STRENGTHS:
+ PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj);
+ break;
+ case LISTEN_OTASP_CHANGED:
+ PhoneStateListener.this.onOtaspChanged(msg.arg1);
+ break;
+ case LISTEN_CELL_INFO:
+ PhoneStateListener.this.onCellInfoChanged((List<CellInfo>)msg.obj);
+ break;
+ case LISTEN_PRECISE_CALL_STATE:
+ PhoneStateListener.this.onPreciseCallStateChanged((PreciseCallState)msg.obj);
+ break;
+ case LISTEN_PRECISE_DATA_CONNECTION_STATE:
+ PhoneStateListener.this.onPreciseDataConnectionStateChanged(
+ (PreciseDataConnectionState)msg.obj);
+ break;
+ case LISTEN_DATA_CONNECTION_REAL_TIME_INFO:
+ PhoneStateListener.this.onDataConnectionRealTimeInfoChanged(
+ (DataConnectionRealTimeInfo)msg.obj);
+ break;
+ case LISTEN_VOLTE_STATE:
+ PhoneStateListener.this.onVoLteServiceStateChanged((VoLteServiceState)msg.obj);
+ break;
+ case LISTEN_VOICE_ACTIVATION_STATE:
+ PhoneStateListener.this.onVoiceActivationStateChanged((int)msg.obj);
+ break;
+ case LISTEN_DATA_ACTIVATION_STATE:
+ PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
+ break;
+ case LISTEN_OEM_HOOK_RAW_EVENT:
+ PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
+ break;
+ case LISTEN_CARRIER_NETWORK_CHANGE:
+ PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
+ break;
+
+ }
+ }
+ };
+ }
+
+ /**
+ * Callback invoked when device service state changes.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ */
+ public void onServiceStateChanged(ServiceState serviceState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when network signal strength changes.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ * @deprecated Use {@link #onSignalStrengthsChanged(SignalStrength)}
+ */
+ @Deprecated
+ public void onSignalStrengthChanged(int asu) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the message-waiting indicator changes.
+ */
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the call-forwarding indicator changes.
+ */
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when device cell location changes.
+ */
+ public void onCellLocationChanged(CellLocation location) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when device call state changes.
+ * @param state call state
+ * @param incomingNumber incoming call phone number. If application does not have
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
+ * string will be passed as an argument.
+ *
+ * @see TelephonyManager#CALL_STATE_IDLE
+ * @see TelephonyManager#CALL_STATE_RINGING
+ * @see TelephonyManager#CALL_STATE_OFFHOOK
+ */
+ public void onCallStateChanged(int state, String incomingNumber) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when connection state changes.
+ *
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ */
+ public void onDataConnectionStateChanged(int state) {
+ // default implementation empty
+ }
+
+ /**
+ * same as above, but with the network type. Both called.
+ */
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ }
+
+ /**
+ * Callback invoked when data activity state changes.
+ *
+ * @see TelephonyManager#DATA_ACTIVITY_NONE
+ * @see TelephonyManager#DATA_ACTIVITY_IN
+ * @see TelephonyManager#DATA_ACTIVITY_OUT
+ * @see TelephonyManager#DATA_ACTIVITY_INOUT
+ * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+ */
+ public void onDataActivity(int direction) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when network signal strengths changes.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ */
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ // default implementation empty
+ }
+
+
+ /**
+ * The Over The Air Service Provisioning (OTASP) has changed. Requires
+ * the READ_PHONE_STATE permission.
+ * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code>
+ * means the value is currently unknown and the system should wait until
+ * <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before
+ * making the decision to perform OTASP or not.
+ *
+ * @hide
+ */
+ public void onOtaspChanged(int otaspMode) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when a observed cell info has changed,
+ * or new cells have been added or removed.
+ * @param cellInfo is the list of currently visible cells.
+ */
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ }
+
+ /**
+ * Callback invoked when precise device call state changes.
+ *
+ * @hide
+ */
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when data connection state changes with precise information.
+ *
+ * @hide
+ */
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState dataConnectionState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when data connection state changes with precise information.
+ *
+ * @hide
+ */
+ public void onDataConnectionRealTimeInfoChanged(
+ DataConnectionRealTimeInfo dcRtInfo) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the service state of LTE network
+ * related to the VoLTE service has changed.
+ * @param stateInfo is the current LTE network information
+ * @hide
+ */
+ public void onVoLteServiceStateChanged(VoLteServiceState stateInfo) {
+ }
+
+ /**
+ * Callback invoked when the SIM voice activation state has changed
+ * @param state is the current SIM voice activation state
+ * @hide
+ */
+ public void onVoiceActivationStateChanged(int state) {
+
+ }
+
+ /**
+ * Callback invoked when the SIM data activation state has changed
+ * @param state is the current SIM data activation state
+ * @hide
+ */
+ public void onDataActivationStateChanged(int state) {
+
+ }
+
+ /**
+ * Callback invoked when OEM hook raw event is received. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param rawData is the byte array of the OEM hook raw data.
+ * @hide
+ */
+ public void onOemHookRawEvent(byte[] rawData) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when telephony has received notice from a carrier
+ * app that a network action that could result in connectivity loss
+ * has been requested by an app using
+ * {@link android.telephony.TelephonyManager#notifyCarrierNetworkChange(boolean)}
+ *
+ * @param active Whether the carrier network change is or shortly
+ * will be active. This value is true to indicate
+ * showing alternative UI and false to stop.
+ *
+ * @hide
+ */
+ public void onCarrierNetworkChange(boolean active) {
+ // default implementation empty
+ }
+
+ /**
+ * The callback methods need to be called on the handler thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ *
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IPhoneStateListener.Stub callback retaining references to the outside PhoneStateListeners:
+ * even caller has been destroyed and "un-registered" the PhoneStateListener, it is still not
+ * eligible for GC given the references coming from:
+ * Native Stack --> PhoneStateListener --> Context (Activity).
+ * memory of caller's context will be collected after GC from service side get triggered
+ */
+ private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+ private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
+
+ public IPhoneStateListenerStub(PhoneStateListener phoneStateListener) {
+ mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
+ }
+
+ private void send(int what, int arg1, int arg2, Object obj) {
+ PhoneStateListener listener = mPhoneStateListenerWeakRef.get();
+ if (listener != null) {
+ Message.obtain(listener.mHandler, what, arg1, arg2, obj).sendToTarget();
+ }
+ }
+
+ public void onServiceStateChanged(ServiceState serviceState) {
+ send(LISTEN_SERVICE_STATE, 0, 0, serviceState);
+ }
+
+ public void onSignalStrengthChanged(int asu) {
+ send(LISTEN_SIGNAL_STRENGTH, asu, 0, null);
+ }
+
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ send(LISTEN_MESSAGE_WAITING_INDICATOR, mwi ? 1 : 0, 0, null);
+ }
+
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ send(LISTEN_CALL_FORWARDING_INDICATOR, cfi ? 1 : 0, 0, null);
+ }
+
+ public void onCellLocationChanged(Bundle bundle) {
+ CellLocation location = CellLocation.newFromBundle(bundle);
+ send(LISTEN_CELL_LOCATION, 0, 0, location);
+ }
+
+ public void onCallStateChanged(int state, String incomingNumber) {
+ send(LISTEN_CALL_STATE, state, 0, incomingNumber);
+ }
+
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ send(LISTEN_DATA_CONNECTION_STATE, state, networkType, null);
+ }
+
+ public void onDataActivity(int direction) {
+ send(LISTEN_DATA_ACTIVITY, direction, 0, null);
+ }
+
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ send(LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength);
+ }
+
+ public void onOtaspChanged(int otaspMode) {
+ send(LISTEN_OTASP_CHANGED, otaspMode, 0, null);
+ }
+
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ send(LISTEN_CELL_INFO, 0, 0, cellInfo);
+ }
+
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ send(LISTEN_PRECISE_CALL_STATE, 0, 0, callState);
+ }
+
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState dataConnectionState) {
+ send(LISTEN_PRECISE_DATA_CONNECTION_STATE, 0, 0, dataConnectionState);
+ }
+
+ public void onDataConnectionRealTimeInfoChanged(
+ DataConnectionRealTimeInfo dcRtInfo) {
+ send(LISTEN_DATA_CONNECTION_REAL_TIME_INFO, 0, 0, dcRtInfo);
+ }
+
+ public void onVoLteServiceStateChanged(VoLteServiceState lteState) {
+ send(LISTEN_VOLTE_STATE, 0, 0, lteState);
+ }
+
+ public void onVoiceActivationStateChanged(int activationState) {
+ send(LISTEN_VOICE_ACTIVATION_STATE, 0, 0, activationState);
+ }
+
+ public void onDataActivationStateChanged(int activationState) {
+ send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
+ }
+
+ public void onOemHookRawEvent(byte[] rawData) {
+ send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
+ }
+
+ public void onCarrierNetworkChange(boolean active) {
+ send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
+ }
+ }
+
+ IPhoneStateListener callback = new IPhoneStateListenerStub(this);
+
+ private void log(String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/PreciseCallState.java b/android/telephony/PreciseCallState.java
new file mode 100644
index 00000000..f246416d
--- /dev/null
+++ b/android/telephony/PreciseCallState.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.DisconnectCause;
+import android.telephony.PreciseDisconnectCause;
+
+/**
+ * Contains precise call state and call fail causes generated by the
+ * framework and the RIL.
+ *
+ * The following call information is included in returned PreciseCallState:
+ *
+ * <ul>
+ * <li>Ringing call state.
+ * <li>Foreground call state.
+ * <li>Background call state.
+ * <li>Disconnect cause; generated by the framework.
+ * <li>Precise disconnect cause; generated by the RIL.
+ * </ul>
+ *
+ * @hide
+ */
+public class PreciseCallState implements Parcelable {
+
+ /** Call state is not valid (Not received a call state). */
+ public static final int PRECISE_CALL_STATE_NOT_VALID = -1;
+ /** Call state: No activity. */
+ public static final int PRECISE_CALL_STATE_IDLE = 0;
+ /** Call state: Active. */
+ public static final int PRECISE_CALL_STATE_ACTIVE = 1;
+ /** Call state: On hold. */
+ public static final int PRECISE_CALL_STATE_HOLDING = 2;
+ /** Call state: Dialing. */
+ public static final int PRECISE_CALL_STATE_DIALING = 3;
+ /** Call state: Alerting. */
+ public static final int PRECISE_CALL_STATE_ALERTING = 4;
+ /** Call state: Incoming. */
+ public static final int PRECISE_CALL_STATE_INCOMING = 5;
+ /** Call state: Waiting. */
+ public static final int PRECISE_CALL_STATE_WAITING = 6;
+ /** Call state: Disconnected. */
+ public static final int PRECISE_CALL_STATE_DISCONNECTED = 7;
+ /** Call state: Disconnecting. */
+ public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
+
+ private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private int mDisconnectCause = DisconnectCause.NOT_VALID;
+ private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall,
+ int disconnectCause, int preciseDisconnectCause) {
+ mRingingCallState = ringingCall;
+ mForegroundCallState = foregroundCall;
+ mBackgroundCallState = backgroundCall;
+ mDisconnectCause = disconnectCause;
+ mPreciseDisconnectCause = preciseDisconnectCause;
+ }
+
+ /**
+ * Empty Constructor
+ *
+ * @hide
+ */
+ public PreciseCallState() {
+ }
+
+ /**
+ * Construct a PreciseCallState object from the given parcel.
+ */
+ private PreciseCallState(Parcel in) {
+ mRingingCallState = in.readInt();
+ mForegroundCallState = in.readInt();
+ mBackgroundCallState = in.readInt();
+ mDisconnectCause = in.readInt();
+ mPreciseDisconnectCause = in.readInt();
+ }
+
+ /**
+ * Get precise ringing call state
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ */
+ public int getRingingCallState() {
+ return mRingingCallState;
+ }
+
+ /**
+ * Get precise foreground call state
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ */
+ public int getForegroundCallState() {
+ return mForegroundCallState;
+ }
+
+ /**
+ * Get precise background call state
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ */
+ public int getBackgroundCallState() {
+ return mBackgroundCallState;
+ }
+
+ /**
+ * Get disconnect cause generated by the framework
+ *
+ * @see DisconnectCause#NOT_VALID
+ * @see DisconnectCause#NOT_DISCONNECTED
+ * @see DisconnectCause#INCOMING_MISSED
+ * @see DisconnectCause#NORMAL
+ * @see DisconnectCause#LOCAL
+ * @see DisconnectCause#BUSY
+ * @see DisconnectCause#CONGESTION
+ * @see DisconnectCause#MMI
+ * @see DisconnectCause#INVALID_NUMBER
+ * @see DisconnectCause#NUMBER_UNREACHABLE
+ * @see DisconnectCause#SERVER_UNREACHABLE
+ * @see DisconnectCause#INVALID_CREDENTIALS
+ * @see DisconnectCause#OUT_OF_NETWORK
+ * @see DisconnectCause#SERVER_ERROR
+ * @see DisconnectCause#TIMED_OUT
+ * @see DisconnectCause#LOST_SIGNAL
+ * @see DisconnectCause#LIMIT_EXCEEDED
+ * @see DisconnectCause#INCOMING_REJECTED
+ * @see DisconnectCause#POWER_OFF
+ * @see DisconnectCause#OUT_OF_SERVICE
+ * @see DisconnectCause#ICC_ERROR
+ * @see DisconnectCause#CALL_BARRED
+ * @see DisconnectCause#FDN_BLOCKED
+ * @see DisconnectCause#CS_RESTRICTED
+ * @see DisconnectCause#CS_RESTRICTED_NORMAL
+ * @see DisconnectCause#CS_RESTRICTED_EMERGENCY
+ * @see DisconnectCause#UNOBTAINABLE_NUMBER
+ * @see DisconnectCause#CDMA_LOCKED_UNTIL_POWER_CYCLE
+ * @see DisconnectCause#CDMA_DROP
+ * @see DisconnectCause#CDMA_INTERCEPT
+ * @see DisconnectCause#CDMA_REORDER
+ * @see DisconnectCause#CDMA_SO_REJECT
+ * @see DisconnectCause#CDMA_RETRY_ORDER
+ * @see DisconnectCause#CDMA_ACCESS_FAILURE
+ * @see DisconnectCause#CDMA_PREEMPTED
+ * @see DisconnectCause#CDMA_NOT_EMERGENCY
+ * @see DisconnectCause#CDMA_ACCESS_BLOCKED
+ * @see DisconnectCause#ERROR_UNSPECIFIED
+ */
+ public int getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * Get disconnect cause generated by the RIL
+ *
+ * @see PreciseDisconnectCause#NOT_VALID
+ * @see PreciseDisconnectCause#NO_DISCONNECT_CAUSE_AVAILABLE
+ * @see PreciseDisconnectCause#UNOBTAINABLE_NUMBER
+ * @see PreciseDisconnectCause#NORMAL
+ * @see PreciseDisconnectCause#BUSY
+ * @see PreciseDisconnectCause#NUMBER_CHANGED
+ * @see PreciseDisconnectCause#STATUS_ENQUIRY
+ * @see PreciseDisconnectCause#NORMAL_UNSPECIFIED
+ * @see PreciseDisconnectCause#NO_CIRCUIT_AVAIL
+ * @see PreciseDisconnectCause#TEMPORARY_FAILURE
+ * @see PreciseDisconnectCause#SWITCHING_CONGESTION
+ * @see PreciseDisconnectCause#CHANNEL_NOT_AVAIL
+ * @see PreciseDisconnectCause#QOS_NOT_AVAIL
+ * @see PreciseDisconnectCause#BEARER_NOT_AVAIL
+ * @see PreciseDisconnectCause#ACM_LIMIT_EXCEEDED
+ * @see PreciseDisconnectCause#CALL_BARRED
+ * @see PreciseDisconnectCause#FDN_BLOCKED
+ * @see PreciseDisconnectCause#IMSI_UNKNOWN_IN_VLR
+ * @see PreciseDisconnectCause#IMEI_NOT_ACCEPTED
+ * @see PreciseDisconnectCause#CDMA_LOCKED_UNTIL_POWER_CYCLE
+ * @see PreciseDisconnectCause#CDMA_DROP
+ * @see PreciseDisconnectCause#CDMA_INTERCEPT
+ * @see PreciseDisconnectCause#CDMA_REORDER
+ * @see PreciseDisconnectCause#CDMA_SO_REJECT
+ * @see PreciseDisconnectCause#CDMA_RETRY_ORDER
+ * @see PreciseDisconnectCause#CDMA_ACCESS_FAILURE
+ * @see PreciseDisconnectCause#CDMA_PREEMPTED
+ * @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
+ * @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
+ * @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+ */
+ public int getPreciseDisconnectCause() {
+ return mPreciseDisconnectCause;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mRingingCallState);
+ out.writeInt(mForegroundCallState);
+ out.writeInt(mBackgroundCallState);
+ out.writeInt(mDisconnectCause);
+ out.writeInt(mPreciseDisconnectCause);
+ }
+
+ public static final Parcelable.Creator<PreciseCallState> CREATOR
+ = new Parcelable.Creator<PreciseCallState>() {
+
+ public PreciseCallState createFromParcel(Parcel in) {
+ return new PreciseCallState(in);
+ }
+
+ public PreciseCallState[] newArray(int size) {
+ return new PreciseCallState[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mRingingCallState;
+ result = prime * result + mForegroundCallState;
+ result = prime * result + mBackgroundCallState;
+ result = prime * result + mDisconnectCause;
+ result = prime * result + mPreciseDisconnectCause;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PreciseCallState other = (PreciseCallState) obj;
+ return (mRingingCallState != other.mRingingCallState &&
+ mForegroundCallState != other.mForegroundCallState &&
+ mBackgroundCallState != other.mBackgroundCallState &&
+ mDisconnectCause != other.mDisconnectCause &&
+ mPreciseDisconnectCause != other.mPreciseDisconnectCause);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("Ringing call state: " + mRingingCallState);
+ sb.append(", Foreground call state: " + mForegroundCallState);
+ sb.append(", Background call state: " + mBackgroundCallState);
+ sb.append(", Disconnect cause: " + mDisconnectCause);
+ sb.append(", Precise disconnect cause: " + mPreciseDisconnectCause);
+
+ return sb.toString();
+ }
+}
diff --git a/android/telephony/PreciseDataConnectionState.java b/android/telephony/PreciseDataConnectionState.java
new file mode 100644
index 00000000..31c9a9e5
--- /dev/null
+++ b/android/telephony/PreciseDataConnectionState.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager;
+import android.net.LinkProperties;
+
+/**
+ * Contains precise data connection state.
+ *
+ * The following data connection information is included in returned PreciseDataConnectionState:
+ *
+ * <ul>
+ * <li>Data connection state.
+ * <li>Network type of the connection.
+ * <li>APN type.
+ * <li>APN.
+ * <li>Data connection change reason.
+ * <li>The properties of the network link.
+ * <li>Data connection fail cause.
+ * </ul>
+ *
+ * @hide
+ */
+public class PreciseDataConnectionState implements Parcelable {
+
+ private int mState = TelephonyManager.DATA_UNKNOWN;
+ private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private String mAPNType = "";
+ private String mAPN = "";
+ private String mReason = "";
+ private LinkProperties mLinkProperties = null;
+ private String mFailCause = "";
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public PreciseDataConnectionState(int state, int networkType,
+ String apnType, String apn, String reason,
+ LinkProperties linkProperties, String failCause) {
+ mState = state;
+ mNetworkType = networkType;
+ mAPNType = apnType;
+ mAPN = apn;
+ mReason = reason;
+ mLinkProperties = linkProperties;
+ mFailCause = failCause;
+ }
+
+ /**
+ * Empty Constructor
+ *
+ * @hide
+ */
+ public PreciseDataConnectionState() {
+ }
+
+ /**
+ * Construct a PreciseDataConnectionState object from the given parcel.
+ */
+ private PreciseDataConnectionState(Parcel in) {
+ mState = in.readInt();
+ mNetworkType = in.readInt();
+ mAPNType = in.readString();
+ mAPN = in.readString();
+ mReason = in.readString();
+ mLinkProperties = (LinkProperties)in.readParcelable(null);
+ mFailCause = in.readString();
+ }
+
+ /**
+ * Get data connection state
+ *
+ * @see TelephonyManager#DATA_UNKNOWN
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ */
+ public int getDataConnectionState() {
+ return mState;
+ }
+
+ /**
+ * Get data connection network type
+ *
+ * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+ * @see TelephonyManager#NETWORK_TYPE_GPRS
+ * @see TelephonyManager#NETWORK_TYPE_EDGE
+ * @see TelephonyManager#NETWORK_TYPE_UMTS
+ * @see TelephonyManager#NETWORK_TYPE_CDMA
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+ * @see TelephonyManager#NETWORK_TYPE_1xRTT
+ * @see TelephonyManager#NETWORK_TYPE_HSDPA
+ * @see TelephonyManager#NETWORK_TYPE_HSUPA
+ * @see TelephonyManager#NETWORK_TYPE_HSPA
+ * @see TelephonyManager#NETWORK_TYPE_IDEN
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+ * @see TelephonyManager#NETWORK_TYPE_LTE
+ * @see TelephonyManager#NETWORK_TYPE_EHRPD
+ * @see TelephonyManager#NETWORK_TYPE_HSPAP
+ */
+ public int getDataConnectionNetworkType() {
+ return mNetworkType;
+ }
+
+ /**
+ * Get data connection APN type
+ */
+ public String getDataConnectionAPNType() {
+ return mAPNType;
+ }
+
+ /**
+ * Get data connection APN.
+ */
+ public String getDataConnectionAPN() {
+ return mAPN;
+ }
+
+ /**
+ * Get data connection change reason.
+ */
+ public String getDataConnectionChangeReason() {
+ return mReason;
+ }
+
+ /**
+ * Get the properties of the network link.
+ */
+ public LinkProperties getDataConnectionLinkProperties() {
+ return mLinkProperties;
+ }
+
+ /**
+ * Get data connection fail cause, in case there was a failure.
+ */
+ public String getDataConnectionFailCause() {
+ return mFailCause;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mState);
+ out.writeInt(mNetworkType);
+ out.writeString(mAPNType);
+ out.writeString(mAPN);
+ out.writeString(mReason);
+ out.writeParcelable(mLinkProperties, flags);
+ out.writeString(mFailCause);
+ }
+
+ public static final Parcelable.Creator<PreciseDataConnectionState> CREATOR
+ = new Parcelable.Creator<PreciseDataConnectionState>() {
+
+ public PreciseDataConnectionState createFromParcel(Parcel in) {
+ return new PreciseDataConnectionState(in);
+ }
+
+ public PreciseDataConnectionState[] newArray(int size) {
+ return new PreciseDataConnectionState[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mState;
+ result = prime * result + mNetworkType;
+ result = prime * result + ((mAPNType == null) ? 0 : mAPNType.hashCode());
+ result = prime * result + ((mAPN == null) ? 0 : mAPN.hashCode());
+ result = prime * result + ((mReason == null) ? 0 : mReason.hashCode());
+ result = prime * result + ((mLinkProperties == null) ? 0 : mLinkProperties.hashCode());
+ result = prime * result + ((mFailCause == null) ? 0 : mFailCause.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
+ if (mAPN == null) {
+ if (other.mAPN != null) {
+ return false;
+ }
+ } else if (!mAPN.equals(other.mAPN)) {
+ return false;
+ }
+ if (mAPNType == null) {
+ if (other.mAPNType != null) {
+ return false;
+ }
+ } else if (!mAPNType.equals(other.mAPNType)) {
+ return false;
+ }
+ if (mFailCause == null) {
+ if (other.mFailCause != null) {
+ return false;
+ }
+ } else if (!mFailCause.equals(other.mFailCause)) {
+ return false;
+ }
+ if (mLinkProperties == null) {
+ if (other.mLinkProperties != null) {
+ return false;
+ }
+ } else if (!mLinkProperties.equals(other.mLinkProperties)) {
+ return false;
+ }
+ if (mNetworkType != other.mNetworkType) {
+ return false;
+ }
+ if (mReason == null) {
+ if (other.mReason != null) {
+ return false;
+ }
+ } else if (!mReason.equals(other.mReason)) {
+ return false;
+ }
+ if (mState != other.mState) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Data Connection state: " + mState);
+ sb.append(", Network type: " + mNetworkType);
+ sb.append(", APN type: " + mAPNType);
+ sb.append(", APN: " + mAPN);
+ sb.append(", Change reason: " + mReason);
+ sb.append(", Link properties: " + mLinkProperties);
+ sb.append(", Fail cause: " + mFailCause);
+
+ return sb.toString();
+ }
+}
diff --git a/android/telephony/PreciseDisconnectCause.java b/android/telephony/PreciseDisconnectCause.java
new file mode 100644
index 00000000..2516d512
--- /dev/null
+++ b/android/telephony/PreciseDisconnectCause.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Contains precise disconnect call causes generated by the
+ * framework and the RIL.
+ *
+ * @hide
+ */
+public class PreciseDisconnectCause {
+
+ /** The disconnect cause is not valid (Not received a disconnect cause)*/
+ public static final int NOT_VALID = -1;
+ /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
+ public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0;
+ /**
+ * The destination cannot be reached because the number, although valid,
+ * is not currently assigned
+ */
+ public static final int UNOBTAINABLE_NUMBER = 1;
+ /** The user cannot be reached because the network through which the call has been
+ * routed does not serve the destination desired
+ */
+ public static final int NO_ROUTE_TO_DESTINATION = 3;
+ /** The channel most recently identified is not acceptable to the sending entity for
+ * use in this call
+ */
+ public static final int CHANNEL_UNACCEPTABLE = 6;
+ /** The MS has tried to access a service that the MS's network operator or service
+ * provider is not prepared to allow
+ */
+ public static final int OPERATOR_DETERMINED_BARRING = 8;
+ /** One of the users involved in the call has requested that the call is cleared */
+ public static final int NORMAL = 16;
+ /** The called user is unable to accept another call */
+ public static final int BUSY = 17;
+ /** The user does not respond to a call establishment message with either an alerting
+ * or connect indication within the prescribed period of time allocated
+ */
+ public static final int NO_USER_RESPONDING = 18;
+ /** The user has provided an alerting indication but has not provided a connect
+ * indication within a prescribed period of time
+ */
+ public static final int NO_ANSWER_FROM_USER = 19;
+ /** The equipment sending this cause does not wish to accept this call */
+ public static final int CALL_REJECTED = 21;
+ /** The called number is no longer assigned */
+ public static final int NUMBER_CHANGED = 22;
+ /** This cause is returned to the network when a mobile station clears an active
+ * call which is being pre-empted by another call with higher precedence
+ */
+ public static final int PREEMPTION = 25;
+ /** The destination indicated by the mobile station cannot be reached because
+ * the interface to the destination is not functioning correctly
+ */
+ public static final int DESTINATION_OUT_OF_ORDER = 27;
+ /** The called party number is not a valid format or is not complete */
+ public static final int INVALID_NUMBER_FORMAT = 28;
+ /** The facility requested by user can not be provided by the network */
+ public static final int FACILITY_REJECTED = 29;
+ /** Provided in response to a STATUS ENQUIRY message */
+ public static final int STATUS_ENQUIRY = 30;
+ /** Reports a normal disconnect only when no other normal cause applies */
+ public static final int NORMAL_UNSPECIFIED = 31;
+ /** There is no channel presently available to handle the call */
+ public static final int NO_CIRCUIT_AVAIL = 34;
+ /** The network is not functioning correctly and that the condition is likely
+ * to last a relatively long period of time
+ */
+ public static final int NETWORK_OUT_OF_ORDER = 38;
+ /**
+ * The network is not functioning correctly and the condition is not likely to last
+ * a long period of time
+ */
+ public static final int TEMPORARY_FAILURE = 41;
+ /** The switching equipment is experiencing a period of high traffic */
+ public static final int SWITCHING_CONGESTION = 42;
+ /** The network could not deliver access information to the remote user as requested */
+ public static final int ACCESS_INFORMATION_DISCARDED = 43;
+ /** The channel cannot be provided */
+ public static final int CHANNEL_NOT_AVAIL = 44;
+ /** This cause is used to report a resource unavailable event only when no other
+ * cause in the resource unavailable class applies
+ */
+ public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 44;
+ /** The requested quality of service (ITU-T X.213) cannot be provided */
+ public static final int QOS_NOT_AVAIL = 49;
+ /** The facility could not be provided by the network because the user has no
+ * complete subscription
+ */
+ public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50;
+ /** Incoming calls are not allowed within this CUG */
+ public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55;
+ /** The mobile station is not authorized to use bearer capability requested */
+ public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57;
+ /** The requested bearer capability is not available at this time */
+ public static final int BEARER_NOT_AVAIL = 58;
+ /** The service option is not availble at this time */
+ public static final int SERVICE_OPTION_NOT_AVAILABLE = 63;
+ /** The equipment sending this cause does not support the bearer capability requested */
+ public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65;
+ /** The call clearing is due to ACM being greater than or equal to ACMmax */
+ public static final int ACM_LIMIT_EXCEEDED = 68;
+ /** The equipment sending this cause does not support the requested facility */
+ public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69;
+ /** The equipment sending this cause only supports the restricted version of
+ * the requested bearer capability
+ */
+ public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70;
+ /** The service requested is not implemented at network */
+ public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79;
+ /** The equipment sending this cause has received a message with a transaction identifier
+ * which is not currently in use on the MS-network interface
+ */
+ public static final int INVALID_TRANSACTION_IDENTIFIER = 81;
+ /** The called user for the incoming CUG call is not a member of the specified CUG */
+ public static final int USER_NOT_MEMBER_OF_CUG = 87;
+ /** The equipment sending this cause has received a request which can't be accomodated */
+ public static final int INCOMPATIBLE_DESTINATION = 88;
+ /** This cause is used to report receipt of a message with semantically incorrect contents */
+ public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95;
+ /** The equipment sending this cause has received a message with a non-semantical
+ * mandatory IE error
+ */
+ public static final int INVALID_MANDATORY_INFORMATION = 96;
+ /** This is sent in response to a message which is not defined, or defined but not
+ * implemented by the equipment sending this cause
+ */
+ public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97;
+ /** The equipment sending this cause has received a message not compatible with the
+ * protocol state
+ */
+ public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98;
+ /** The equipment sending this cause has received a message which includes information
+ * elements not recognized because its identifier is not defined or it is defined but not
+ * implemented by the equipment sending the cause
+ */
+ public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99;
+ /** The equipment sending this cause has received a message with conditional IE errors */
+ public static final int CONDITIONAL_IE_ERROR = 100;
+ /** The message has been received which is incompatible with the protocol state */
+ public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101;
+ /** The procedure has been initiated by the expiry of a timer in association with
+ * 3GPP TS 24.008 error handling procedures
+ */
+ public static final int RECOVERY_ON_TIMER_EXPIRED = 102;
+ /** This protocol error event is reported only when no other cause in the protocol
+ * error class applies
+ */
+ public static final int PROTOCOL_ERROR_UNSPECIFIED = 111;
+ /** interworking with a network which does not provide causes for actions it takes
+ * thus, the precise cause for a message which is being sent cannot be ascertained
+ */
+ public static final int INTERWORKING_UNSPECIFIED = 127;
+ /** The call is restricted */
+ public static final int CALL_BARRED = 240;
+ /** The call is blocked by the Fixed Dialing Number list */
+ public static final int FDN_BLOCKED = 241;
+ /** The given IMSI is not known at the VLR */
+ /** TS 24.008 cause 4 */
+ public static final int IMSI_UNKNOWN_IN_VLR = 242;
+ /**
+ * The network does not accept emergency call establishment using an IMEI or not accept attach
+ * procedure for emergency services using an IMEI
+ */
+ public static final int IMEI_NOT_ACCEPTED = 243;
+ /** The call cannot be established because RADIO is OFF */
+ public static final int RADIO_OFF = 247;
+ /** The call cannot be established because of no cell coverage */
+ public static final int OUT_OF_SRV = 248;
+ /** The call cannot be established because of no valid SIM */
+ public static final int NO_VALID_SIM = 249;
+ /** The call is dropped or failed internally by modem */
+ public static final int RADIO_INTERNAL_ERROR = 250;
+ /** Call failed because of UE timer expired while waiting for a response from network */
+ public static final int NETWORK_RESP_TIMEOUT = 251;
+ /** Call failed because of a network reject */
+ public static final int NETWORK_REJECT = 252;
+ /** Call failed because of radio access failure. ex. RACH failure */
+ public static final int RADIO_ACCESS_FAILURE = 253;
+ /** Call failed/dropped because of a RLF */
+ public static final int RADIO_LINK_FAILURE = 254;
+ /** Call failed/dropped because of radio link lost */
+ public static final int RADIO_LINK_LOST = 255;
+ /** Call failed because of a radio uplink issue */
+ public static final int RADIO_UPLINK_FAILURE = 256;
+ /** Call failed because of a RRC connection setup failure */
+ public static final int RADIO_SETUP_FAILURE = 257;
+ /** Call failed/dropped because of RRC connection release from NW */
+ public static final int RADIO_RELEASE_NORMAL = 258;
+ /** Call failed/dropped because of RRC abnormally released by modem/network */
+ public static final int RADIO_RELEASE_ABNORMAL = 259;
+ /** Call setup failed because of access class barring */
+ public static final int ACCESS_CLASS_BLOCKED = 260;
+ /** Call failed/dropped because of a network detach */
+ public static final int NETWORK_DETACH = 261;
+
+ /** MS is locked until next power cycle */
+ public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
+ /** Drop call*/
+ public static final int CDMA_DROP = 1001;
+ /** INTERCEPT order received, MS state idle entered */
+ public static final int CDMA_INTERCEPT = 1002;
+ /** MS has been redirected, call is cancelled */
+ public static final int CDMA_REORDER = 1003;
+ /** Service option rejection */
+ public static final int CDMA_SO_REJECT = 1004;
+ /** Requested service is rejected, retry delay is set */
+ public static final int CDMA_RETRY_ORDER = 1005;
+ /** Unable to obtain access to the CDMA system */
+ public static final int CDMA_ACCESS_FAILURE = 1006;
+ /** Not a preempted call */
+ public static final int CDMA_PREEMPTED = 1007;
+ /** Not an emergency call */
+ public static final int CDMA_NOT_EMERGENCY = 1008;
+ /** Access Blocked by CDMA network */
+ public static final int CDMA_ACCESS_BLOCKED = 1009;
+
+ /** Mapped from ImsReasonInfo */
+ /* The passed argument is an invalid */
+ public static final int LOCAL_ILLEGAL_ARGUMENT = 1200;
+ // The operation is invoked in invalid call state
+ public static final int LOCAL_ILLEGAL_STATE = 1201;
+ // IMS service internal error
+ public static final int LOCAL_INTERNAL_ERROR = 1202;
+ // IMS service goes down (service connection is lost)
+ public static final int LOCAL_IMS_SERVICE_DOWN = 1203;
+ // No pending incoming call exists
+ public static final int LOCAL_NO_PENDING_CALL = 1204;
+ // Service unavailable; by power off
+ public static final int LOCAL_POWER_OFF = 1205;
+ // Service unavailable; by low battery
+ public static final int LOCAL_LOW_BATTERY = 1206;
+ // Service unavailable; by out of service (data service state)
+ public static final int LOCAL_NETWORK_NO_SERVICE = 1207;
+ /* Service unavailable; by no LTE coverage
+ * (VoLTE is not supported even though IMS is registered)
+ */
+ public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208;
+ /** Service unavailable; by located in roaming area */
+ public static final int LOCAL_NETWORK_ROAMING = 1209;
+ /** Service unavailable; by IP changed */
+ public static final int LOCAL_NETWORK_IP_CHANGED = 1210;
+ /** Service unavailable; other */
+ public static final int LOCAL_SERVICE_UNAVAILABLE = 1211;
+ /* Service unavailable; IMS connection is lost (IMS is not registered) */
+ public static final int LOCAL_NOT_REGISTERED = 1212;
+ /** Max call exceeded */
+ public static final int LOCAL_MAX_CALL_EXCEEDED = 1213;
+ /** Call decline */
+ public static final int LOCAL_CALL_DECLINE = 1214;
+ /** SRVCC is in progress */
+ public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215;
+ /** Resource reservation is failed (QoS precondition) */
+ public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216;
+ /** Retry CS call; VoLTE service can't be provided by the network or remote end
+ * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ */
+ public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217;
+ /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+ public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218;
+ /** IMS call is already terminated (in TERMINATED state) */
+ public static final int LOCAL_CALL_TERMINATED = 1219;
+ /** Handover not feasible */
+ public static final int LOCAL_HO_NOT_FEASIBLE = 1220;
+
+ /** 1xx waiting timer is expired after sending INVITE request (MO only) */
+ public static final int TIMEOUT_1XX_WAITING = 1221;
+ /** User no answer during call setup operation (MO/MT)
+ * MO : 200 OK to INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
+ public static final int TIMEOUT_NO_ANSWER = 1222;
+ /** User no answer during call update operation (MO/MT)
+ * MO : 200 OK to re-INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
+ public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223;
+
+ /**
+ * STATUSCODE (SIP response code) (IMS -> Telephony)
+ */
+ /** SIP request is redirected */
+ public static final int SIP_REDIRECTED = 1300;
+ /** 4xx responses */
+ /** 400 : Bad Request */
+ public static final int SIP_BAD_REQUEST = 1310;
+ /** 403 : Forbidden */
+ public static final int SIP_FORBIDDEN = 1311;
+ /** 404 : Not Found */
+ public static final int SIP_NOT_FOUND = 1312;
+ /** 415 : Unsupported Media Type
+ * 416 : Unsupported URI Scheme
+ * 420 : Bad Extension
+ */
+ public static final int SIP_NOT_SUPPORTED = 1313;
+ /** 408 : Request Timeout */
+ public static final int SIP_REQUEST_TIMEOUT = 1314;
+ /** 480 : Temporarily Unavailable */
+ public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315;
+ /** 484 : Address Incomplete */
+ public static final int SIP_BAD_ADDRESS = 1316;
+ /** 486 : Busy Here
+ * 600 : Busy Everywhere
+ */
+ public static final int SIP_BUSY = 1317;
+ /** 487 : Request Terminated */
+ public static final int SIP_REQUEST_CANCELLED = 1318;
+ /** 406 : Not Acceptable
+ * 488 : Not Acceptable Here
+ * 606 : Not Acceptable
+ */
+ public static final int SIP_NOT_ACCEPTABLE = 1319;
+ /** 410 : Gone
+ * 604 : Does Not Exist Anywhere
+ */
+ public static final int SIP_NOT_REACHABLE = 1320;
+ /** Others */
+ public static final int SIP_CLIENT_ERROR = 1321;
+ /** 5xx responses
+ * 501 : Server Internal Error
+ */
+ public static final int SIP_SERVER_INTERNAL_ERROR = 1330;
+ /** 503 : Service Unavailable */
+ public static final int SIP_SERVICE_UNAVAILABLE = 1331;
+ /** 504 : Server Time-out */
+ public static final int SIP_SERVER_TIMEOUT = 1332;
+ /** Others */
+ public static final int SIP_SERVER_ERROR = 1333;
+ /** 6xx responses
+ * 603 : Decline
+ */
+ public static final int SIP_USER_REJECTED = 1340;
+ /** Others */
+ public static final int SIP_GLOBAL_ERROR = 1341;
+ /** Emergency failure */
+ public static final int EMERGENCY_TEMP_FAILURE = 1342;
+ public static final int EMERGENCY_PERM_FAILURE = 1343;
+ /** Media resource initialization failed */
+ public static final int MEDIA_INIT_FAILED = 1400;
+ /** RTP timeout (no audio / video traffic in the session) */
+ public static final int MEDIA_NO_DATA = 1401;
+ /** Media is not supported; so dropped the call */
+ public static final int MEDIA_NOT_ACCEPTABLE = 1402;
+ /** Unknown media related errors */
+ public static final int MEDIA_UNSPECIFIED = 1403;
+ /** User triggers the call end */
+ public static final int USER_TERMINATED = 1500;
+ /** No action while an incoming call is ringing */
+ public static final int USER_NOANSWER = 1501;
+ /** User ignores an incoming call */
+ public static final int USER_IGNORE = 1502;
+ /** User declines an incoming call */
+ public static final int USER_DECLINE = 1503;
+ /** Device declines/ends a call due to low battery */
+ public static final int LOW_BATTERY = 1504;
+ /** Device declines call due to blacklisted call ID */
+ public static final int BLACKLISTED_CALL_ID = 1505;
+ /** The call is terminated by the network or remote user */
+ public static final int USER_TERMINATED_BY_REMOTE = 1510;
+
+ /**
+ * UT
+ */
+ public static final int UT_NOT_SUPPORTED = 1800;
+ public static final int UT_SERVICE_UNAVAILABLE = 1801;
+ public static final int UT_OPERATION_NOT_ALLOWED = 1802;
+ public static final int UT_NETWORK_ERROR = 1803;
+ public static final int UT_CB_PASSWORD_MISMATCH = 1804;
+
+ /**
+ * ECBM
+ */
+ public static final int ECBM_NOT_SUPPORTED = 1900;
+
+ /**
+ * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ */
+ public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901;
+
+ /**
+ * CALL DROP error codes (Call could drop because of many reasons like Network not available,
+ * handover, failed, etc)
+ */
+
+ /**
+ * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+ * active wifi call and at the edge of coverage and there is no qualified LTE network available
+ * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+ * code is received as part of the handover message.
+ */
+ public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000;
+
+ /**
+ * MT call has ended due to a release from the network
+ * because the call was answered elsewhere
+ */
+ public static final int ANSWERED_ELSEWHERE = 2100;
+
+ /**
+ * For MultiEndpoint - Call Pull request has failed
+ */
+ public static final int CALL_PULL_OUT_OF_SYNC = 2101;
+
+ /**
+ * For MultiEndpoint - Call has been pulled from primary to secondary
+ */
+ public static final int CALL_PULLED = 2102;
+
+ /**
+ * Supplementary services (HOLD/RESUME) failure error codes.
+ * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ */
+ public static final int SUPP_SVC_FAILED = 2300;
+ public static final int SUPP_SVC_CANCELLED = 2301;
+ public static final int SUPP_SVC_REINVITE_COLLISION = 2302;
+
+ /**
+ * DPD Procedure received no response or send failed
+ */
+ public static final int IWLAN_DPD_FAILURE = 2400;
+
+ /**
+ * Establishment of the ePDG Tunnel Failed
+ */
+ public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500;
+
+ /**
+ * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ */
+ public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501;
+
+ /**
+ * Connection to the packet gateway is lost
+ */
+ public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502;
+
+ /**
+ * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
+ * where the number of calls across all connected devices has reached the maximum.
+ */
+ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503;
+
+ /**
+ * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+ * declined the call. Used in a multi-endpoint scenario where a remote device declined an
+ * incoming call.
+ */
+ public static final int REMOTE_CALL_DECLINE = 2504;
+
+ /**
+ * Indicates the call was disconnected due to the user reaching their data limit.
+ */
+ public static final int DATA_LIMIT_REACHED = 2505;
+
+ /**
+ * Indicates the call was disconnected due to the user disabling cellular data.
+ */
+ public static final int DATA_DISABLED = 2506;
+
+ /**
+ * Indicates a call was disconnected due to loss of wifi signal.
+ */
+ public static final int WIFI_LOST = 2507;
+
+
+ /* OEM specific error codes. To be used by OEMs when they don't want to
+ reveal error code which would be replaced by ERROR_UNSPECIFIED */
+ public static final int OEM_CAUSE_1 = 0xf001;
+ public static final int OEM_CAUSE_2 = 0xf002;
+ public static final int OEM_CAUSE_3 = 0xf003;
+ public static final int OEM_CAUSE_4 = 0xf004;
+ public static final int OEM_CAUSE_5 = 0xf005;
+ public static final int OEM_CAUSE_6 = 0xf006;
+ public static final int OEM_CAUSE_7 = 0xf007;
+ public static final int OEM_CAUSE_8 = 0xf008;
+ public static final int OEM_CAUSE_9 = 0xf009;
+ public static final int OEM_CAUSE_10 = 0xf00a;
+ public static final int OEM_CAUSE_11 = 0xf00b;
+ public static final int OEM_CAUSE_12 = 0xf00c;
+ public static final int OEM_CAUSE_13 = 0xf00d;
+ public static final int OEM_CAUSE_14 = 0xf00e;
+ public static final int OEM_CAUSE_15 = 0xf00f;
+
+ /** Disconnected due to unspecified reasons */
+ public static final int ERROR_UNSPECIFIED = 0xffff;
+
+ /** Private constructor to avoid class instantiation. */
+ private PreciseDisconnectCause() {
+ // Do nothing.
+ }
+}
diff --git a/android/telephony/RadioAccessFamily.java b/android/telephony/RadioAccessFamily.java
new file mode 100644
index 00000000..d657bae0
--- /dev/null
+++ b/android/telephony/RadioAccessFamily.java
@@ -0,0 +1,385 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.RILConstants;
+
+/**
+ * Object to indicate the phone radio type and access technology.
+ *
+ * @hide
+ */
+public class RadioAccessFamily implements Parcelable {
+
+ // Radio Access Family
+ // 2G
+ public static final int RAF_UNKNOWN = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
+ public static final int RAF_GSM = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
+ public static final int RAF_GPRS = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GPRS);
+ public static final int RAF_EDGE = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EDGE);
+ public static final int RAF_IS95A = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95A);
+ public static final int RAF_IS95B = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95B);
+ public static final int RAF_1xRTT = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
+ // 3G
+ public static final int RAF_EVDO_0 = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0);
+ public static final int RAF_EVDO_A = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A);
+ public static final int RAF_EVDO_B = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B);
+ public static final int RAF_EHRPD = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD);
+ public static final int RAF_HSUPA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA);
+ public static final int RAF_HSDPA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA);
+ public static final int RAF_HSPA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
+ public static final int RAF_HSPAP = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP);
+ public static final int RAF_UMTS = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
+ public static final int RAF_TD_SCDMA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA);
+ // 4G
+ public static final int RAF_LTE = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ public static final int RAF_LTE_CA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA);
+
+ // Grouping of RAFs
+ // 2G
+ private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
+ private static final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
+ // 3G
+ private static final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD;
+ private static final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
+ private static final int WCDMA = HS | RAF_UMTS;
+ // 4G
+ private static final int LTE = RAF_LTE | RAF_LTE_CA;
+
+ /* Phone ID of phone */
+ private int mPhoneId;
+
+ /* Radio Access Family */
+ private int mRadioAccessFamily;
+
+ /**
+ * Constructor.
+ *
+ * @param phoneId the phone ID
+ * @param radioAccessFamily the phone radio access family defined
+ * in RadioAccessFamily. It's a bit mask value to represent
+ * the support type.
+ */
+ public RadioAccessFamily(int phoneId, int radioAccessFamily) {
+ mPhoneId = phoneId;
+ mRadioAccessFamily = radioAccessFamily;
+ }
+
+ /**
+ * Get phone ID.
+ *
+ * @return phone ID
+ */
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ /**
+ * get radio access family.
+ *
+ * @return radio access family
+ */
+ public int getRadioAccessFamily() {
+ return mRadioAccessFamily;
+ }
+
+ @Override
+ public String toString() {
+ String ret = "{ mPhoneId = " + mPhoneId
+ + ", mRadioAccessFamily = " + mRadioAccessFamily
+ + "}";
+ return ret;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ *
+ * @return describe content
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ *
+ * @param outParcel The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ @Override
+ public void writeToParcel(Parcel outParcel, int flags) {
+ outParcel.writeInt(mPhoneId);
+ outParcel.writeInt(mRadioAccessFamily);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final Creator<RadioAccessFamily> CREATOR =
+ new Creator<RadioAccessFamily>() {
+
+ @Override
+ public RadioAccessFamily createFromParcel(Parcel in) {
+ int phoneId = in.readInt();
+ int radioAccessFamily = in.readInt();
+
+ return new RadioAccessFamily(phoneId, radioAccessFamily);
+ }
+
+ @Override
+ public RadioAccessFamily[] newArray(int size) {
+ return new RadioAccessFamily[size];
+ }
+ };
+
+ public static int getRafFromNetworkType(int type) {
+ int raf;
+
+ switch (type) {
+ case RILConstants.NETWORK_MODE_WCDMA_PREF:
+ raf = GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_GSM_ONLY:
+ raf = GSM;
+ break;
+ case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+ raf = WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_GSM_UMTS:
+ raf = GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_CDMA:
+ raf = CDMA | EVDO;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+ raf = LTE | CDMA | EVDO;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
+ raf = LTE | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+ raf = LTE | CDMA | EVDO | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_ONLY:
+ raf = LTE;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_WCDMA:
+ raf = LTE | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+ raf = CDMA;
+ break;
+ case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+ raf = EVDO;
+ break;
+ case RILConstants.NETWORK_MODE_GLOBAL:
+ raf = GSM | WCDMA | CDMA | EVDO;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+ raf = RAF_TD_SCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+ raf = RAF_TD_SCDMA | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+ raf = LTE | RAF_TD_SCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+ raf = RAF_TD_SCDMA | GSM;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+ raf = LTE | RAF_TD_SCDMA | GSM;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+ raf = RAF_TD_SCDMA | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+ raf = LTE | RAF_TD_SCDMA | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+ raf = LTE | RAF_TD_SCDMA | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ raf = RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ raf = LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ break;
+ default:
+ raf = RAF_UNKNOWN;
+ break;
+ }
+
+ return raf;
+ }
+
+ /**
+ * if the raf includes ANY bit set for a group
+ * adjust it to contain ALL the bits for that group
+ */
+ private static int getAdjustedRaf(int raf) {
+ raf = ((GSM & raf) > 0) ? (GSM | raf) : raf;
+ raf = ((WCDMA & raf) > 0) ? (WCDMA | raf) : raf;
+ raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
+ raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
+ raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
+
+ return raf;
+ }
+
+ /**
+ * Returns the highest capability of the RadioAccessFamily (4G > 3G > 2G).
+ * @param raf The RadioAccessFamily that we wish to filter
+ * @return The highest radio capability
+ */
+ public static int getHighestRafCapability(int raf) {
+ if ((LTE & raf) > 0) {
+ return TelephonyManager.NETWORK_CLASS_4_G;
+ }
+ if ((EVDO|HS|WCDMA & raf) > 0) {
+ return TelephonyManager.NETWORK_CLASS_3_G;
+ }
+ if((GSM|CDMA & raf) > 0) {
+ return TelephonyManager.NETWORK_CLASS_2_G;
+ }
+ return TelephonyManager.NETWORK_CLASS_UNKNOWN;
+ }
+
+ public static int getNetworkTypeFromRaf(int raf) {
+ int type;
+
+ raf = getAdjustedRaf(raf);
+
+ switch (raf) {
+ case (GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_WCDMA_PREF;
+ break;
+ case GSM:
+ type = RILConstants.NETWORK_MODE_GSM_ONLY;
+ break;
+ case WCDMA:
+ type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+ break;
+ case (CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_CDMA;
+ break;
+ case (LTE | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+ break;
+ case (LTE | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+ break;
+ case (LTE | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+ break;
+ case LTE:
+ type = RILConstants.NETWORK_MODE_LTE_ONLY;
+ break;
+ case (LTE | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_WCDMA;
+ break;
+ case CDMA:
+ type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+ break;
+ case EVDO:
+ type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+ break;
+ case (GSM | WCDMA | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_GLOBAL;
+ break;
+ case RAF_TD_SCDMA:
+ type = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+ break;
+ case (RAF_TD_SCDMA | WCDMA):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+ break;
+ case (LTE | RAF_TD_SCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+ break;
+ case (RAF_TD_SCDMA | GSM):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+ break;
+ case (LTE | RAF_TD_SCDMA | GSM):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+ break;
+ case (RAF_TD_SCDMA | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+ break;
+ case (LTE | RAF_TD_SCDMA | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+ break;
+ case (LTE | RAF_TD_SCDMA | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+ break;
+ case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ break;
+ case (LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ break;
+ default:
+ type = RILConstants.PREFERRED_NETWORK_MODE ;
+ break;
+ }
+
+ return type;
+ }
+
+ public static int singleRafTypeFromString(String rafString) {
+ switch (rafString) {
+ case "GPRS": return RAF_GPRS;
+ case "EDGE": return RAF_EDGE;
+ case "UMTS": return RAF_UMTS;
+ case "IS95A": return RAF_IS95A;
+ case "IS95B": return RAF_IS95B;
+ case "1XRTT": return RAF_1xRTT;
+ case "EVDO_0": return RAF_EVDO_0;
+ case "EVDO_A": return RAF_EVDO_A;
+ case "HSDPA": return RAF_HSDPA;
+ case "HSUPA": return RAF_HSUPA;
+ case "HSPA": return RAF_HSPA;
+ case "EVDO_B": return RAF_EVDO_B;
+ case "EHRPD": return RAF_EHRPD;
+ case "LTE": return RAF_LTE;
+ case "HSPAP": return RAF_HSPAP;
+ case "GSM": return RAF_GSM;
+ case "TD_SCDMA":return RAF_TD_SCDMA;
+ case "HS": return HS;
+ case "CDMA": return CDMA;
+ case "EVDO": return EVDO;
+ case "WCDMA": return WCDMA;
+ case "LTE_CA": return RAF_LTE_CA;
+ default: return RAF_UNKNOWN;
+ }
+ }
+
+ public static int rafTypeFromString(String rafList) {
+ rafList = rafList.toUpperCase();
+ String[] rafs = rafList.split("\\|");
+ int result = 0;
+ for(String raf : rafs) {
+ int rafType = singleRafTypeFromString(raf.trim());
+ if (rafType == RAF_UNKNOWN) return rafType;
+ result |= rafType;
+ }
+ return result;
+ }
+}
diff --git a/android/telephony/RadioAccessSpecifier.java b/android/telephony/RadioAccessSpecifier.java
new file mode 100644
index 00000000..33ce8b42
--- /dev/null
+++ b/android/telephony/RadioAccessSpecifier.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Describes a particular radio access network to be scanned.
+ *
+ * The scan can be performed on either bands or channels for a specific radio access network type.
+ * @hide
+ */
+public final class RadioAccessSpecifier implements Parcelable {
+
+ /**
+ * The radio access network that needs to be scanned
+ *
+ * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+ */
+ public int radioAccessNetwork;
+
+ /**
+ * The frequency bands that need to be scanned
+ *
+ * bands must be used together with radioAccessNetwork
+ *
+ * See {@link RadioNetworkConstants} for details.
+ */
+ public int[] bands;
+
+ /**
+ * The frequency channels that need to be scanned
+ *
+ * channels must be used together with radioAccessNetwork
+ *
+ * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+ */
+ public int[] channels;
+
+ /**
+ * Creates a new RadioAccessSpecifier with radio network, bands and channels
+ *
+ * The user must specify the radio network type, and at least specify either of frequency
+ * bands or channels.
+ *
+ * @param ran The type of the radio access network
+ * @param bands the frequency bands to be scanned
+ * @param channels the frequency bands to be scanned
+ */
+ public RadioAccessSpecifier(int ran, int[] bands, int[] channels) {
+ this.radioAccessNetwork = ran;
+ this.bands = bands;
+ this.channels = channels;
+ }
+
+ public static final Parcelable.Creator<RadioAccessSpecifier> CREATOR =
+ new Parcelable.Creator<RadioAccessSpecifier> (){
+ @Override
+ public RadioAccessSpecifier createFromParcel(Parcel in) {
+ return new RadioAccessSpecifier(in);
+ }
+
+ @Override
+ public RadioAccessSpecifier[] newArray(int size) {
+ return new RadioAccessSpecifier[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(radioAccessNetwork);
+ dest.writeIntArray(bands);
+ dest.writeIntArray(channels);
+ }
+
+ private RadioAccessSpecifier(Parcel in) {
+ radioAccessNetwork = in.readInt();
+ bands = in.createIntArray();
+ channels = in.createIntArray();
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ RadioAccessSpecifier ras;
+
+ try {
+ ras = (RadioAccessSpecifier) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (radioAccessNetwork == ras.radioAccessNetwork
+ && Arrays.equals(bands, ras.bands)
+ && Arrays.equals(channels, ras.channels));
+ }
+
+ @Override
+ public int hashCode () {
+ return ((radioAccessNetwork * 31)
+ + (Arrays.hashCode(bands) * 37)
+ + (Arrays.hashCode(channels)) * 39);
+ }
+}
diff --git a/android/telephony/RadioNetworkConstants.java b/android/telephony/RadioNetworkConstants.java
new file mode 100644
index 00000000..1a9072d3
--- /dev/null
+++ b/android/telephony/RadioNetworkConstants.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Contains radio access network related constants.
+ * @hide
+ */
+public final class RadioNetworkConstants {
+
+ public static final class RadioAccessNetworks {
+ public static final int GERAN = 1;
+ public static final int UTRAN = 2;
+ public static final int EUTRAN = 3;
+ /** @hide */
+ public static final int CDMA2000 = 4;
+ }
+
+ /**
+ * Frenquency bands for GERAN.
+ * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
+ */
+ public static final class GeranBands {
+ public static final int BAND_T380 = 1;
+ public static final int BAND_T410 = 2;
+ public static final int BAND_450 = 3;
+ public static final int BAND_480 = 4;
+ public static final int BAND_710 = 5;
+ public static final int BAND_750 = 6;
+ public static final int BAND_T810 = 7;
+ public static final int BAND_850 = 8;
+ public static final int BAND_P900 = 9;
+ public static final int BAND_E900 = 10;
+ public static final int BAND_R900 = 11;
+ public static final int BAND_DCS1800 = 12;
+ public static final int BAND_PCS1900 = 13;
+ public static final int BAND_ER900 = 14;
+ }
+
+ /**
+ * Frenquency bands for UTRAN.
+ * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf
+ */
+ public static final class UtranBands {
+ public static final int BAND_1 = 1;
+ public static final int BAND_2 = 2;
+ public static final int BAND_3 = 3;
+ public static final int BAND_4 = 4;
+ public static final int BAND_5 = 5;
+ public static final int BAND_6 = 6;
+ public static final int BAND_7 = 7;
+ public static final int BAND_8 = 8;
+ public static final int BAND_9 = 9;
+ public static final int BAND_10 = 10;
+ public static final int BAND_11 = 11;
+ public static final int BAND_12 = 12;
+ public static final int BAND_13 = 13;
+ public static final int BAND_14 = 14;
+ /** band 15, 16, 17, 18 are reserved */
+ public static final int BAND_19 = 19;
+ public static final int BAND_20 = 20;
+ public static final int BAND_21 = 21;
+ public static final int BAND_22 = 22;
+ /** band 23, 24 are reserved */
+ public static final int BAND_25 = 25;
+ public static final int BAND_26 = 26;
+ }
+
+ /**
+ * Frenquency bands for EUTRAN.
+ * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf
+ */
+ public static final class EutranBands {
+ public static final int BAND_1 = 1;
+ public static final int BAND_2 = 2;
+ public static final int BAND_3 = 3;
+ public static final int BAND_4 = 4;
+ public static final int BAND_5 = 5;
+ public static final int BAND_6 = 6;
+ public static final int BAND_7 = 7;
+ public static final int BAND_8 = 8;
+ public static final int BAND_9 = 9;
+ public static final int BAND_10 = 10;
+ public static final int BAND_11 = 11;
+ public static final int BAND_12 = 12;
+ public static final int BAND_13 = 13;
+ public static final int BAND_14 = 14;
+ public static final int BAND_17 = 17;
+ public static final int BAND_18 = 18;
+ public static final int BAND_19 = 19;
+ public static final int BAND_20 = 20;
+ public static final int BAND_21 = 21;
+ public static final int BAND_22 = 22;
+ public static final int BAND_23 = 23;
+ public static final int BAND_24 = 24;
+ public static final int BAND_25 = 25;
+ public static final int BAND_26 = 26;
+ public static final int BAND_27 = 27;
+ public static final int BAND_28 = 28;
+ public static final int BAND_30 = 30;
+ public static final int BAND_31 = 31;
+ public static final int BAND_33 = 33;
+ public static final int BAND_34 = 34;
+ public static final int BAND_35 = 35;
+ public static final int BAND_36 = 36;
+ public static final int BAND_37 = 37;
+ public static final int BAND_38 = 38;
+ public static final int BAND_39 = 39;
+ public static final int BAND_40 = 40;
+ public static final int BAND_41 = 41;
+ public static final int BAND_42 = 42;
+ public static final int BAND_43 = 43;
+ public static final int BAND_44 = 44;
+ public static final int BAND_45 = 45;
+ public static final int BAND_46 = 46;
+ public static final int BAND_47 = 47;
+ public static final int BAND_48 = 48;
+ public static final int BAND_65 = 65;
+ public static final int BAND_66 = 66;
+ public static final int BAND_68 = 68;
+ public static final int BAND_70 = 70;
+ }
+
+ /**
+ * Frenquency bands for CDMA2000.
+ * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf
+ * @hide
+ *
+ * TODO(yinxu): Check with the nexus team about the definition of CDMA bands.
+ */
+ public static final class CdmaBands {
+ public static final int BAND_0 = 1;
+ public static final int BAND_1 = 2;
+ public static final int BAND_2 = 3;
+ public static final int BAND_3 = 4;
+ public static final int BAND_4 = 5;
+ public static final int BAND_5 = 6;
+ public static final int BAND_6 = 7;
+ public static final int BAND_7 = 8;
+ public static final int BAND_8 = 9;
+ public static final int BAND_9 = 10;
+ public static final int BAND_10 = 11;
+ public static final int BAND_11 = 12;
+ public static final int BAND_12 = 13;
+ public static final int BAND_13 = 14;
+ public static final int BAND_14 = 15;
+ public static final int BAND_15 = 16;
+ public static final int BAND_16 = 17;
+ public static final int BAND_17 = 18;
+ public static final int BAND_18 = 19;
+ public static final int BAND_19 = 20;
+ public static final int BAND_20 = 21;
+ public static final int BAND_21 = 22;
+ }
+}
diff --git a/android/telephony/Rlog.java b/android/telephony/Rlog.java
new file mode 100644
index 00000000..e0b46e10
--- /dev/null
+++ b/android/telephony/Rlog.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import android.util.Base64;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * A class to log strings to the RADIO LOG.
+ *
+ * @hide
+ */
+public final class Rlog {
+
+ private static final boolean USER_BUILD = Build.IS_USER;
+
+ private Rlog() {
+ }
+
+ public static int v(String tag, String msg) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg);
+ }
+
+ public static int v(String tag, String msg, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int d(String tag, String msg) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag, msg);
+ }
+
+ public static int d(String tag, String msg, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int i(String tag, String msg) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag, msg);
+ }
+
+ public static int i(String tag, String msg, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int w(String tag, String msg) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag, msg);
+ }
+
+ public static int w(String tag, String msg, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int w(String tag, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag, Log.getStackTraceString(tr));
+ }
+
+ public static int e(String tag, String msg) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.ERROR, tag, msg);
+ }
+
+ public static int e(String tag, String msg, Throwable tr) {
+ return Log.println_native(Log.LOG_ID_RADIO, Log.ERROR, tag,
+ msg + '\n' + Log.getStackTraceString(tr));
+ }
+
+ public static int println(int priority, String tag, String msg) {
+ return Log.println_native(Log.LOG_ID_RADIO, priority, tag, msg);
+ }
+
+ public static boolean isLoggable(String tag, int level) {
+ return Log.isLoggable(tag, level);
+ }
+
+ /**
+ * Redact personally identifiable information for production users.
+ * @param tag used to identify the source of a log message
+ * @param pii the personally identifiable information we want to apply secure hash on.
+ * @return If tag is loggable in verbose mode or pii is null, return the original input.
+ * otherwise return a secure Hash of input pii
+ */
+ public static String pii(String tag, Object pii) {
+ String val = String.valueOf(pii);
+ if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) {
+ return val;
+ }
+ return "[" + secureHash(val.getBytes()) + "]";
+ }
+
+ /**
+ * Redact personally identifiable information for production users.
+ * @param enablePiiLogging set when caller explicitly want to enable sensitive logging.
+ * @param pii the personally identifiable information we want to apply secure hash on.
+ * @return If enablePiiLogging is set to true or pii is null, return the original input.
+ * otherwise return a secure Hash of input pii
+ */
+ public static String pii(boolean enablePiiLogging, Object pii) {
+ String val = String.valueOf(pii);
+ if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) {
+ return val;
+ }
+ return "[" + secureHash(val.getBytes()) + "]";
+ }
+
+ /**
+ * Returns a secure hash (using the SHA1 algorithm) of the provided input.
+ *
+ * @return "****" if the build type is user, otherwise the hash
+ * @param input the bytes for which the secure hash should be computed.
+ */
+ private static String secureHash(byte[] input) {
+ // Refrain from logging user personal information in user build.
+ if (USER_BUILD) {
+ return "****";
+ }
+
+ MessageDigest messageDigest;
+
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return "####";
+ }
+
+ byte[] result = messageDigest.digest(input);
+ return Base64.encodeToString(
+ result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+ }
+}
+
diff --git a/android/telephony/ServiceState.java b/android/telephony/ServiceState.java
new file mode 100644
index 00000000..e448fb2a
--- /dev/null
+++ b/android/telephony/ServiceState.java
@@ -0,0 +1,1330 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+/**
+ * Contains phone state and service related information.
+ *
+ * The following phone information is included in returned ServiceState:
+ *
+ * <ul>
+ * <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
+ * <li>Roaming indicator
+ * <li>Operator name, short name and numeric id
+ * <li>Network selection mode
+ * </ul>
+ */
+public class ServiceState implements Parcelable {
+
+ static final String LOG_TAG = "PHONE";
+ static final boolean DBG = false;
+ static final boolean VDBG = false; // STOPSHIP if true
+
+ /**
+ * Normal operation condition, the phone is registered
+ * with an operator either in home network or in roaming.
+ */
+ public static final int STATE_IN_SERVICE = 0;
+
+ /**
+ * Phone is not registered with any operator, the phone
+ * can be currently searching a new operator to register to, or not
+ * searching to registration at all, or registration is denied, or radio
+ * signal is not available.
+ */
+ public static final int STATE_OUT_OF_SERVICE = 1;
+
+ /**
+ * The phone is registered and locked. Only emergency numbers are allowed. {@more}
+ */
+ public static final int STATE_EMERGENCY_ONLY = 2;
+
+ /**
+ * Radio of telephony is explicitly powered off.
+ */
+ public static final int STATE_POWER_OFF = 3;
+
+ /**
+ * RIL level registration state values from ril.h
+ * ((const char **)response)[0] is registration state 0-6,
+ * 0 - Not registered, MT is not currently searching
+ * a new operator to register
+ * 1 - Registered, home network
+ * 2 - Not registered, but MT is currently searching
+ * a new operator to register
+ * 3 - Registration denied
+ * 4 - Unknown
+ * 5 - Registered, roaming
+ * 10 - Same as 0, but indicates that emergency calls
+ * are enabled.
+ * 12 - Same as 2, but indicates that emergency calls
+ * are enabled.
+ * 13 - Same as 3, but indicates that emergency calls
+ * are enabled.
+ * 14 - Same as 4, but indicates that emergency calls
+ * are enabled.
+ * @hide
+ */
+ public static final int RIL_REG_STATE_NOT_REG = 0;
+ /** @hide */
+ public static final int RIL_REG_STATE_HOME = 1;
+ /** @hide */
+ public static final int RIL_REG_STATE_SEARCHING = 2;
+ /** @hide */
+ public static final int RIL_REG_STATE_DENIED = 3;
+ /** @hide */
+ public static final int RIL_REG_STATE_UNKNOWN = 4;
+ /** @hide */
+ public static final int RIL_REG_STATE_ROAMING = 5;
+ /** @hide */
+ public static final int RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED = 10;
+ /** @hide */
+ public static final int RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED = 12;
+ /** @hide */
+ public static final int RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED = 13;
+ /** @hide */
+ public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14;
+
+ /**
+ * Available radio technologies for GSM, UMTS and CDMA.
+ * Duplicates the constants from hardware/radio/include/ril.h
+ * This should only be used by agents working with the ril. Others
+ * should use the equivalent TelephonyManager.NETWORK_TYPE_*
+ */
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_UNKNOWN = 0;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_GPRS = 1;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_EDGE = 2;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_UMTS = 3;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_IS95A = 4;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_IS95B = 5;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_1xRTT = 6;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_EVDO_0 = 7;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_EVDO_A = 8;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_HSDPA = 9;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_HSUPA = 10;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_HSPA = 11;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_EVDO_B = 12;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_EHRPD = 13;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_LTE = 14;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_HSPAP = 15;
+ /**
+ * GSM radio technology only supports voice. It does not support data.
+ * @hide
+ */
+ public static final int RIL_RADIO_TECHNOLOGY_GSM = 16;
+ /** @hide */
+ public static final int RIL_RADIO_TECHNOLOGY_TD_SCDMA = 17;
+ /**
+ * IWLAN
+ * @hide
+ */
+ public static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18;
+
+ /**
+ * LTE_CA
+ * @hide
+ */
+ public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
+
+ /** @hide */
+ public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
+ (1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_IS95B - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_1xRTT - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_0 - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_A - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
+
+ /**
+ * Available registration states for GSM, UMTS and CDMA.
+ */
+ /** @hide */
+ public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0;
+ /** @hide */
+ public static final int REGISTRATION_STATE_HOME_NETWORK = 1;
+ /** @hide */
+ public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2;
+ /** @hide */
+ public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3;
+ /** @hide */
+ public static final int REGISTRATION_STATE_UNKNOWN = 4;
+ /** @hide */
+ public static final int REGISTRATION_STATE_ROAMING = 5;
+
+ private int mVoiceRegState = STATE_OUT_OF_SERVICE;
+ private int mDataRegState = STATE_OUT_OF_SERVICE;
+
+ /**
+ * Roaming type
+ * HOME : in home network
+ * @hide
+ */
+ public static final int ROAMING_TYPE_NOT_ROAMING = 0;
+ /**
+ * Roaming type
+ * UNKNOWN : in a roaming network, but we can not tell if it's domestic or international
+ * @hide
+ */
+ public static final int ROAMING_TYPE_UNKNOWN = 1;
+ /**
+ * Roaming type
+ * DOMESTIC : in domestic roaming network
+ * @hide
+ */
+ public static final int ROAMING_TYPE_DOMESTIC = 2;
+ /**
+ * Roaming type
+ * INTERNATIONAL : in international roaming network
+ * @hide
+ */
+ public static final int ROAMING_TYPE_INTERNATIONAL = 3;
+
+ private int mVoiceRoamingType;
+ private int mDataRoamingType;
+ private String mVoiceOperatorAlphaLong;
+ private String mVoiceOperatorAlphaShort;
+ private String mVoiceOperatorNumeric;
+ private String mDataOperatorAlphaLong;
+ private String mDataOperatorAlphaShort;
+ private String mDataOperatorNumeric;
+ private boolean mIsManualNetworkSelection;
+
+ private boolean mIsEmergencyOnly;
+
+ private int mRilVoiceRadioTechnology;
+ private int mRilDataRadioTechnology;
+
+ private boolean mCssIndicator;
+ private int mNetworkId;
+ private int mSystemId;
+ private int mCdmaRoamingIndicator;
+ private int mCdmaDefaultRoamingIndicator;
+ private int mCdmaEriIconIndex;
+ private int mCdmaEriIconMode;
+
+ private boolean mIsDataRoamingFromRegistration;
+
+ private boolean mIsUsingCarrierAggregation;
+
+ /* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
+ * Reference: 3GPP TS 36.104 5.4.3 */
+ private int mLteEarfcnRsrpBoost = 0;
+
+ /**
+ * get String description of roaming type
+ * @hide
+ */
+ public static final String getRoamingLogString(int roamingType) {
+ switch (roamingType) {
+ case ROAMING_TYPE_NOT_ROAMING:
+ return "home";
+
+ case ROAMING_TYPE_UNKNOWN:
+ return "roaming";
+
+ case ROAMING_TYPE_DOMESTIC:
+ return "Domestic Roaming";
+
+ case ROAMING_TYPE_INTERNATIONAL:
+ return "International Roaming";
+
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Create a new ServiceState from a intent notifier Bundle
+ *
+ * This method is used by PhoneStateIntentReceiver and maybe by
+ * external applications.
+ *
+ * @param m Bundle from intent notifier
+ * @return newly created ServiceState
+ * @hide
+ */
+ public static ServiceState newFromBundle(Bundle m) {
+ ServiceState ret;
+ ret = new ServiceState();
+ ret.setFromNotifierBundle(m);
+ return ret;
+ }
+
+ /**
+ * Empty constructor
+ */
+ public ServiceState() {
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source service state
+ */
+ public ServiceState(ServiceState s) {
+ copyFrom(s);
+ }
+
+ protected void copyFrom(ServiceState s) {
+ mVoiceRegState = s.mVoiceRegState;
+ mDataRegState = s.mDataRegState;
+ mVoiceRoamingType = s.mVoiceRoamingType;
+ mDataRoamingType = s.mDataRoamingType;
+ mVoiceOperatorAlphaLong = s.mVoiceOperatorAlphaLong;
+ mVoiceOperatorAlphaShort = s.mVoiceOperatorAlphaShort;
+ mVoiceOperatorNumeric = s.mVoiceOperatorNumeric;
+ mDataOperatorAlphaLong = s.mDataOperatorAlphaLong;
+ mDataOperatorAlphaShort = s.mDataOperatorAlphaShort;
+ mDataOperatorNumeric = s.mDataOperatorNumeric;
+ mIsManualNetworkSelection = s.mIsManualNetworkSelection;
+ mRilVoiceRadioTechnology = s.mRilVoiceRadioTechnology;
+ mRilDataRadioTechnology = s.mRilDataRadioTechnology;
+ mCssIndicator = s.mCssIndicator;
+ mNetworkId = s.mNetworkId;
+ mSystemId = s.mSystemId;
+ mCdmaRoamingIndicator = s.mCdmaRoamingIndicator;
+ mCdmaDefaultRoamingIndicator = s.mCdmaDefaultRoamingIndicator;
+ mCdmaEriIconIndex = s.mCdmaEriIconIndex;
+ mCdmaEriIconMode = s.mCdmaEriIconMode;
+ mIsEmergencyOnly = s.mIsEmergencyOnly;
+ mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
+ mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
+ mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
+ }
+
+ /**
+ * Construct a ServiceState object from the given parcel.
+ */
+ public ServiceState(Parcel in) {
+ mVoiceRegState = in.readInt();
+ mDataRegState = in.readInt();
+ mVoiceRoamingType = in.readInt();
+ mDataRoamingType = in.readInt();
+ mVoiceOperatorAlphaLong = in.readString();
+ mVoiceOperatorAlphaShort = in.readString();
+ mVoiceOperatorNumeric = in.readString();
+ mDataOperatorAlphaLong = in.readString();
+ mDataOperatorAlphaShort = in.readString();
+ mDataOperatorNumeric = in.readString();
+ mIsManualNetworkSelection = in.readInt() != 0;
+ mRilVoiceRadioTechnology = in.readInt();
+ mRilDataRadioTechnology = in.readInt();
+ mCssIndicator = (in.readInt() != 0);
+ mNetworkId = in.readInt();
+ mSystemId = in.readInt();
+ mCdmaRoamingIndicator = in.readInt();
+ mCdmaDefaultRoamingIndicator = in.readInt();
+ mCdmaEriIconIndex = in.readInt();
+ mCdmaEriIconMode = in.readInt();
+ mIsEmergencyOnly = in.readInt() != 0;
+ mIsDataRoamingFromRegistration = in.readInt() != 0;
+ mIsUsingCarrierAggregation = in.readInt() != 0;
+ mLteEarfcnRsrpBoost = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mVoiceRegState);
+ out.writeInt(mDataRegState);
+ out.writeInt(mVoiceRoamingType);
+ out.writeInt(mDataRoamingType);
+ out.writeString(mVoiceOperatorAlphaLong);
+ out.writeString(mVoiceOperatorAlphaShort);
+ out.writeString(mVoiceOperatorNumeric);
+ out.writeString(mDataOperatorAlphaLong);
+ out.writeString(mDataOperatorAlphaShort);
+ out.writeString(mDataOperatorNumeric);
+ out.writeInt(mIsManualNetworkSelection ? 1 : 0);
+ out.writeInt(mRilVoiceRadioTechnology);
+ out.writeInt(mRilDataRadioTechnology);
+ out.writeInt(mCssIndicator ? 1 : 0);
+ out.writeInt(mNetworkId);
+ out.writeInt(mSystemId);
+ out.writeInt(mCdmaRoamingIndicator);
+ out.writeInt(mCdmaDefaultRoamingIndicator);
+ out.writeInt(mCdmaEriIconIndex);
+ out.writeInt(mCdmaEriIconMode);
+ out.writeInt(mIsEmergencyOnly ? 1 : 0);
+ out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
+ out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
+ out.writeInt(mLteEarfcnRsrpBoost);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<ServiceState> CREATOR =
+ new Parcelable.Creator<ServiceState>() {
+ public ServiceState createFromParcel(Parcel in) {
+ return new ServiceState(in);
+ }
+
+ public ServiceState[] newArray(int size) {
+ return new ServiceState[size];
+ }
+ };
+
+ /**
+ * Get current voice service state
+ */
+ public int getState() {
+ return getVoiceRegState();
+ }
+
+ /**
+ * Get current voice service state
+ *
+ * @see #STATE_IN_SERVICE
+ * @see #STATE_OUT_OF_SERVICE
+ * @see #STATE_EMERGENCY_ONLY
+ * @see #STATE_POWER_OFF
+ *
+ * @hide
+ */
+ public int getVoiceRegState() {
+ return mVoiceRegState;
+ }
+
+ /**
+ * Get current data service state
+ *
+ * @see #STATE_IN_SERVICE
+ * @see #STATE_OUT_OF_SERVICE
+ * @see #STATE_EMERGENCY_ONLY
+ * @see #STATE_POWER_OFF
+ *
+ * @hide
+ */
+ public int getDataRegState() {
+ return mDataRegState;
+ }
+
+ /**
+ * Get current roaming indicator of phone
+ * (note: not just decoding from TS 27.007 7.2)
+ *
+ * @return true if TS 27.007 7.2 roaming is true
+ * and ONS is different from SPN
+ */
+ public boolean getRoaming() {
+ return getVoiceRoaming() || getDataRoaming();
+ }
+
+ /**
+ * Get current voice network roaming status
+ * @return roaming status
+ * @hide
+ */
+ public boolean getVoiceRoaming() {
+ return mVoiceRoamingType != ROAMING_TYPE_NOT_ROAMING;
+ }
+
+ /**
+ * Get current voice network roaming type
+ * @return roaming type
+ * @hide
+ */
+ public int getVoiceRoamingType() {
+ return mVoiceRoamingType;
+ }
+
+ /**
+ * Get current data network roaming type
+ * @return roaming type
+ * @hide
+ */
+ public boolean getDataRoaming() {
+ return mDataRoamingType != ROAMING_TYPE_NOT_ROAMING;
+ }
+
+ /**
+ * Set whether data network registration state is roaming
+ *
+ * This should only be set to the roaming value received
+ * once the data registration phase has completed.
+ * @hide
+ */
+ public void setDataRoamingFromRegistration(boolean dataRoaming) {
+ mIsDataRoamingFromRegistration = dataRoaming;
+ }
+
+ /**
+ * Get whether data network registration state is roaming
+ * @return true if registration indicates roaming, false otherwise
+ * @hide
+ */
+ public boolean getDataRoamingFromRegistration() {
+ return mIsDataRoamingFromRegistration;
+ }
+
+ /**
+ * Get current data network roaming type
+ * @return roaming type
+ * @hide
+ */
+ public int getDataRoamingType() {
+ return mDataRoamingType;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isEmergencyOnly() {
+ return mIsEmergencyOnly;
+ }
+
+ /**
+ * @hide
+ */
+ public int getCdmaRoamingIndicator(){
+ return this.mCdmaRoamingIndicator;
+ }
+
+ /**
+ * @hide
+ */
+ public int getCdmaDefaultRoamingIndicator(){
+ return this.mCdmaDefaultRoamingIndicator;
+ }
+
+ /**
+ * @hide
+ */
+ public int getCdmaEriIconIndex() {
+ return this.mCdmaEriIconIndex;
+ }
+
+ /**
+ * @hide
+ */
+ public int getCdmaEriIconMode() {
+ return this.mCdmaEriIconMode;
+ }
+
+ /**
+ * Get current registered operator name in long alphanumeric format.
+ *
+ * In GSM/UMTS, long format can be up to 16 characters long.
+ * In CDMA, returns the ERI text, if set. Otherwise, returns the ONS.
+ *
+ * @return long name of operator, null if unregistered or unknown
+ */
+ public String getOperatorAlphaLong() {
+ return mVoiceOperatorAlphaLong;
+ }
+
+ /**
+ * Get current registered voice network operator name in long alphanumeric format.
+ * @return long name of operator
+ * @hide
+ */
+ public String getVoiceOperatorAlphaLong() {
+ return mVoiceOperatorAlphaLong;
+ }
+
+ /**
+ * Get current registered data network operator name in long alphanumeric format.
+ * @return long name of voice operator
+ * @hide
+ */
+ public String getDataOperatorAlphaLong() {
+ return mDataOperatorAlphaLong;
+ }
+
+ /**
+ * Get current registered operator name in short alphanumeric format.
+ *
+ * In GSM/UMTS, short format can be up to 8 characters long.
+ *
+ * @return short name of operator, null if unregistered or unknown
+ */
+ public String getOperatorAlphaShort() {
+ return mVoiceOperatorAlphaShort;
+ }
+
+ /**
+ * Get current registered voice network operator name in short alphanumeric format.
+ * @return short name of operator, null if unregistered or unknown
+ * @hide
+ */
+ public String getVoiceOperatorAlphaShort() {
+ return mVoiceOperatorAlphaShort;
+ }
+
+ /**
+ * Get current registered data network operator name in short alphanumeric format.
+ * @return short name of operator, null if unregistered or unknown
+ * @hide
+ */
+ public String getDataOperatorAlphaShort() {
+ return mDataOperatorAlphaShort;
+ }
+
+ /**
+ * Get current registered operator name in long alphanumeric format if
+ * available or short otherwise.
+ *
+ * @see #getOperatorAlphaLong
+ * @see #getOperatorAlphaShort
+ *
+ * @return name of operator, null if unregistered or unknown
+ * @hide
+ */
+ public String getOperatorAlpha() {
+ if (TextUtils.isEmpty(mVoiceOperatorAlphaLong)) {
+ return mVoiceOperatorAlphaShort;
+ }
+
+ return mVoiceOperatorAlphaLong;
+ }
+
+ /**
+ * Get current registered operator numeric id.
+ *
+ * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
+ * network code.
+ *
+ * @return numeric format of operator, null if unregistered or unknown
+ */
+ /*
+ * The country code can be decoded using
+ * {@link com.android.internal.telephony.MccTable#countryCodeForMcc(int)}.
+ */
+ public String getOperatorNumeric() {
+ return mVoiceOperatorNumeric;
+ }
+
+ /**
+ * Get current registered voice network operator numeric id.
+ * @return numeric format of operator, null if unregistered or unknown
+ * @hide
+ */
+ public String getVoiceOperatorNumeric() {
+ return mVoiceOperatorNumeric;
+ }
+
+ /**
+ * Get current registered data network operator numeric id.
+ * @return numeric format of operator, null if unregistered or unknown
+ * @hide
+ */
+ public String getDataOperatorNumeric() {
+ return mDataOperatorNumeric;
+ }
+
+ /**
+ * Get current network selection mode.
+ *
+ * @return true if manual mode, false if automatic mode
+ */
+ public boolean getIsManualSelection() {
+ return mIsManualNetworkSelection;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((mVoiceRegState * 31)
+ + (mDataRegState * 37)
+ + mVoiceRoamingType
+ + mDataRoamingType
+ + (mIsManualNetworkSelection ? 1 : 0)
+ + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
+ + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
+ + ((null == mVoiceOperatorNumeric) ? 0 : mVoiceOperatorNumeric.hashCode())
+ + ((null == mDataOperatorAlphaLong) ? 0 : mDataOperatorAlphaLong.hashCode())
+ + ((null == mDataOperatorAlphaShort) ? 0 : mDataOperatorAlphaShort.hashCode())
+ + ((null == mDataOperatorNumeric) ? 0 : mDataOperatorNumeric.hashCode())
+ + mCdmaRoamingIndicator
+ + mCdmaDefaultRoamingIndicator
+ + (mIsEmergencyOnly ? 1 : 0)
+ + (mIsDataRoamingFromRegistration ? 1 : 0));
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ ServiceState s;
+
+ try {
+ s = (ServiceState) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (mVoiceRegState == s.mVoiceRegState
+ && mDataRegState == s.mDataRegState
+ && mIsManualNetworkSelection == s.mIsManualNetworkSelection
+ && mVoiceRoamingType == s.mVoiceRoamingType
+ && mDataRoamingType == s.mDataRoamingType
+ && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
+ && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
+ && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
+ && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
+ && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
+ && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
+ && equalsHandlesNulls(mRilVoiceRadioTechnology, s.mRilVoiceRadioTechnology)
+ && equalsHandlesNulls(mRilDataRadioTechnology, s.mRilDataRadioTechnology)
+ && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
+ && equalsHandlesNulls(mNetworkId, s.mNetworkId)
+ && equalsHandlesNulls(mSystemId, s.mSystemId)
+ && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
+ && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
+ s.mCdmaDefaultRoamingIndicator)
+ && mIsEmergencyOnly == s.mIsEmergencyOnly
+ && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
+ && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation);
+ }
+
+ /**
+ * Convert radio technology to String
+ *
+ * @param radioTechnology
+ * @return String representation of the RAT
+ *
+ * @hide
+ */
+ public static String rilRadioTechnologyToString(int rt) {
+ String rtString;
+
+ switch(rt) {
+ case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+ rtString = "Unknown";
+ break;
+ case RIL_RADIO_TECHNOLOGY_GPRS:
+ rtString = "GPRS";
+ break;
+ case RIL_RADIO_TECHNOLOGY_EDGE:
+ rtString = "EDGE";
+ break;
+ case RIL_RADIO_TECHNOLOGY_UMTS:
+ rtString = "UMTS";
+ break;
+ case RIL_RADIO_TECHNOLOGY_IS95A:
+ rtString = "CDMA-IS95A";
+ break;
+ case RIL_RADIO_TECHNOLOGY_IS95B:
+ rtString = "CDMA-IS95B";
+ break;
+ case RIL_RADIO_TECHNOLOGY_1xRTT:
+ rtString = "1xRTT";
+ break;
+ case RIL_RADIO_TECHNOLOGY_EVDO_0:
+ rtString = "EvDo-rev.0";
+ break;
+ case RIL_RADIO_TECHNOLOGY_EVDO_A:
+ rtString = "EvDo-rev.A";
+ break;
+ case RIL_RADIO_TECHNOLOGY_HSDPA:
+ rtString = "HSDPA";
+ break;
+ case RIL_RADIO_TECHNOLOGY_HSUPA:
+ rtString = "HSUPA";
+ break;
+ case RIL_RADIO_TECHNOLOGY_HSPA:
+ rtString = "HSPA";
+ break;
+ case RIL_RADIO_TECHNOLOGY_EVDO_B:
+ rtString = "EvDo-rev.B";
+ break;
+ case RIL_RADIO_TECHNOLOGY_EHRPD:
+ rtString = "eHRPD";
+ break;
+ case RIL_RADIO_TECHNOLOGY_LTE:
+ rtString = "LTE";
+ break;
+ case RIL_RADIO_TECHNOLOGY_HSPAP:
+ rtString = "HSPAP";
+ break;
+ case RIL_RADIO_TECHNOLOGY_GSM:
+ rtString = "GSM";
+ break;
+ case RIL_RADIO_TECHNOLOGY_IWLAN:
+ rtString = "IWLAN";
+ break;
+ case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ rtString = "TD-SCDMA";
+ break;
+ case RIL_RADIO_TECHNOLOGY_LTE_CA:
+ rtString = "LTE_CA";
+ break;
+ default:
+ rtString = "Unexpected";
+ Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
+ break;
+ }
+ return rtString;
+ }
+
+ /**
+ * Convert RIL Service State to String
+ *
+ * @param serviceState
+ * @return String representation of the ServiceState
+ *
+ * @hide
+ */
+ public static String rilServiceStateToString(int serviceState) {
+ switch(serviceState) {
+ case STATE_IN_SERVICE:
+ return "IN_SERVICE";
+ case STATE_OUT_OF_SERVICE:
+ return "OUT_OF_SERVICE";
+ case STATE_EMERGENCY_ONLY:
+ return "EMERGENCY_ONLY";
+ case STATE_POWER_OFF:
+ return "POWER_OFF";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
+ .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
+ .append(", mDataRegState=").append(mDataRegState)
+ .append("(" + rilServiceStateToString(mDataRegState) + ")")
+ .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
+ .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
+ .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
+ .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
+ .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
+ .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+ .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
+ .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
+ .append(", mRilVoiceRadioTechnology=").append(mRilVoiceRadioTechnology)
+ .append("(" + rilRadioTechnologyToString(mRilVoiceRadioTechnology) + ")")
+ .append(", mRilDataRadioTechnology=").append(mRilDataRadioTechnology)
+ .append("(" + rilRadioTechnologyToString(mRilDataRadioTechnology) + ")")
+ .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
+ .append(", mNetworkId=").append(mNetworkId)
+ .append(", mSystemId=").append(mSystemId)
+ .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
+ .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
+ .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
+ .append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration)
+ .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
+ .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+ .append("}").toString();
+ }
+
+ private void setNullState(int state) {
+ if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setNullState=" + state);
+ mVoiceRegState = state;
+ mDataRegState = state;
+ mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
+ mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+ mVoiceOperatorAlphaLong = null;
+ mVoiceOperatorAlphaShort = null;
+ mVoiceOperatorNumeric = null;
+ mDataOperatorAlphaLong = null;
+ mDataOperatorAlphaShort = null;
+ mDataOperatorNumeric = null;
+ mIsManualNetworkSelection = false;
+ mRilVoiceRadioTechnology = 0;
+ mRilDataRadioTechnology = 0;
+ mCssIndicator = false;
+ mNetworkId = -1;
+ mSystemId = -1;
+ mCdmaRoamingIndicator = -1;
+ mCdmaDefaultRoamingIndicator = -1;
+ mCdmaEriIconIndex = -1;
+ mCdmaEriIconMode = -1;
+ mIsEmergencyOnly = false;
+ mIsDataRoamingFromRegistration = false;
+ mIsUsingCarrierAggregation = false;
+ mLteEarfcnRsrpBoost = 0;
+ }
+
+ public void setStateOutOfService() {
+ setNullState(STATE_OUT_OF_SERVICE);
+ }
+
+ public void setStateOff() {
+ setNullState(STATE_POWER_OFF);
+ }
+
+ public void setState(int state) {
+ setVoiceRegState(state);
+ if (DBG) Rlog.e(LOG_TAG, "[ServiceState] setState deprecated use setVoiceRegState()");
+ }
+
+ /** @hide */
+ public void setVoiceRegState(int state) {
+ mVoiceRegState = state;
+ if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setVoiceRegState=" + mVoiceRegState);
+ }
+
+ /** @hide */
+ public void setDataRegState(int state) {
+ mDataRegState = state;
+ if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
+ }
+
+ public void setRoaming(boolean roaming) {
+ mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+ mDataRoamingType = mVoiceRoamingType;
+ }
+
+ /** @hide */
+ public void setVoiceRoaming(boolean roaming) {
+ mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+ }
+
+ /** @hide */
+ public void setVoiceRoamingType(int type) {
+ mVoiceRoamingType = type;
+ }
+
+ /** @hide */
+ public void setDataRoaming(boolean dataRoaming) {
+ mDataRoamingType = (dataRoaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+ }
+
+ /** @hide */
+ public void setDataRoamingType(int type) {
+ mDataRoamingType = type;
+ }
+
+ /**
+ * @hide
+ */
+ public void setEmergencyOnly(boolean emergencyOnly) {
+ mIsEmergencyOnly = emergencyOnly;
+ }
+
+ /**
+ * @hide
+ */
+ public void setCdmaRoamingIndicator(int roaming) {
+ this.mCdmaRoamingIndicator = roaming;
+ }
+
+ /**
+ * @hide
+ */
+ public void setCdmaDefaultRoamingIndicator (int roaming) {
+ this.mCdmaDefaultRoamingIndicator = roaming;
+ }
+
+ /**
+ * @hide
+ */
+ public void setCdmaEriIconIndex(int index) {
+ this.mCdmaEriIconIndex = index;
+ }
+
+ /**
+ * @hide
+ */
+ public void setCdmaEriIconMode(int mode) {
+ this.mCdmaEriIconMode = mode;
+ }
+
+ public void setOperatorName(String longName, String shortName, String numeric) {
+ mVoiceOperatorAlphaLong = longName;
+ mVoiceOperatorAlphaShort = shortName;
+ mVoiceOperatorNumeric = numeric;
+ mDataOperatorAlphaLong = longName;
+ mDataOperatorAlphaShort = shortName;
+ mDataOperatorNumeric = numeric;
+ }
+
+ /** @hide */
+ public void setVoiceOperatorName(String longName, String shortName, String numeric) {
+ mVoiceOperatorAlphaLong = longName;
+ mVoiceOperatorAlphaShort = shortName;
+ mVoiceOperatorNumeric = numeric;
+ }
+
+ /** @hide */
+ public void setDataOperatorName(String longName, String shortName, String numeric) {
+ mDataOperatorAlphaLong = longName;
+ mDataOperatorAlphaShort = shortName;
+ mDataOperatorNumeric = numeric;
+ }
+
+ /**
+ * In CDMA, mOperatorAlphaLong can be set from the ERI text.
+ * This is done from the GsmCdmaPhone and not from the ServiceStateTracker.
+ *
+ * @hide
+ */
+ public void setOperatorAlphaLong(String longName) {
+ mVoiceOperatorAlphaLong = longName;
+ mDataOperatorAlphaLong = longName;
+ }
+
+ /** @hide */
+ public void setVoiceOperatorAlphaLong(String longName) {
+ mVoiceOperatorAlphaLong = longName;
+ }
+
+ /** @hide */
+ public void setDataOperatorAlphaLong(String longName) {
+ mDataOperatorAlphaLong = longName;
+ }
+
+ public void setIsManualSelection(boolean isManual) {
+ mIsManualNetworkSelection = isManual;
+ }
+
+ /**
+ * Test whether two objects hold the same data values or both are null.
+ *
+ * @param a first obj
+ * @param b second obj
+ * @return true if two objects equal or both are null
+ */
+ private static boolean equalsHandlesNulls (Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals (b);
+ }
+
+ /**
+ * Set ServiceState based on intent notifier map.
+ *
+ * @param m intent notifier map
+ * @hide
+ */
+ private void setFromNotifierBundle(Bundle m) {
+ mVoiceRegState = m.getInt("voiceRegState");
+ mDataRegState = m.getInt("dataRegState");
+ mVoiceRoamingType = m.getInt("voiceRoamingType");
+ mDataRoamingType = m.getInt("dataRoamingType");
+ mVoiceOperatorAlphaLong = m.getString("operator-alpha-long");
+ mVoiceOperatorAlphaShort = m.getString("operator-alpha-short");
+ mVoiceOperatorNumeric = m.getString("operator-numeric");
+ mDataOperatorAlphaLong = m.getString("data-operator-alpha-long");
+ mDataOperatorAlphaShort = m.getString("data-operator-alpha-short");
+ mDataOperatorNumeric = m.getString("data-operator-numeric");
+ mIsManualNetworkSelection = m.getBoolean("manual");
+ mRilVoiceRadioTechnology = m.getInt("radioTechnology");
+ mRilDataRadioTechnology = m.getInt("dataRadioTechnology");
+ mCssIndicator = m.getBoolean("cssIndicator");
+ mNetworkId = m.getInt("networkId");
+ mSystemId = m.getInt("systemId");
+ mCdmaRoamingIndicator = m.getInt("cdmaRoamingIndicator");
+ mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator");
+ mIsEmergencyOnly = m.getBoolean("emergencyOnly");
+ mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
+ mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
+ mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
+ }
+
+ /**
+ * Set intent notifier Bundle based on service state.
+ *
+ * @param m intent notifier Bundle
+ * @hide
+ */
+ public void fillInNotifierBundle(Bundle m) {
+ m.putInt("voiceRegState", mVoiceRegState);
+ m.putInt("dataRegState", mDataRegState);
+ m.putInt("voiceRoamingType", mVoiceRoamingType);
+ m.putInt("dataRoamingType", mDataRoamingType);
+ m.putString("operator-alpha-long", mVoiceOperatorAlphaLong);
+ m.putString("operator-alpha-short", mVoiceOperatorAlphaShort);
+ m.putString("operator-numeric", mVoiceOperatorNumeric);
+ m.putString("data-operator-alpha-long", mDataOperatorAlphaLong);
+ m.putString("data-operator-alpha-short", mDataOperatorAlphaShort);
+ m.putString("data-operator-numeric", mDataOperatorNumeric);
+ m.putBoolean("manual", mIsManualNetworkSelection);
+ m.putInt("radioTechnology", mRilVoiceRadioTechnology);
+ m.putInt("dataRadioTechnology", mRilDataRadioTechnology);
+ m.putBoolean("cssIndicator", mCssIndicator);
+ m.putInt("networkId", mNetworkId);
+ m.putInt("systemId", mSystemId);
+ m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator);
+ m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator);
+ m.putBoolean("emergencyOnly", mIsEmergencyOnly);
+ m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
+ m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
+ m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
+ }
+
+ /** @hide */
+ public void setRilVoiceRadioTechnology(int rt) {
+ if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
+ rt = RIL_RADIO_TECHNOLOGY_LTE;
+ }
+
+ this.mRilVoiceRadioTechnology = rt;
+ }
+
+ /** @hide */
+ public void setRilDataRadioTechnology(int rt) {
+ if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
+ rt = RIL_RADIO_TECHNOLOGY_LTE;
+ this.mIsUsingCarrierAggregation = true;
+ } else {
+ this.mIsUsingCarrierAggregation = false;
+ }
+ this.mRilDataRadioTechnology = rt;
+ if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setRilDataRadioTechnology=" +
+ mRilDataRadioTechnology);
+ }
+
+ /** @hide */
+ public boolean isUsingCarrierAggregation() {
+ return mIsUsingCarrierAggregation;
+ }
+
+ /** @hide */
+ public void setIsUsingCarrierAggregation(boolean ca) {
+ mIsUsingCarrierAggregation = ca;
+ }
+
+ /** @hide */
+ public int getLteEarfcnRsrpBoost() {
+ return mLteEarfcnRsrpBoost;
+ }
+
+ /** @hide */
+ public void setLteEarfcnRsrpBoost(int LteEarfcnRsrpBoost) {
+ mLteEarfcnRsrpBoost = LteEarfcnRsrpBoost;
+ }
+
+ /** @hide */
+ public void setCssIndicator(int css) {
+ this.mCssIndicator = (css != 0);
+ }
+
+ /** @hide */
+ public void setSystemAndNetworkId(int systemId, int networkId) {
+ this.mSystemId = systemId;
+ this.mNetworkId = networkId;
+ }
+
+ /** @hide */
+ public int getRilVoiceRadioTechnology() {
+ return this.mRilVoiceRadioTechnology;
+ }
+ /** @hide */
+ public int getRilDataRadioTechnology() {
+ return this.mRilDataRadioTechnology;
+ }
+ /**
+ * @hide
+ * @Deprecated to be removed Q3 2013 use {@link #getRilDataRadioTechnology} or
+ * {@link #getRilVoiceRadioTechnology}
+ */
+ public int getRadioTechnology() {
+ Rlog.e(LOG_TAG, "ServiceState.getRadioTechnology() DEPRECATED will be removed *******");
+ return getRilDataRadioTechnology();
+ }
+
+ private int rilRadioTechnologyToNetworkType(int rt) {
+ switch(rt) {
+ case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+ return TelephonyManager.NETWORK_TYPE_GPRS;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+ return TelephonyManager.NETWORK_TYPE_EDGE;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+ return TelephonyManager.NETWORK_TYPE_UMTS;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+ return TelephonyManager.NETWORK_TYPE_HSDPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+ return TelephonyManager.NETWORK_TYPE_HSUPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+ return TelephonyManager.NETWORK_TYPE_HSPA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+ return TelephonyManager.NETWORK_TYPE_CDMA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+ return TelephonyManager.NETWORK_TYPE_1xRTT;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+ return TelephonyManager.NETWORK_TYPE_EVDO_0;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+ return TelephonyManager.NETWORK_TYPE_EVDO_A;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+ return TelephonyManager.NETWORK_TYPE_EVDO_B;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+ return TelephonyManager.NETWORK_TYPE_EHRPD;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+ return TelephonyManager.NETWORK_TYPE_HSPAP;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
+ return TelephonyManager.NETWORK_TYPE_GSM;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+ return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+ return TelephonyManager.NETWORK_TYPE_LTE_CA;
+ default:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * @Deprecated to be removed Q3 2013 use {@link #getVoiceNetworkType}
+ * @hide
+ */
+ public int getNetworkType() {
+ Rlog.e(LOG_TAG, "ServiceState.getNetworkType() DEPRECATED will be removed *******");
+ return rilRadioTechnologyToNetworkType(mRilVoiceRadioTechnology);
+ }
+
+ /** @hide */
+ public int getDataNetworkType() {
+ return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
+ }
+
+ /** @hide */
+ public int getVoiceNetworkType() {
+ return rilRadioTechnologyToNetworkType(mRilVoiceRadioTechnology);
+ }
+
+ /** @hide */
+ public int getCssIndicator() {
+ return this.mCssIndicator ? 1 : 0;
+ }
+
+ /** @hide */
+ public int getNetworkId() {
+ return this.mNetworkId;
+ }
+
+ /** @hide */
+ public int getSystemId() {
+ return this.mSystemId;
+ }
+
+ /** @hide */
+ public static boolean isGsm(int radioTechnology) {
+ return radioTechnology == RIL_RADIO_TECHNOLOGY_GPRS
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_EDGE
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_UMTS
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_HSDPA
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_HSUPA
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPA
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPAP
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_GSM
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA;
+
+ }
+
+ /** @hide */
+ public static boolean isCdma(int radioTechnology) {
+ return radioTechnology == RIL_RADIO_TECHNOLOGY_IS95A
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_IS95B
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_1xRTT
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_0
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_A
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_B
+ || radioTechnology == RIL_RADIO_TECHNOLOGY_EHRPD;
+ }
+
+ /** @hide */
+ public static boolean isLte(int radioTechnology) {
+ return radioTechnology == RIL_RADIO_TECHNOLOGY_LTE ||
+ radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA;
+ }
+
+ /** @hide */
+ public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) {
+ return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0;
+ }
+
+ /** @hide */
+ public static boolean bitmaskHasTech(int bearerBitmask, int radioTech) {
+ if (bearerBitmask == 0) {
+ return true;
+ } else if (radioTech >= 1) {
+ return ((bearerBitmask & (1 << (radioTech - 1))) != 0);
+ }
+ return false;
+ }
+
+ /** @hide */
+ public static int getBitmaskForTech(int radioTech) {
+ if (radioTech >= 1) {
+ return (1 << (radioTech - 1));
+ }
+ return 0;
+ }
+
+ /** @hide */
+ public static int getBitmaskFromString(String bearerList) {
+ String[] bearers = bearerList.split("\\|");
+ int bearerBitmask = 0;
+ for (String bearer : bearers) {
+ int bearerInt = 0;
+ try {
+ bearerInt = Integer.parseInt(bearer.trim());
+ } catch (NumberFormatException nfe) {
+ return 0;
+ }
+
+ if (bearerInt == 0) {
+ return 0;
+ }
+
+ bearerBitmask |= getBitmaskForTech(bearerInt);
+ }
+ return bearerBitmask;
+ }
+
+ /**
+ * Returns a merged ServiceState consisting of the base SS with voice settings from the
+ * voice SS. The voice SS is only used if it is IN_SERVICE (otherwise the base SS is returned).
+ * @hide
+ * */
+ public static ServiceState mergeServiceStates(ServiceState baseSs, ServiceState voiceSs) {
+ if (voiceSs.mVoiceRegState != STATE_IN_SERVICE) {
+ return baseSs;
+ }
+
+ ServiceState newSs = new ServiceState(baseSs);
+
+ // voice overrides
+ newSs.mVoiceRegState = voiceSs.mVoiceRegState;
+ newSs.mIsEmergencyOnly = false; // only get here if voice is IN_SERVICE
+
+ return newSs;
+ }
+}
diff --git a/android/telephony/SignalStrength.java b/android/telephony/SignalStrength.java
new file mode 100644
index 00000000..9e023993
--- /dev/null
+++ b/android/telephony/SignalStrength.java
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.util.Log;
+import android.content.res.Resources;
+
+/**
+ * Contains phone signal strength related information.
+ */
+public class SignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "SignalStrength";
+ private static final boolean DBG = false;
+
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_POOR = 1;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_MODERATE = 2;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_GOOD = 3;
+ /** @hide */
+ public static final int SIGNAL_STRENGTH_GREAT = 4;
+ /** @hide */
+ public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+ /** @hide */
+ public static final String[] SIGNAL_STRENGTH_NAMES = {
+ "none", "poor", "moderate", "good", "great"
+ };
+
+ /** @hide */
+ //Use int max, as -1 is a valid value in signal strength
+ public static final int INVALID = 0x7FFFFFFF;
+
+ private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mCdmaDbm; // This value is the RSSI value
+ private int mCdmaEcio; // This value is the Ec/Io
+ private int mEvdoDbm; // This value is the EVDO RSSI value
+ private int mEvdoEcio; // This value is the EVDO Ec/Io
+ private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio
+ private int mLteSignalStrength;
+ private int mLteRsrp;
+ private int mLteRsrq;
+ private int mLteRssnr;
+ private int mLteCqi;
+ private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
+ // signal strength level
+ private int mTdScdmaRscp;
+
+ private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+
+ /**
+ * Create a new SignalStrength from a intent notifier Bundle
+ *
+ * This method is used by PhoneStateIntentReceiver and maybe by
+ * external applications.
+ *
+ * @param m Bundle from intent notifier
+ * @return newly created SignalStrength
+ *
+ * @hide
+ */
+ public static SignalStrength newFromBundle(Bundle m) {
+ SignalStrength ret;
+ ret = new SignalStrength();
+ ret.setFromNotifierBundle(m);
+ return ret;
+ }
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public SignalStrength() {
+ mGsmSignalStrength = 99;
+ mGsmBitErrorRate = -1;
+ mCdmaDbm = -1;
+ mCdmaEcio = -1;
+ mEvdoDbm = -1;
+ mEvdoEcio = -1;
+ mEvdoSnr = -1;
+ mLteSignalStrength = 99;
+ mLteRsrp = INVALID;
+ mLteRsrq = INVALID;
+ mLteRssnr = INVALID;
+ mLteCqi = INVALID;
+ mLteRsrpBoost = 0;
+ mTdScdmaRscp = INVALID;
+ isGsm = true;
+ }
+
+ /**
+ * This constructor is used to create SignalStrength with default
+ * values and set the isGsmFlag with the value passed in the input
+ *
+ * @param gsmFlag true if Gsm Phone,false if Cdma phone
+ * @return newly created SignalStrength
+ * @hide
+ */
+ public SignalStrength(boolean gsmFlag) {
+ mGsmSignalStrength = 99;
+ mGsmBitErrorRate = -1;
+ mCdmaDbm = -1;
+ mCdmaEcio = -1;
+ mEvdoDbm = -1;
+ mEvdoEcio = -1;
+ mEvdoSnr = -1;
+ mLteSignalStrength = 99;
+ mLteRsrp = INVALID;
+ mLteRsrq = INVALID;
+ mLteRssnr = INVALID;
+ mLteCqi = INVALID;
+ mLteRsrpBoost = 0;
+ mTdScdmaRscp = INVALID;
+ isGsm = gsmFlag;
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag) {
+ initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+ lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag);
+ mTdScdmaRscp = tdScdmaRscp;
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ int tdScdmaRscp, boolean gsmFlag) {
+ initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ mTdScdmaRscp = tdScdmaRscp;
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ boolean gsmFlag) {
+ initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ boolean gsmFlag) {
+ initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
+ INVALID, INVALID, INVALID, 0, gsmFlag);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public SignalStrength(SignalStrength s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize gsm/cdma values, sets lte values to defaults.
+ *
+ * @param gsmSignalStrength
+ * @param gsmBitErrorRate
+ * @param cdmaDbm
+ * @param cdmaEcio
+ * @param evdoDbm
+ * @param evdoEcio
+ * @param evdoSnr
+ * @param gsm
+ *
+ * @hide
+ */
+ public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ boolean gsm) {
+ initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+ evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
+ INVALID, INVALID, INVALID, 0, gsm);
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param gsmSignalStrength
+ * @param gsmBitErrorRate
+ * @param cdmaDbm
+ * @param cdmaEcio
+ * @param evdoDbm
+ * @param evdoEcio
+ * @param evdoSnr
+ * @param lteSignalStrength
+ * @param lteRsrp
+ * @param lteRsrq
+ * @param lteRssnr
+ * @param lteCqi
+ * @param lteRsrpBoost
+ * @param gsm
+ *
+ * @hide
+ */
+ public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
+ int cdmaDbm, int cdmaEcio,
+ int evdoDbm, int evdoEcio, int evdoSnr,
+ int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+ int lteRsrpBoost, boolean gsm) {
+ mGsmSignalStrength = gsmSignalStrength;
+ mGsmBitErrorRate = gsmBitErrorRate;
+ mCdmaDbm = cdmaDbm;
+ mCdmaEcio = cdmaEcio;
+ mEvdoDbm = evdoDbm;
+ mEvdoEcio = evdoEcio;
+ mEvdoSnr = evdoSnr;
+ mLteSignalStrength = lteSignalStrength;
+ mLteRsrp = lteRsrp;
+ mLteRsrq = lteRsrq;
+ mLteRssnr = lteRssnr;
+ mLteCqi = lteCqi;
+ mLteRsrpBoost = lteRsrpBoost;
+ mTdScdmaRscp = INVALID;
+ isGsm = gsm;
+ if (DBG) log("initialize: " + toString());
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(SignalStrength s) {
+ mGsmSignalStrength = s.mGsmSignalStrength;
+ mGsmBitErrorRate = s.mGsmBitErrorRate;
+ mCdmaDbm = s.mCdmaDbm;
+ mCdmaEcio = s.mCdmaEcio;
+ mEvdoDbm = s.mEvdoDbm;
+ mEvdoEcio = s.mEvdoEcio;
+ mEvdoSnr = s.mEvdoSnr;
+ mLteSignalStrength = s.mLteSignalStrength;
+ mLteRsrp = s.mLteRsrp;
+ mLteRsrq = s.mLteRsrq;
+ mLteRssnr = s.mLteRssnr;
+ mLteCqi = s.mLteCqi;
+ mLteRsrpBoost = s.mLteRsrpBoost;
+ mTdScdmaRscp = s.mTdScdmaRscp;
+ isGsm = s.isGsm;
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel.
+ *
+ * @hide
+ */
+ public SignalStrength(Parcel in) {
+ if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
+ mGsmSignalStrength = in.readInt();
+ mGsmBitErrorRate = in.readInt();
+ mCdmaDbm = in.readInt();
+ mCdmaEcio = in.readInt();
+ mEvdoDbm = in.readInt();
+ mEvdoEcio = in.readInt();
+ mEvdoSnr = in.readInt();
+ mLteSignalStrength = in.readInt();
+ mLteRsrp = in.readInt();
+ mLteRsrq = in.readInt();
+ mLteRssnr = in.readInt();
+ mLteCqi = in.readInt();
+ mLteRsrpBoost = in.readInt();
+ mTdScdmaRscp = in.readInt();
+ isGsm = (in.readInt() != 0);
+ }
+
+ /**
+ * Make a SignalStrength object from the given parcel as passed up by
+ * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
+ * so the default is a don't care.
+ *
+ * @hide
+ */
+ public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
+ if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
+ SignalStrength ss = new SignalStrength();
+ ss.mGsmSignalStrength = in.readInt();
+ ss.mGsmBitErrorRate = in.readInt();
+ ss.mCdmaDbm = in.readInt();
+ ss.mCdmaEcio = in.readInt();
+ ss.mEvdoDbm = in.readInt();
+ ss.mEvdoEcio = in.readInt();
+ ss.mEvdoSnr = in.readInt();
+ ss.mLteSignalStrength = in.readInt();
+ ss.mLteRsrp = in.readInt();
+ ss.mLteRsrq = in.readInt();
+ ss.mLteRssnr = in.readInt();
+ ss.mLteCqi = in.readInt();
+ ss.mTdScdmaRscp = in.readInt();
+ return ss;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mGsmSignalStrength);
+ out.writeInt(mGsmBitErrorRate);
+ out.writeInt(mCdmaDbm);
+ out.writeInt(mCdmaEcio);
+ out.writeInt(mEvdoDbm);
+ out.writeInt(mEvdoEcio);
+ out.writeInt(mEvdoSnr);
+ out.writeInt(mLteSignalStrength);
+ out.writeInt(mLteRsrp);
+ out.writeInt(mLteRsrq);
+ out.writeInt(mLteRssnr);
+ out.writeInt(mLteCqi);
+ out.writeInt(mLteRsrpBoost);
+ out.writeInt(mTdScdmaRscp);
+ out.writeInt(isGsm ? 1 : 0);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable.Creator}
+ *
+ * @hide
+ */
+ public static final Parcelable.Creator<SignalStrength> CREATOR = new Parcelable.Creator() {
+ public SignalStrength createFromParcel(Parcel in) {
+ return new SignalStrength(in);
+ }
+
+ public SignalStrength[] newArray(int size) {
+ return new SignalStrength[size];
+ }
+ };
+
+ /**
+ * Validate the individual signal strength fields as per the range
+ * specified in ril.h
+ * Set to invalid any field that is not in the valid range
+ * Cdma, evdo, lte rsrp & rsrq values are sign converted
+ * when received from ril interface
+ *
+ * @return
+ * Valid values for all signalstrength fields
+ * @hide
+ */
+ public void validateInput() {
+ if (DBG) log("Signal before validate=" + this);
+ // TS 27.007 8.5
+ mGsmSignalStrength = mGsmSignalStrength >= 0 ? mGsmSignalStrength : 99;
+ // BER no change;
+
+ mCdmaDbm = mCdmaDbm > 0 ? -mCdmaDbm : -120;
+ mCdmaEcio = (mCdmaEcio > 0) ? -mCdmaEcio : -160;
+
+ mEvdoDbm = (mEvdoDbm > 0) ? -mEvdoDbm : -120;
+ mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -1;
+ mEvdoSnr = ((mEvdoSnr > 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
+
+ // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
+ mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
+ mLteRsrp = ((mLteRsrp >= 44) && (mLteRsrp <= 140)) ? -mLteRsrp : SignalStrength.INVALID;
+ mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
+ mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
+ : SignalStrength.INVALID;
+
+ mTdScdmaRscp = ((mTdScdmaRscp >= 25) && (mTdScdmaRscp <= 120))
+ ? -mTdScdmaRscp : SignalStrength.INVALID;
+ // Cqi no change
+ if (DBG) log("Signal after validate=" + this);
+ }
+
+ /**
+ * @param true - Gsm, Lte phones
+ * false - Cdma phones
+ *
+ * Used by voice phone to set the isGsm
+ * flag
+ * @hide
+ */
+ public void setGsm(boolean gsmFlag) {
+ isGsm = gsmFlag;
+ }
+
+ /**
+ * @param lteRsrpBoost - signal strength offset
+ *
+ * Used by phone to set the lte signal strength offset which will be
+ * reduced from rsrp threshold while calculating signal strength level
+ *
+ * @hide
+ */
+ public void setLteRsrpBoost(int lteRsrpBoost) {
+ mLteRsrpBoost = lteRsrpBoost;
+ }
+
+ /**
+ * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
+ * 27.007 8.5
+ */
+ public int getGsmSignalStrength() {
+ return this.mGsmSignalStrength;
+ }
+
+ /**
+ * Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ */
+ public int getGsmBitErrorRate() {
+ return this.mGsmBitErrorRate;
+ }
+
+ /**
+ * Get the CDMA RSSI value in dBm
+ */
+ public int getCdmaDbm() {
+ return this.mCdmaDbm;
+ }
+
+ /**
+ * Get the CDMA Ec/Io value in dB*10
+ */
+ public int getCdmaEcio() {
+ return this.mCdmaEcio;
+ }
+
+ /**
+ * Get the EVDO RSSI value in dBm
+ */
+ public int getEvdoDbm() {
+ return this.mEvdoDbm;
+ }
+
+ /**
+ * Get the EVDO Ec/Io value in dB*10
+ */
+ public int getEvdoEcio() {
+ return this.mEvdoEcio;
+ }
+
+ /**
+ * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
+ */
+ public int getEvdoSnr() {
+ return this.mEvdoSnr;
+ }
+
+ /** @hide */
+ public int getLteSignalStrength() {
+ return mLteSignalStrength;
+ }
+
+ /** @hide */
+ public int getLteRsrp() {
+ return mLteRsrp;
+ }
+
+ /** @hide */
+ public int getLteRsrq() {
+ return mLteRsrq;
+ }
+
+ /** @hide */
+ public int getLteRssnr() {
+ return mLteRssnr;
+ }
+
+ /** @hide */
+ public int getLteCqi() {
+ return mLteCqi;
+ }
+
+ /** @hide */
+ public int getLteRsrpBoost() {
+ return mLteRsrpBoost;
+ }
+
+ /**
+ * Retrieve an abstract level value for the overall signal strength.
+ *
+ * @return a single integer from 0 to 4 representing the general signal quality.
+ * This may take into account many different radio technology inputs.
+ * 0 represents very poor signal strength
+ * while 4 represents a very strong signal strength.
+ */
+ public int getLevel() {
+ int level = 0;
+
+ if (isGsm) {
+ level = getLteLevel();
+ if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ level = getTdScdmaLevel();
+ if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ level = getGsmLevel();
+ }
+ }
+ } else {
+ int cdmaLevel = getCdmaLevel();
+ int evdoLevel = getEvdoLevel();
+ if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ /* We don't know evdo, use cdma */
+ level = cdmaLevel;
+ } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ /* We don't know cdma, use evdo */
+ level = evdoLevel;
+ } else {
+ /* We know both, use the lowest level */
+ level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
+ }
+ }
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getAsuLevel() {
+ int asuLevel = 0;
+ if (isGsm) {
+ if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ asuLevel = getGsmAsuLevel();
+ } else {
+ asuLevel = getTdScdmaAsuLevel();
+ }
+ } else {
+ asuLevel = getLteAsuLevel();
+ }
+ } else {
+ int cdmaAsuLevel = getCdmaAsuLevel();
+ int evdoAsuLevel = getEvdoAsuLevel();
+ if (evdoAsuLevel == 0) {
+ /* We don't know evdo use, cdma */
+ asuLevel = cdmaAsuLevel;
+ } else if (cdmaAsuLevel == 0) {
+ /* We don't know cdma use, evdo */
+ asuLevel = evdoAsuLevel;
+ } else {
+ /* We know both, use the lowest level */
+ asuLevel = cdmaAsuLevel < evdoAsuLevel ? cdmaAsuLevel : evdoAsuLevel;
+ }
+ }
+ if (DBG) log("getAsuLevel=" + asuLevel);
+ return asuLevel;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ *
+ * @hide
+ */
+ public int getDbm() {
+ int dBm = INVALID;
+
+ if(isGsm()) {
+ dBm = getLteDbm();
+ if (dBm == INVALID) {
+ if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+ dBm = getGsmDbm();
+ } else {
+ dBm = getTdScdmaDbm();
+ }
+ }
+ } else {
+ int cdmaDbm = getCdmaDbm();
+ int evdoDbm = getEvdoDbm();
+
+ return (evdoDbm == -120) ? cdmaDbm : ((cdmaDbm == -120) ? evdoDbm
+ : (cdmaDbm < evdoDbm ? cdmaDbm : evdoDbm));
+ }
+ if (DBG) log("getDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get Gsm signal strength as dBm
+ *
+ * @hide
+ */
+ public int getGsmDbm() {
+ int dBm;
+
+ int gsmSignalStrength = getGsmSignalStrength();
+ int asu = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
+ if (asu != -1) {
+ dBm = -113 + (2 * asu);
+ } else {
+ dBm = -1;
+ }
+ if (DBG) log("getGsmDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get gsm as level 0..4
+ *
+ * @hide
+ */
+ public int getGsmLevel() {
+ int level;
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int asu = getGsmSignalStrength();
+ if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (asu >= 12) level = SIGNAL_STRENGTH_GREAT;
+ else if (asu >= 8) level = SIGNAL_STRENGTH_GOOD;
+ else if (asu >= 5) level = SIGNAL_STRENGTH_MODERATE;
+ else level = SIGNAL_STRENGTH_POOR;
+ if (DBG) log("getGsmLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the gsm signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getGsmAsuLevel() {
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int level = getGsmSignalStrength();
+ if (DBG) log("getGsmAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get cdma as level 0..4
+ *
+ * @hide
+ */
+ public int getCdmaLevel() {
+ final int cdmaDbm = getCdmaDbm();
+ final int cdmaEcio = getCdmaEcio();
+ int levelDbm;
+ int levelEcio;
+
+ if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
+ else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
+ else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
+ else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
+ else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
+ else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
+ else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
+ else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
+ else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ if (DBG) log("getCdmaLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the cdma signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getCdmaAsuLevel() {
+ final int cdmaDbm = getCdmaDbm();
+ final int cdmaEcio = getCdmaEcio();
+ int cdmaAsuLevel;
+ int ecioAsuLevel;
+
+ if (cdmaDbm >= -75) cdmaAsuLevel = 16;
+ else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
+ else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
+ else if (cdmaDbm >= -95) cdmaAsuLevel = 2;
+ else if (cdmaDbm >= -100) cdmaAsuLevel = 1;
+ else cdmaAsuLevel = 99;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) ecioAsuLevel = 16;
+ else if (cdmaEcio >= -100) ecioAsuLevel = 8;
+ else if (cdmaEcio >= -115) ecioAsuLevel = 4;
+ else if (cdmaEcio >= -130) ecioAsuLevel = 2;
+ else if (cdmaEcio >= -150) ecioAsuLevel = 1;
+ else ecioAsuLevel = 99;
+
+ int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel;
+ if (DBG) log("getCdmaAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get Evdo as level 0..4
+ *
+ * @hide
+ */
+ public int getEvdoLevel() {
+ int evdoDbm = getEvdoDbm();
+ int evdoSnr = getEvdoSnr();
+ int levelEvdoDbm;
+ int levelEvdoSnr;
+
+ if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
+ else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
+ else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
+ else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
+ else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
+ else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
+ else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
+ else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
+ else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ if (DBG) log("getEvdoLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the evdo signal level as an asu value between 0..31, 99 is unknown
+ *
+ * @hide
+ */
+ public int getEvdoAsuLevel() {
+ int evdoDbm = getEvdoDbm();
+ int evdoSnr = getEvdoSnr();
+ int levelEvdoDbm;
+ int levelEvdoSnr;
+
+ if (evdoDbm >= -65) levelEvdoDbm = 16;
+ else if (evdoDbm >= -75) levelEvdoDbm = 8;
+ else if (evdoDbm >= -85) levelEvdoDbm = 4;
+ else if (evdoDbm >= -95) levelEvdoDbm = 2;
+ else if (evdoDbm >= -105) levelEvdoDbm = 1;
+ else levelEvdoDbm = 99;
+
+ if (evdoSnr >= 7) levelEvdoSnr = 16;
+ else if (evdoSnr >= 6) levelEvdoSnr = 8;
+ else if (evdoSnr >= 5) levelEvdoSnr = 4;
+ else if (evdoSnr >= 3) levelEvdoSnr = 2;
+ else if (evdoSnr >= 1) levelEvdoSnr = 1;
+ else levelEvdoSnr = 99;
+
+ int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ if (DBG) log("getEvdoAsuLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get LTE as dBm
+ *
+ * @hide
+ */
+ public int getLteDbm() {
+ return mLteRsrp;
+ }
+
+ /**
+ * Get LTE as level 0..4
+ *
+ * @hide
+ */
+ public int getLteLevel() {
+ /*
+ * TS 36.214 Physical Layer Section 5.1.3 TS 36.331 RRC RSSI = received
+ * signal + noise RSRP = reference signal dBm RSRQ = quality of signal
+ * dB= Number of Resource blocksxRSRP/RSSI SNR = gain=signal/noise ratio
+ * = -10log P1/P2 dB
+ */
+ int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
+
+ int[] threshRsrp = Resources.getSystem().getIntArray(
+ com.android.internal.R.array.config_lteDbmThresholds);
+ if (threshRsrp.length != 6) {
+ Log.wtf(LOG_TAG, "getLteLevel - config_lteDbmThresholds has invalid num of elements."
+ + " Cannot evaluate RSRP signal.");
+ } else {
+ if (mLteRsrp > threshRsrp[5]) {
+ rsrpIconLevel = -1;
+ } else if (mLteRsrp >= (threshRsrp[4] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+ } else if (mLteRsrp >= (threshRsrp[3] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+ } else if (mLteRsrp >= (threshRsrp[2] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+ } else if (mLteRsrp >= (threshRsrp[1] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+ } else if (mLteRsrp >= threshRsrp[0]) {
+ rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ }
+ }
+
+ /*
+ * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
+ * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
+ * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
+ * Icon Only
+ */
+ if (mLteRssnr > 300) snrIconLevel = -1;
+ else if (mLteRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mLteRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mLteRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mLteRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
+ else if (mLteRssnr >= -200)
+ snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (DBG) log("getLTELevel - rsrp:" + mLteRsrp + " snr:" + mLteRssnr + " rsrpIconLevel:"
+ + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
+ + " lteRsrpBoost:" + mLteRsrpBoost);
+
+ /* Choose a measurement type to use for notification */
+ if (snrIconLevel != -1 && rsrpIconLevel != -1) {
+ /*
+ * The number of bars displayed shall be the smaller of the bars
+ * associated with LTE RSRP and the bars associated with the LTE
+ * RS_SNR
+ */
+ return (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
+ }
+
+ if (snrIconLevel != -1) return snrIconLevel;
+
+ if (rsrpIconLevel != -1) return rsrpIconLevel;
+
+ /* Valid values are (0-63, 99) as defined in TS 36.331 */
+ if (mLteSignalStrength > 63) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR;
+
+ if (DBG) log("getLTELevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
+ + rssiIconLevel);
+ return rssiIconLevel;
+
+ }
+ /**
+ * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ *
+ * @hide
+ */
+ public int getLteAsuLevel() {
+ int lteAsuLevel = 99;
+ int lteDbm = getLteDbm();
+ /*
+ * 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ * 0 -140 dBm or less
+ * 1 -139 dBm
+ * 2...96 -138... -44 dBm
+ * 97 -43 dBm or greater
+ * 255 not known or not detectable
+ */
+ /*
+ * validateInput will always give a valid range between -140 t0 -44 as
+ * per ril.h. so RSRP >= -43 & <-140 will fall under asu level 255
+ * and not 97 or 0
+ */
+ if (lteDbm == SignalStrength.INVALID) lteAsuLevel = 255;
+ else lteAsuLevel = lteDbm + 140;
+ if (DBG) log("Lte Asu level: "+lteAsuLevel);
+ return lteAsuLevel;
+ }
+
+ /**
+ * @return true if this is for GSM
+ */
+ public boolean isGsm() {
+ return this.isGsm;
+ }
+
+ /**
+ * @return get TD_SCDMA dbm
+ *
+ * @hide
+ */
+ public int getTdScdmaDbm() {
+ return this.mTdScdmaRscp;
+ }
+
+ /**
+ * Get TD-SCDMA as level 0..4
+ * Range : 25 to 120
+ * INT_MAX: 0x7FFFFFFF denotes invalid value
+ * Reference: 3GPP TS 25.123, section 9.1.1.1
+ *
+ * @hide
+ */
+ public int getTdScdmaLevel() {
+ final int tdScdmaDbm = getTdScdmaDbm();
+ int level;
+
+ if ((tdScdmaDbm > -25) || (tdScdmaDbm == SignalStrength.INVALID))
+ level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT;
+ else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD;
+ else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE;
+ else if (tdScdmaDbm >= -110) level = SIGNAL_STRENGTH_POOR;
+ else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+ if (DBG) log("getTdScdmaLevel = " + level);
+ return level;
+ }
+
+ /**
+ * Get the TD-SCDMA signal level as an asu value.
+ *
+ * @hide
+ */
+ public int getTdScdmaAsuLevel() {
+ final int tdScdmaDbm = getTdScdmaDbm();
+ int tdScdmaAsuLevel;
+
+ if (tdScdmaDbm == INVALID) tdScdmaAsuLevel = 255;
+ else tdScdmaAsuLevel = tdScdmaDbm + 120;
+ if (DBG) log("TD-SCDMA Asu level: " + tdScdmaAsuLevel);
+ return tdScdmaAsuLevel;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return ((mGsmSignalStrength * primeNum)
+ + (mGsmBitErrorRate * primeNum)
+ + (mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
+ + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
+ + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
+ }
+
+ /**
+ * @return true if the signal strengths are the same
+ */
+ @Override
+ public boolean equals (Object o) {
+ SignalStrength s;
+
+ try {
+ s = (SignalStrength) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (mGsmSignalStrength == s.mGsmSignalStrength
+ && mGsmBitErrorRate == s.mGsmBitErrorRate
+ && mCdmaDbm == s.mCdmaDbm
+ && mCdmaEcio == s.mCdmaEcio
+ && mEvdoDbm == s.mEvdoDbm
+ && mEvdoEcio == s.mEvdoEcio
+ && mEvdoSnr == s.mEvdoSnr
+ && mLteSignalStrength == s.mLteSignalStrength
+ && mLteRsrp == s.mLteRsrp
+ && mLteRsrq == s.mLteRsrq
+ && mLteRssnr == s.mLteRssnr
+ && mLteCqi == s.mLteCqi
+ && mLteRsrpBoost == s.mLteRsrpBoost
+ && mTdScdmaRscp == s.mTdScdmaRscp
+ && isGsm == s.isGsm);
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return ("SignalStrength:"
+ + " " + mGsmSignalStrength
+ + " " + mGsmBitErrorRate
+ + " " + mCdmaDbm
+ + " " + mCdmaEcio
+ + " " + mEvdoDbm
+ + " " + mEvdoEcio
+ + " " + mEvdoSnr
+ + " " + mLteSignalStrength
+ + " " + mLteRsrp
+ + " " + mLteRsrq
+ + " " + mLteRssnr
+ + " " + mLteCqi
+ + " " + mLteRsrpBoost
+ + " " + mTdScdmaRscp
+ + " " + (isGsm ? "gsm|lte" : "cdma"));
+ }
+
+ /**
+ * Set SignalStrength based on intent notifier map
+ *
+ * @param m intent notifier map
+ * @hide
+ */
+ private void setFromNotifierBundle(Bundle m) {
+ mGsmSignalStrength = m.getInt("GsmSignalStrength");
+ mGsmBitErrorRate = m.getInt("GsmBitErrorRate");
+ mCdmaDbm = m.getInt("CdmaDbm");
+ mCdmaEcio = m.getInt("CdmaEcio");
+ mEvdoDbm = m.getInt("EvdoDbm");
+ mEvdoEcio = m.getInt("EvdoEcio");
+ mEvdoSnr = m.getInt("EvdoSnr");
+ mLteSignalStrength = m.getInt("LteSignalStrength");
+ mLteRsrp = m.getInt("LteRsrp");
+ mLteRsrq = m.getInt("LteRsrq");
+ mLteRssnr = m.getInt("LteRssnr");
+ mLteCqi = m.getInt("LteCqi");
+ mLteRsrpBoost = m.getInt("lteRsrpBoost");
+ mTdScdmaRscp = m.getInt("TdScdma");
+ isGsm = m.getBoolean("isGsm");
+ }
+
+ /**
+ * Set intent notifier Bundle based on SignalStrength
+ *
+ * @param m intent notifier Bundle
+ * @hide
+ */
+ public void fillInNotifierBundle(Bundle m) {
+ m.putInt("GsmSignalStrength", mGsmSignalStrength);
+ m.putInt("GsmBitErrorRate", mGsmBitErrorRate);
+ m.putInt("CdmaDbm", mCdmaDbm);
+ m.putInt("CdmaEcio", mCdmaEcio);
+ m.putInt("EvdoDbm", mEvdoDbm);
+ m.putInt("EvdoEcio", mEvdoEcio);
+ m.putInt("EvdoSnr", mEvdoSnr);
+ m.putInt("LteSignalStrength", mLteSignalStrength);
+ m.putInt("LteRsrp", mLteRsrp);
+ m.putInt("LteRsrq", mLteRsrq);
+ m.putInt("LteRssnr", mLteRssnr);
+ m.putInt("LteCqi", mLteCqi);
+ m.putInt("lteRsrpBoost", mLteRsrpBoost);
+ m.putInt("TdScdma", mTdScdmaRscp);
+ m.putBoolean("isGsm", isGsm);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/SmsCbCmasInfo.java b/android/telephony/SmsCbCmasInfo.java
new file mode 100644
index 00000000..c9129244
--- /dev/null
+++ b/android/telephony/SmsCbCmasInfo.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains CMAS warning notification Type 1 elements for a {@link SmsCbMessage}.
+ * Supported values for each element are defined in TIA-1149-0-1 (CMAS over CDMA) and
+ * 3GPP TS 23.041 (for GSM/UMTS).
+ *
+ * {@hide}
+ */
+public class SmsCbCmasInfo implements Parcelable {
+
+ // CMAS message class (in GSM/UMTS message identifier or CDMA service category).
+
+ /** Presidential-level alert (Korean Public Alert System Class 0 message). */
+ public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0x00;
+
+ /** Extreme threat to life and property (Korean Public Alert System Class 1 message). */
+ public static final int CMAS_CLASS_EXTREME_THREAT = 0x01;
+
+ /** Severe threat to life and property (Korean Public Alert System Class 1 message). */
+ public static final int CMAS_CLASS_SEVERE_THREAT = 0x02;
+
+ /** Child abduction emergency (AMBER Alert). */
+ public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 0x03;
+
+ /** CMAS test message. */
+ public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 0x04;
+
+ /** CMAS exercise. */
+ public static final int CMAS_CLASS_CMAS_EXERCISE = 0x05;
+
+ /** CMAS category for operator defined use. */
+ public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 0x06;
+
+ /** CMAS category for warning types that are reserved for future extension. */
+ public static final int CMAS_CLASS_UNKNOWN = -1;
+
+ // CMAS alert category (in CDMA type 1 elements record).
+
+ /** CMAS alert category: Geophysical including landslide. */
+ public static final int CMAS_CATEGORY_GEO = 0x00;
+
+ /** CMAS alert category: Meteorological including flood. */
+ public static final int CMAS_CATEGORY_MET = 0x01;
+
+ /** CMAS alert category: General emergency and public safety. */
+ public static final int CMAS_CATEGORY_SAFETY = 0x02;
+
+ /** CMAS alert category: Law enforcement, military, homeland/local/private security. */
+ public static final int CMAS_CATEGORY_SECURITY = 0x03;
+
+ /** CMAS alert category: Rescue and recovery. */
+ public static final int CMAS_CATEGORY_RESCUE = 0x04;
+
+ /** CMAS alert category: Fire suppression and rescue. */
+ public static final int CMAS_CATEGORY_FIRE = 0x05;
+
+ /** CMAS alert category: Medical and public health. */
+ public static final int CMAS_CATEGORY_HEALTH = 0x06;
+
+ /** CMAS alert category: Pollution and other environmental. */
+ public static final int CMAS_CATEGORY_ENV = 0x07;
+
+ /** CMAS alert category: Public and private transportation. */
+ public static final int CMAS_CATEGORY_TRANSPORT = 0x08;
+
+ /** CMAS alert category: Utility, telecom, other non-transport infrastructure. */
+ public static final int CMAS_CATEGORY_INFRA = 0x09;
+
+ /** CMAS alert category: Chem, bio, radiological, nuclear, high explosive threat or attack. */
+ public static final int CMAS_CATEGORY_CBRNE = 0x0a;
+
+ /** CMAS alert category: Other events. */
+ public static final int CMAS_CATEGORY_OTHER = 0x0b;
+
+ /**
+ * CMAS alert category is unknown. The category is only available for CDMA broadcasts
+ * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+ */
+ public static final int CMAS_CATEGORY_UNKNOWN = -1;
+
+ // CMAS response type (in CDMA type 1 elements record).
+
+ /** CMAS response type: Take shelter in place. */
+ public static final int CMAS_RESPONSE_TYPE_SHELTER = 0x00;
+
+ /** CMAS response type: Evacuate (Relocate). */
+ public static final int CMAS_RESPONSE_TYPE_EVACUATE = 0x01;
+
+ /** CMAS response type: Make preparations. */
+ public static final int CMAS_RESPONSE_TYPE_PREPARE = 0x02;
+
+ /** CMAS response type: Execute a pre-planned activity. */
+ public static final int CMAS_RESPONSE_TYPE_EXECUTE = 0x03;
+
+ /** CMAS response type: Attend to information sources. */
+ public static final int CMAS_RESPONSE_TYPE_MONITOR = 0x04;
+
+ /** CMAS response type: Avoid hazard. */
+ public static final int CMAS_RESPONSE_TYPE_AVOID = 0x05;
+
+ /** CMAS response type: Evaluate the information in this message (not for public warnings). */
+ public static final int CMAS_RESPONSE_TYPE_ASSESS = 0x06;
+
+ /** CMAS response type: No action recommended. */
+ public static final int CMAS_RESPONSE_TYPE_NONE = 0x07;
+
+ /**
+ * CMAS response type is unknown. The response type is only available for CDMA broadcasts
+ * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+ */
+ public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1;
+
+ // 4-bit CMAS severity (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+ /** CMAS severity type: Extraordinary threat to life or property. */
+ public static final int CMAS_SEVERITY_EXTREME = 0x0;
+
+ /** CMAS severity type: Significant threat to life or property. */
+ public static final int CMAS_SEVERITY_SEVERE = 0x1;
+
+ /**
+ * CMAS alert severity is unknown. The severity is available for CDMA warning alerts
+ * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+ * Presidential-level alert class (Korean Public Alert System Class 0).
+ */
+ public static final int CMAS_SEVERITY_UNKNOWN = -1;
+
+ // CMAS urgency (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+ /** CMAS urgency type: Responsive action should be taken immediately. */
+ public static final int CMAS_URGENCY_IMMEDIATE = 0x0;
+
+ /** CMAS urgency type: Responsive action should be taken within the next hour. */
+ public static final int CMAS_URGENCY_EXPECTED = 0x1;
+
+ /**
+ * CMAS alert urgency is unknown. The urgency is available for CDMA warning alerts
+ * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+ * Presidential-level alert class (Korean Public Alert System Class 0).
+ */
+ public static final int CMAS_URGENCY_UNKNOWN = -1;
+
+ // CMAS certainty (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+ /** CMAS certainty type: Determined to have occurred or to be ongoing. */
+ public static final int CMAS_CERTAINTY_OBSERVED = 0x0;
+
+ /** CMAS certainty type: Likely (probability > ~50%). */
+ public static final int CMAS_CERTAINTY_LIKELY = 0x1;
+
+ /**
+ * CMAS alert certainty is unknown. The certainty is available for CDMA warning alerts
+ * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+ * Presidential-level alert class (Korean Public Alert System Class 0).
+ */
+ public static final int CMAS_CERTAINTY_UNKNOWN = -1;
+
+ /** CMAS message class. */
+ private final int mMessageClass;
+
+ /** CMAS category. */
+ private final int mCategory;
+
+ /** CMAS response type. */
+ private final int mResponseType;
+
+ /** CMAS severity. */
+ private final int mSeverity;
+
+ /** CMAS urgency. */
+ private final int mUrgency;
+
+ /** CMAS certainty. */
+ private final int mCertainty;
+
+ /** Create a new SmsCbCmasInfo object with the specified values. */
+ public SmsCbCmasInfo(int messageClass, int category, int responseType, int severity,
+ int urgency, int certainty) {
+ mMessageClass = messageClass;
+ mCategory = category;
+ mResponseType = responseType;
+ mSeverity = severity;
+ mUrgency = urgency;
+ mCertainty = certainty;
+ }
+
+ /** Create a new SmsCbCmasInfo object from a Parcel. */
+ SmsCbCmasInfo(Parcel in) {
+ mMessageClass = in.readInt();
+ mCategory = in.readInt();
+ mResponseType = in.readInt();
+ mSeverity = in.readInt();
+ mUrgency = in.readInt();
+ mCertainty = in.readInt();
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageClass);
+ dest.writeInt(mCategory);
+ dest.writeInt(mResponseType);
+ dest.writeInt(mSeverity);
+ dest.writeInt(mUrgency);
+ dest.writeInt(mCertainty);
+ }
+
+ /**
+ * Returns the CMAS message class, e.g. {@link #CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT}.
+ * @return one of the {@code CMAS_CLASS} values
+ */
+ public int getMessageClass() {
+ return mMessageClass;
+ }
+
+ /**
+ * Returns the CMAS category, e.g. {@link #CMAS_CATEGORY_GEO}.
+ * @return one of the {@code CMAS_CATEGORY} values
+ */
+ public int getCategory() {
+ return mCategory;
+ }
+
+ /**
+ * Returns the CMAS response type, e.g. {@link #CMAS_RESPONSE_TYPE_SHELTER}.
+ * @return one of the {@code CMAS_RESPONSE_TYPE} values
+ */
+ public int getResponseType() {
+ return mResponseType;
+ }
+
+ /**
+ * Returns the CMAS severity, e.g. {@link #CMAS_SEVERITY_EXTREME}.
+ * @return one of the {@code CMAS_SEVERITY} values
+ */
+ public int getSeverity() {
+ return mSeverity;
+ }
+
+ /**
+ * Returns the CMAS urgency, e.g. {@link #CMAS_URGENCY_IMMEDIATE}.
+ * @return one of the {@code CMAS_URGENCY} values
+ */
+ public int getUrgency() {
+ return mUrgency;
+ }
+
+ /**
+ * Returns the CMAS certainty, e.g. {@link #CMAS_CERTAINTY_OBSERVED}.
+ * @return one of the {@code CMAS_CERTAINTY} values
+ */
+ public int getCertainty() {
+ return mCertainty;
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbCmasInfo{messageClass=" + mMessageClass + ", category=" + mCategory
+ + ", responseType=" + mResponseType + ", severity=" + mSeverity
+ + ", urgency=" + mUrgency + ", certainty=" + mCertainty + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Creator for unparcelling objects. */
+ public static final Parcelable.Creator<SmsCbCmasInfo>
+ CREATOR = new Parcelable.Creator<SmsCbCmasInfo>() {
+ @Override
+ public SmsCbCmasInfo createFromParcel(Parcel in) {
+ return new SmsCbCmasInfo(in);
+ }
+
+ @Override
+ public SmsCbCmasInfo[] newArray(int size) {
+ return new SmsCbCmasInfo[size];
+ }
+ };
+}
diff --git a/android/telephony/SmsCbEtwsInfo.java b/android/telephony/SmsCbEtwsInfo.java
new file mode 100644
index 00000000..14e02de2
--- /dev/null
+++ b/android/telephony/SmsCbEtwsInfo.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.Time;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.Arrays;
+
+/**
+ * Contains information elements for a GSM or UMTS ETWS warning notification.
+ * Supported values for each element are defined in 3GPP TS 23.041.
+ *
+ * {@hide}
+ */
+public class SmsCbEtwsInfo implements Parcelable {
+
+ /** ETWS warning type for earthquake. */
+ public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
+
+ /** ETWS warning type for tsunami. */
+ public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
+
+ /** ETWS warning type for earthquake and tsunami. */
+ public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
+
+ /** ETWS warning type for test messages. */
+ public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 0x03;
+
+ /** ETWS warning type for other emergency types. */
+ public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 0x04;
+
+ /** Unknown ETWS warning type. */
+ public static final int ETWS_WARNING_TYPE_UNKNOWN = -1;
+
+ /** One of the ETWS warning type constants defined in this class. */
+ private final int mWarningType;
+
+ /** Whether or not to activate the emergency user alert tone and vibration. */
+ private final boolean mEmergencyUserAlert;
+
+ /** Whether or not to activate a popup alert. */
+ private final boolean mActivatePopup;
+
+ /** Whether ETWS primary message or not/ */
+ private final boolean mPrimary;
+
+ /**
+ * 50-byte security information (ETWS primary notification for GSM only). As of Release 10,
+ * 3GPP TS 23.041 states that the UE shall ignore the ETWS primary notification timestamp
+ * and digital signature if received. Therefore it is treated as a raw byte array and
+ * parceled with the broadcast intent if present, but the timestamp is only computed if an
+ * application asks for the individual components.
+ */
+ private final byte[] mWarningSecurityInformation;
+
+ /** Create a new SmsCbEtwsInfo object with the specified values. */
+ public SmsCbEtwsInfo(int warningType, boolean emergencyUserAlert, boolean activatePopup,
+ boolean primary, byte[] warningSecurityInformation) {
+ mWarningType = warningType;
+ mEmergencyUserAlert = emergencyUserAlert;
+ mActivatePopup = activatePopup;
+ mPrimary = primary;
+ mWarningSecurityInformation = warningSecurityInformation;
+ }
+
+ /** Create a new SmsCbEtwsInfo object from a Parcel. */
+ SmsCbEtwsInfo(Parcel in) {
+ mWarningType = in.readInt();
+ mEmergencyUserAlert = (in.readInt() != 0);
+ mActivatePopup = (in.readInt() != 0);
+ mPrimary = (in.readInt() != 0);
+ mWarningSecurityInformation = in.createByteArray();
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mWarningType);
+ dest.writeInt(mEmergencyUserAlert ? 1 : 0);
+ dest.writeInt(mActivatePopup ? 1 : 0);
+ dest.writeInt(mPrimary ? 1 : 0);
+ dest.writeByteArray(mWarningSecurityInformation);
+ }
+
+ /**
+ * Returns the ETWS warning type.
+ * @return a warning type such as {@link #ETWS_WARNING_TYPE_EARTHQUAKE}
+ */
+ public int getWarningType() {
+ return mWarningType;
+ }
+
+ /**
+ * Returns the ETWS emergency user alert flag.
+ * @return true to notify terminal to activate emergency user alert; false otherwise
+ */
+ public boolean isEmergencyUserAlert() {
+ return mEmergencyUserAlert;
+ }
+
+ /**
+ * Returns the ETWS activate popup flag.
+ * @return true to notify terminal to activate display popup; false otherwise
+ */
+ public boolean isPopupAlert() {
+ return mActivatePopup;
+ }
+
+ /**
+ * Returns the ETWS format flag.
+ * @return true if the message is primary message, otherwise secondary message
+ */
+ public boolean isPrimary() {
+ return mPrimary;
+ }
+
+ /**
+ * Returns the Warning-Security-Information timestamp (GSM primary notifications only).
+ * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
+ * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present
+ */
+ public long getPrimaryNotificationTimestamp() {
+ if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
+ return 0;
+ }
+
+ int year = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[0]);
+ int month = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[1]);
+ int day = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[2]);
+ int hour = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[3]);
+ int minute = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[4]);
+ int second = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[5]);
+
+ // For the timezone, the most significant bit of the
+ // least significant nibble is the sign byte
+ // (meaning the max range of this field is 79 quarter-hours,
+ // which is more than enough)
+
+ byte tzByte = mWarningSecurityInformation[6];
+
+ // Mask out sign bit.
+ int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
+
+ timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
+
+ Time time = new Time(Time.TIMEZONE_UTC);
+
+ // We only need to support years above 2000.
+ time.year = year + 2000;
+ time.month = month - 1;
+ time.monthDay = day;
+ time.hour = hour;
+ time.minute = minute;
+ time.second = second;
+
+ // Timezone offset is in quarter hours.
+ return time.toMillis(true) - timezoneOffset * 15 * 60 * 1000;
+ }
+
+ /**
+ * Returns the digital signature (GSM primary notifications only). As of Release 10,
+ * 3GPP TS 23.041 states that the UE shall ignore this value if received.
+ * @return a byte array containing a copy of the primary notification digital signature
+ */
+ public byte[] getPrimaryNotificationSignature() {
+ if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 50) {
+ return null;
+ }
+ return Arrays.copyOfRange(mWarningSecurityInformation, 7, 50);
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbEtwsInfo{warningType=" + mWarningType + ", emergencyUserAlert="
+ + mEmergencyUserAlert + ", activatePopup=" + mActivatePopup + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Creator for unparcelling objects. */
+ public static final Creator<SmsCbEtwsInfo> CREATOR = new Creator<SmsCbEtwsInfo>() {
+ @Override
+ public SmsCbEtwsInfo createFromParcel(Parcel in) {
+ return new SmsCbEtwsInfo(in);
+ }
+
+ @Override
+ public SmsCbEtwsInfo[] newArray(int size) {
+ return new SmsCbEtwsInfo[size];
+ }
+ };
+}
diff --git a/android/telephony/SmsCbLocation.java b/android/telephony/SmsCbLocation.java
new file mode 100644
index 00000000..6eb72a86
--- /dev/null
+++ b/android/telephony/SmsCbLocation.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the location and geographical scope of a cell broadcast message.
+ * For GSM/UMTS, the Location Area and Cell ID are set when the broadcast
+ * geographical scope is cell wide or Location Area wide. For CDMA, the
+ * broadcast geographical scope is always PLMN wide.
+ *
+ * @hide
+ */
+public class SmsCbLocation implements Parcelable {
+
+ /** The PLMN. Note that this field may be an empty string, but isn't allowed to be null. */
+ private final String mPlmn;
+
+ private final int mLac;
+ private final int mCid;
+
+ /**
+ * Construct an empty location object. This is used for some test cases, and for
+ * cell broadcasts saved in older versions of the database without location info.
+ */
+ public SmsCbLocation() {
+ mPlmn = "";
+ mLac = -1;
+ mCid = -1;
+ }
+
+ /**
+ * Construct a location object for the PLMN. This class is immutable, so
+ * the same object can be reused for multiple broadcasts.
+ */
+ public SmsCbLocation(String plmn) {
+ mPlmn = plmn;
+ mLac = -1;
+ mCid = -1;
+ }
+
+ /**
+ * Construct a location object for the PLMN, LAC, and Cell ID. This class is immutable, so
+ * the same object can be reused for multiple broadcasts.
+ */
+ public SmsCbLocation(String plmn, int lac, int cid) {
+ mPlmn = plmn;
+ mLac = lac;
+ mCid = cid;
+ }
+
+ /**
+ * Initialize the object from a Parcel.
+ */
+ public SmsCbLocation(Parcel in) {
+ mPlmn = in.readString();
+ mLac = in.readInt();
+ mCid = in.readInt();
+ }
+
+ /**
+ * Returns the MCC/MNC of the network as a String.
+ * @return the PLMN identifier (MCC+MNC) as a String
+ */
+ public String getPlmn() {
+ return mPlmn;
+ }
+
+ /**
+ * Returns the GSM location area code, or UMTS service area code.
+ * @return location area code, -1 if unknown, 0xffff max legal value
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * Returns the GSM or UMTS cell ID.
+ * @return gsm cell id, -1 if unknown, 0xffff max legal value
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = mPlmn.hashCode();
+ hash = hash * 31 + mLac;
+ hash = hash * 31 + mCid;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null || !(o instanceof SmsCbLocation)) {
+ return false;
+ }
+ SmsCbLocation other = (SmsCbLocation) o;
+ return mPlmn.equals(other.mPlmn) && mLac == other.mLac && mCid == other.mCid;
+ }
+
+ @Override
+ public String toString() {
+ return '[' + mPlmn + ',' + mLac + ',' + mCid + ']';
+ }
+
+ /**
+ * Test whether this location is within the location area of the specified object.
+ *
+ * @param area the location area to compare with this location
+ * @return true if this location is contained within the specified location area
+ */
+ public boolean isInLocationArea(SmsCbLocation area) {
+ if (mCid != -1 && mCid != area.mCid) {
+ return false;
+ }
+ if (mLac != -1 && mLac != area.mLac) {
+ return false;
+ }
+ return mPlmn.equals(area.mPlmn);
+ }
+
+ /**
+ * Test whether this location is within the location area of the CellLocation.
+ *
+ * @param plmn the PLMN to use for comparison
+ * @param lac the Location Area (GSM) or Service Area (UMTS) to compare with
+ * @param cid the Cell ID to compare with
+ * @return true if this location is contained within the specified PLMN, LAC, and Cell ID
+ */
+ public boolean isInLocationArea(String plmn, int lac, int cid) {
+ if (!mPlmn.equals(plmn)) {
+ return false;
+ }
+
+ if (mLac != -1 && mLac != lac) {
+ return false;
+ }
+
+ if (mCid != -1 && mCid != cid) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mPlmn);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ }
+
+ public static final Parcelable.Creator<SmsCbLocation> CREATOR
+ = new Parcelable.Creator<SmsCbLocation>() {
+ @Override
+ public SmsCbLocation createFromParcel(Parcel in) {
+ return new SmsCbLocation(in);
+ }
+
+ @Override
+ public SmsCbLocation[] newArray(int size) {
+ return new SmsCbLocation[size];
+ }
+ };
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/android/telephony/SmsCbMessage.java b/android/telephony/SmsCbMessage.java
new file mode 100644
index 00000000..046bf8c7
--- /dev/null
+++ b/android/telephony/SmsCbMessage.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object containing a received cell broadcast message. There are four different types
+ * of Cell Broadcast messages:
+ *
+ * <ul>
+ * <li>opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores</li>
+ * <li>cell information messages, broadcast on channel 50, indicating the current cell name for
+ * roaming purposes (required to display on the idle screen in Brazil)</li>
+ * <li>emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)</li>
+ * <li>emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)</li>
+ * </ul>
+ *
+ * <p>There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only),
+ * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were
+ * unified under a common name, avoiding some names, such as "Message Identifier", that refer to
+ * two completely different concepts in 3GPP and CDMA.
+ *
+ * <p>The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name
+ * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP
+ * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the
+ * application should
+ *
+ * <p>The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used
+ * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is
+ * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit
+ * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number
+ * are considered unique to the PLMN, to the current cell, or to the current Location Area (or
+ * Service Area in UMTS). The relevant values are concatenated into a single String which will be
+ * unique if the messages are not duplicates.
+ *
+ * <p>The SMS dispatcher does not detect duplicate messages. However, it does concatenate the
+ * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object.
+ *
+ * <p>Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive
+ * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts.
+ * Only system applications such as the CellBroadcastReceiver may receive notifications for
+ * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or
+ * interference with the immediate display of the alert message and playing of the alert sound and
+ * vibration pattern, which could be caused by poorly written or malicious non-system code.
+ *
+ * @hide
+ */
+public class SmsCbMessage implements Parcelable {
+
+ protected static final String LOG_TAG = "SMSCB";
+
+ /** Cell wide geographical scope with immediate display (GSM/UMTS only). */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+ /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */
+ public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+ /** Location / service area wide geographical scope (GSM/UMTS only). */
+ public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+ /** Cell wide geographical scope (GSM/UMTS only). */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+ /** GSM or UMTS format cell broadcast. */
+ public static final int MESSAGE_FORMAT_3GPP = 1;
+
+ /** CDMA format cell broadcast. */
+ public static final int MESSAGE_FORMAT_3GPP2 = 2;
+
+ /** Normal message priority. */
+ public static final int MESSAGE_PRIORITY_NORMAL = 0;
+
+ /** Interactive message priority. */
+ public static final int MESSAGE_PRIORITY_INTERACTIVE = 1;
+
+ /** Urgent message priority. */
+ public static final int MESSAGE_PRIORITY_URGENT = 2;
+
+ /** Emergency message priority. */
+ public static final int MESSAGE_PRIORITY_EMERGENCY = 3;
+
+ /** Format of this message (for interpretation of service category values). */
+ private final int mMessageFormat;
+
+ /** Geographical scope of broadcast. */
+ private final int mGeographicalScope;
+
+ /**
+ * Serial number of broadcast (message identifier for CDMA, geographical scope + message code +
+ * update number for GSM/UMTS). The serial number plus the location code uniquely identify
+ * a cell broadcast for duplicate detection.
+ */
+ private final int mSerialNumber;
+
+ /**
+ * Location identifier for this message. It consists of the current operator MCC/MNC as a
+ * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+ * message is not binary 01, the Location Area is included for comparison. If the GS is
+ * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified.
+ */
+ private final SmsCbLocation mLocation;
+
+ /**
+ * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings,
+ * the information provided by the category is also available via {@link #getEtwsWarningInfo()}
+ * or {@link #getCmasWarningInfo()}.
+ */
+ private final int mServiceCategory;
+
+ /** Message language, as a two-character string, e.g. "en". */
+ private final String mLanguage;
+
+ /** Message body, as a String. */
+ private final String mBody;
+
+ /** Message priority (including emergency priority). */
+ private final int mPriority;
+
+ /** ETWS warning notification information (ETWS warnings only). */
+ private final SmsCbEtwsInfo mEtwsWarningInfo;
+
+ /** CMAS warning notification information (CMAS warnings only). */
+ private final SmsCbCmasInfo mCmasWarningInfo;
+
+ /**
+ * Create a new SmsCbMessage with the specified data.
+ */
+ public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+ SmsCbLocation location, int serviceCategory, String language, String body,
+ int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo) {
+ mMessageFormat = messageFormat;
+ mGeographicalScope = geographicalScope;
+ mSerialNumber = serialNumber;
+ mLocation = location;
+ mServiceCategory = serviceCategory;
+ mLanguage = language;
+ mBody = body;
+ mPriority = priority;
+ mEtwsWarningInfo = etwsWarningInfo;
+ mCmasWarningInfo = cmasWarningInfo;
+ }
+
+ /** Create a new SmsCbMessage object from a Parcel. */
+ public SmsCbMessage(Parcel in) {
+ mMessageFormat = in.readInt();
+ mGeographicalScope = in.readInt();
+ mSerialNumber = in.readInt();
+ mLocation = new SmsCbLocation(in);
+ mServiceCategory = in.readInt();
+ mLanguage = in.readString();
+ mBody = in.readString();
+ mPriority = in.readInt();
+ int type = in.readInt();
+ switch (type) {
+ case 'E':
+ // unparcel ETWS warning information
+ mEtwsWarningInfo = new SmsCbEtwsInfo(in);
+ mCmasWarningInfo = null;
+ break;
+
+ case 'C':
+ // unparcel CMAS warning information
+ mEtwsWarningInfo = null;
+ mCmasWarningInfo = new SmsCbCmasInfo(in);
+ break;
+
+ default:
+ mEtwsWarningInfo = null;
+ mCmasWarningInfo = null;
+ }
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageFormat);
+ dest.writeInt(mGeographicalScope);
+ dest.writeInt(mSerialNumber);
+ mLocation.writeToParcel(dest, flags);
+ dest.writeInt(mServiceCategory);
+ dest.writeString(mLanguage);
+ dest.writeString(mBody);
+ dest.writeInt(mPriority);
+ if (mEtwsWarningInfo != null) {
+ // parcel ETWS warning information
+ dest.writeInt('E');
+ mEtwsWarningInfo.writeToParcel(dest, flags);
+ } else if (mCmasWarningInfo != null) {
+ // parcel CMAS warning information
+ dest.writeInt('C');
+ mCmasWarningInfo.writeToParcel(dest, flags);
+ } else {
+ // no ETWS or CMAS warning information
+ dest.writeInt('0');
+ }
+ }
+
+ public static final Parcelable.Creator<SmsCbMessage> CREATOR
+ = new Parcelable.Creator<SmsCbMessage>() {
+ @Override
+ public SmsCbMessage createFromParcel(Parcel in) {
+ return new SmsCbMessage(in);
+ }
+
+ @Override
+ public SmsCbMessage[] newArray(int size) {
+ return new SmsCbMessage[size];
+ }
+ };
+
+ /**
+ * Return the geographical scope of this message (GSM/UMTS only).
+ *
+ * @return Geographical scope
+ */
+ public int getGeographicalScope() {
+ return mGeographicalScope;
+ }
+
+ /**
+ * Return the broadcast serial number of broadcast (message identifier for CDMA, or
+ * geographical scope + message code + update number for GSM/UMTS). The serial number plus
+ * the location code uniquely identify a cell broadcast for duplicate detection.
+ *
+ * @return the 16-bit CDMA message identifier or GSM/UMTS serial number
+ */
+ public int getSerialNumber() {
+ return mSerialNumber;
+ }
+
+ /**
+ * Return the location identifier for this message, consisting of the MCC/MNC as a
+ * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+ * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the
+ * cell ID is also included. The {@link SmsCbLocation} object includes a method to test
+ * if the location is included within another location area or within a PLMN and CellLocation.
+ *
+ * @return the geographical location code for duplicate message detection
+ */
+ public SmsCbLocation getLocation() {
+ return mLocation;
+ }
+
+ /**
+ * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation
+ * of the category is radio technology specific. For ETWS and CMAS warnings, the information
+ * provided by the category is available via {@link #getEtwsWarningInfo()} or
+ * {@link #getCmasWarningInfo()} in a radio technology independent format.
+ *
+ * @return the radio technology specific service category
+ */
+ public int getServiceCategory() {
+ return mServiceCategory;
+ }
+
+ /**
+ * Get the ISO-639-1 language code for this message, or null if unspecified
+ *
+ * @return Language code
+ */
+ public String getLanguageCode() {
+ return mLanguage;
+ }
+
+ /**
+ * Get the body of this message, or null if no body available
+ *
+ * @return Body, or null
+ */
+ public String getMessageBody() {
+ return mBody;
+ }
+
+ /**
+ * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
+ * @return an integer representing 3GPP or 3GPP2 message format
+ */
+ public int getMessageFormat() {
+ return mMessageFormat;
+ }
+
+ /**
+ * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL}
+ * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return
+ * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}.
+ * @return an integer representing the message priority
+ */
+ public int getMessagePriority() {
+ return mPriority;
+ }
+
+ /**
+ * If this is an ETWS warning notification then this method will return an object containing
+ * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an
+ * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte
+ * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the
+ * ETWS primary notification timestamp and digital signature if received.
+ *
+ * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification
+ */
+ public SmsCbEtwsInfo getEtwsWarningInfo() {
+ return mEtwsWarningInfo;
+ }
+
+ /**
+ * If this is a CMAS warning notification then this method will return an object containing
+ * the CMAS message class, category, response type, severity, urgency and certainty.
+ * The message class is always present. Severity, urgency and certainty are present for CDMA
+ * warning notifications containing a type 1 elements record and for GSM and UMTS warnings
+ * except for the Presidential-level alert category. Category and response type are only
+ * available for CDMA notifications containing a type 1 elements record.
+ *
+ * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification
+ */
+ public SmsCbCmasInfo getCmasWarningInfo() {
+ return mCmasWarningInfo;
+ }
+
+ /**
+ * Return whether this message is an emergency (PWS) message type.
+ * @return true if the message is a public warning notification; false otherwise
+ */
+ public boolean isEmergencyMessage() {
+ return mPriority == MESSAGE_PRIORITY_EMERGENCY;
+ }
+
+ /**
+ * Return whether this message is an ETWS warning alert.
+ * @return true if the message is an ETWS warning notification; false otherwise
+ */
+ public boolean isEtwsMessage() {
+ return mEtwsWarningInfo != null;
+ }
+
+ /**
+ * Return whether this message is a CMAS warning alert.
+ * @return true if the message is a CMAS warning notification; false otherwise
+ */
+ public boolean isCmasMessage() {
+ return mCmasWarningInfo != null;
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber="
+ + mSerialNumber + ", location=" + mLocation + ", serviceCategory="
+ + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
+ + ", priority=" + mPriority
+ + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
+ + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/android/telephony/SmsManager.java b/android/telephony/SmsManager.java
new file mode 100644
index 00000000..6029995f
--- /dev/null
+++ b/android/telephony/SmsManager.java
@@ -0,0 +1,1714 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.ActivityThread;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.telephony.IMms;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.SmsRawData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/*
+ * TODO(code review): Curious question... Why are a lot of these
+ * methods not declared as static, since they do not seem to require
+ * any local object state? Presumably this cannot be changed without
+ * interfering with the API...
+ */
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method {@link #getDefault()}.
+ *
+ * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19)
+ * and higher, see {@link android.provider.Telephony}.
+ */
+public final class SmsManager {
+ private static final String TAG = "SmsManager";
+ /**
+ * A psuedo-subId that represents the default subId at any given time. The actual subId it
+ * represents changes as the default subId is changed.
+ */
+ private static final int DEFAULT_SUBSCRIPTION_ID = -1002;
+
+ /** Singleton object constructed during class initialization. */
+ private static final SmsManager sInstance = new SmsManager(DEFAULT_SUBSCRIPTION_ID);
+ private static final Object sLockObject = new Object();
+
+ /** @hide */
+ public static final int CELL_BROADCAST_RAN_TYPE_GSM = 0;
+ /** @hide */
+ public static final int CELL_BROADCAST_RAN_TYPE_CDMA = 1;
+
+ /** SMS record length from TS 51.011 10.5.3
+ * @hide
+ */
+ public static final int SMS_RECORD_LENGTH = 176;
+
+ /** SMS record length from C.S0023 3.4.27
+ * @hide
+ */
+ public static final int CDMA_SMS_RECORD_LENGTH = 255;
+
+ private static final Map<Integer, SmsManager> sSubInstances =
+ new ArrayMap<Integer, SmsManager>();
+
+ /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */
+ private int mSubId;
+
+ /*
+ * Key for the various carrier-dependent configuration values.
+ * Some of the values are used by the system in processing SMS or MMS messages. Others
+ * are provided for the convenience of SMS applications.
+ */
+
+ /**
+ * Whether to append transaction id to MMS WAP Push M-Notification.ind's content location URI
+ * when constructing the download URL of a new MMS (boolean type)
+ */
+ public static final String MMS_CONFIG_APPEND_TRANSACTION_ID =
+ CarrierConfigManager.KEY_MMS_APPEND_TRANSACTION_ID_BOOL;
+ /**
+ * Whether MMS is enabled for the current carrier (boolean type)
+ */
+ public static final String
+ MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL;
+ /**
+ * Whether group MMS is enabled for the current carrier (boolean type)
+ */
+ public static final String
+ MMS_CONFIG_GROUP_MMS_ENABLED = CarrierConfigManager.KEY_MMS_GROUP_MMS_ENABLED_BOOL;
+ /**
+ * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location instead
+ * of the default MMSC (boolean type)
+ */
+ public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED =
+ CarrierConfigManager.KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL;
+ /**
+ * Whether alias is enabled (boolean type)
+ */
+ public static final String
+ MMS_CONFIG_ALIAS_ENABLED = CarrierConfigManager.KEY_MMS_ALIAS_ENABLED_BOOL;
+ /**
+ * Whether audio is allowed to be attached for MMS messages (boolean type)
+ */
+ public static final String
+ MMS_CONFIG_ALLOW_ATTACH_AUDIO = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL;
+ /**
+ * Whether multipart SMS is enabled (boolean type)
+ */
+ public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED =
+ CarrierConfigManager.KEY_MMS_MULTIPART_SMS_ENABLED_BOOL;
+ /**
+ * Whether SMS delivery report is enabled (boolean type)
+ */
+ public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED =
+ CarrierConfigManager.KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL;
+ /**
+ * Whether content-disposition field should be expected in an MMS PDU (boolean type)
+ */
+ public static final String MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION =
+ CarrierConfigManager.KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL;
+ /**
+ * Whether multipart SMS should be sent as separate messages
+ */
+ public static final String MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES =
+ CarrierConfigManager.KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL;
+ /**
+ * Whether MMS read report is enabled (boolean type)
+ */
+ public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED =
+ CarrierConfigManager.KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL;
+ /**
+ * Whether MMS delivery report is enabled (boolean type)
+ */
+ public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED =
+ CarrierConfigManager.KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL;
+ /**
+ * Max MMS message size in bytes (int type)
+ */
+ public static final String
+ MMS_CONFIG_MAX_MESSAGE_SIZE = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT;
+ /**
+ * Max MMS image width (int type)
+ */
+ public static final String
+ MMS_CONFIG_MAX_IMAGE_WIDTH = CarrierConfigManager.KEY_MMS_MAX_IMAGE_WIDTH_INT;
+ /**
+ * Max MMS image height (int type)
+ */
+ public static final String
+ MMS_CONFIG_MAX_IMAGE_HEIGHT = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT;
+ /**
+ * Limit of recipients of MMS messages (int type)
+ */
+ public static final String
+ MMS_CONFIG_RECIPIENT_LIMIT = CarrierConfigManager.KEY_MMS_RECIPIENT_LIMIT_INT;
+ /**
+ * Min alias character count (int type)
+ */
+ public static final String
+ MMS_CONFIG_ALIAS_MIN_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT;
+ /**
+ * Max alias character count (int type)
+ */
+ public static final String
+ MMS_CONFIG_ALIAS_MAX_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MAX_CHARS_INT;
+ /**
+ * When the number of parts of a multipart SMS reaches this threshold, it should be converted
+ * into an MMS (int type)
+ */
+ public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD =
+ CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT;
+ /**
+ * Some carriers require SMS to be converted into MMS when text length reaches this threshold
+ * (int type)
+ */
+ public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD =
+ CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT;
+ /**
+ * Max message text size (int type)
+ */
+ public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE =
+ CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT;
+ /**
+ * Max message subject length (int type)
+ */
+ public static final String
+ MMS_CONFIG_SUBJECT_MAX_LENGTH = CarrierConfigManager.KEY_MMS_SUBJECT_MAX_LENGTH_INT;
+ /**
+ * MMS HTTP socket timeout in milliseconds (int type)
+ */
+ public static final String
+ MMS_CONFIG_HTTP_SOCKET_TIMEOUT = CarrierConfigManager.KEY_MMS_HTTP_SOCKET_TIMEOUT_INT;
+ /**
+ * The name of the UA Prof URL HTTP header for MMS HTTP request (String type)
+ */
+ public static final String
+ MMS_CONFIG_UA_PROF_TAG_NAME = CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING;
+ /**
+ * The User-Agent header value for MMS HTTP request (String type)
+ */
+ public static final String
+ MMS_CONFIG_USER_AGENT = CarrierConfigManager.KEY_MMS_USER_AGENT_STRING;
+ /**
+ * The UA Profile URL header value for MMS HTTP request (String type)
+ */
+ public static final String
+ MMS_CONFIG_UA_PROF_URL = CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING;
+ /**
+ * A list of HTTP headers to add to MMS HTTP request, separated by "|" (String type)
+ */
+ public static final String
+ MMS_CONFIG_HTTP_PARAMS = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING;
+ /**
+ * Email gateway number (String type)
+ */
+ public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER =
+ CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING;
+ /**
+ * The suffix to append to the NAI header value for MMS HTTP request (String type)
+ */
+ public static final String
+ MMS_CONFIG_NAI_SUFFIX = CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING;
+ /**
+ * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers don't want
+ * this shown. (Boolean type)
+ */
+ public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS =
+ CarrierConfigManager.KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL;
+ /**
+ * Whether the carrier MMSC supports charset field in Content-Type header. If this is false,
+ * then we don't add "charset" to "Content-Type"
+ */
+ public static final String MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER =
+ CarrierConfigManager.KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL;
+ /**
+ * If true, add "Connection: close" header to MMS HTTP requests so the connection
+ * is immediately closed (disabling keep-alive). (Boolean type)
+ * @hide
+ */
+ public static final String MMS_CONFIG_CLOSE_CONNECTION =
+ CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL;
+
+ /*
+ * Forwarded constants from SimDialogActivity.
+ */
+ private static String DIALOG_TYPE_KEY = "dialog_type";
+ private static final int SMS_PICK = 2;
+
+ /**
+ * Send a text based SMS.
+ *
+ * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+ * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+ *
+ * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+ * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+ * writes messages sent using this method to the SMS Provider (the default SMS app is always
+ * responsible for writing its sent messages to the SMS Provider). For information about
+ * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+ *
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ */
+ public void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ true /* persistMessage*/);
+ }
+
+ private void sendTextMessageInternal(String destinationAddress, String scAddress,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+ boolean persistMessage) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
+ destinationAddress,
+ scAddress, text, sentIntent, deliveryIntent,
+ persistMessage);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Send a text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+ * privileges.
+ * </p>
+ *
+ * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void sendTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ false /* persistMessage */);
+ }
+
+ /**
+ * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is
+ * for internal use only.
+ *
+ * @param persistMessage whether to persist the sent message in the SMS app. the caller must be
+ * the Phone process if set to false.
+ *
+ * @hide
+ */
+ public void sendTextMessageWithSelfPermissions(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(),
+ ActivityThread.currentPackageName(),
+ destinationAddress,
+ scAddress, text, sentIntent, deliveryIntent, persistMessage);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Inject an SMS PDU into the android application framework.
+ *
+ * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+ * privileges. @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ *
+ * @param pdu is the byte array of pdu to be injected into android application framework
+ * @param format is the format of SMS pdu (3gpp or 3gpp2)
+ * @param receivedIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully received by the
+ * android application framework, or failed. This intent is broadcasted at
+ * the same time an SMS received from radio is acknowledged back.
+ * The result code will be <code>RESULT_SMS_HANDLED</code> for success, or
+ * <code>RESULT_SMS_GENERIC_ERROR</code> for error.
+ *
+ * @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2.
+ */
+ public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
+ if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
+ // Format must be either 3gpp or 3gpp2.
+ throw new IllegalArgumentException(
+ "Invalid pdu format. format must be either 3gpp or 3gpp2");
+ }
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ iccISms.injectSmsPduForSubscriber(
+ getSubscriptionId(), pdu, format, receivedIntent);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Divide a message text into several fragments, none bigger than
+ * the maximum SMS message size.
+ *
+ * @param text the original message. Must not be null.
+ * @return an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ *
+ * @throws IllegalArgumentException if text is null
+ */
+ public ArrayList<String> divideMessage(String text) {
+ if (null == text) {
+ throw new IllegalArgumentException("text is null");
+ }
+ return SmsMessage.fragmentText(text);
+ }
+
+ /**
+ * Send a multi-part text based SMS. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * <code>divideMessage</code>.
+ *
+ * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+ * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+ *
+ * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+ * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+ * writes messages sent using this method to the SMS Provider (the default SMS app is always
+ * responsible for writing its sent messages to the SMS Provider). For information about
+ * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ */
+ public void sendMultipartTextMessage(
+ String destinationAddress, String scAddress, ArrayList<String> parts,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, true /* persistMessage*/);
+ }
+
+ private void sendMultipartTextMessageInternal(
+ String destinationAddress, String scAddress, List<String> parts,
+ List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+ boolean persistMessage) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+ if (parts == null || parts.size() < 1) {
+ throw new IllegalArgumentException("Invalid message body");
+ }
+
+ if (parts.size() > 1) {
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendMultipartTextForSubscriber(getSubscriptionId(),
+ ActivityThread.currentPackageName(),
+ destinationAddress, scAddress, parts,
+ sentIntents, deliveryIntents, persistMessage);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ } else {
+ PendingIntent sentIntent = null;
+ PendingIntent deliveryIntent = null;
+ if (sentIntents != null && sentIntents.size() > 0) {
+ sentIntent = sentIntents.get(0);
+ }
+ if (deliveryIntents != null && deliveryIntents.size() > 0) {
+ deliveryIntent = deliveryIntents.get(0);
+ }
+ sendTextMessage(destinationAddress, scAddress, parts.get(0),
+ sentIntent, deliveryIntent);
+ }
+ }
+
+ /**
+ * Send a multi-part text based SMS without writing it into the SMS Provider.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+ * privileges.
+ * </p>
+ *
+ * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+ * @hide
+ **/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void sendMultipartTextMessageWithoutPersisting(
+ String destinationAddress, String scAddress, List<String> parts,
+ List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
+ sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, false /* persistMessage*/);
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+ * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param destinationPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ */
+ public void sendDataMessage(
+ String destinationAddress, String scAddress, short destinationPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (data == null || data.length == 0) {
+ throw new IllegalArgumentException("Invalid message data");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendDataForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
+ destinationAddress, scAddress, destinationPort & 0xFFFF,
+ data, sentIntent, deliveryIntent);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is
+ * for internal use only.
+ *
+ * @hide
+ */
+ public void sendDataMessageWithSelfPermissions(
+ String destinationAddress, String scAddress, short destinationPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ if (TextUtils.isEmpty(destinationAddress)) {
+ throw new IllegalArgumentException("Invalid destinationAddress");
+ }
+
+ if (data == null || data.length == 0) {
+ throw new IllegalArgumentException("Invalid message data");
+ }
+
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress, scAddress,
+ destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+
+
+ /**
+ * Get the SmsManager associated with the default subscription id. The instance will always be
+ * associated with the default subscription id, even if the default subscription id is changed.
+ *
+ * @return the SmsManager associated with the default subscription id
+ */
+ public static SmsManager getDefault() {
+ return sInstance;
+ }
+
+ /**
+ * Get the the instance of the SmsManager associated with a particular subscription id
+ *
+ * @param subId an SMS subscription id, typically accessed using
+ * {@link android.telephony.SubscriptionManager}
+ * @return the instance of the SmsManager associated with subId
+ */
+ public static SmsManager getSmsManagerForSubscriptionId(int subId) {
+ // TODO(shri): Add javadoc link once SubscriptionManager is made public api
+ synchronized(sLockObject) {
+ SmsManager smsManager = sSubInstances.get(subId);
+ if (smsManager == null) {
+ smsManager = new SmsManager(subId);
+ sSubInstances.put(subId, smsManager);
+ }
+ return smsManager;
+ }
+ }
+
+ private SmsManager(int subId) {
+ mSubId = subId;
+ }
+
+ /**
+ * Get the associated subscription id. If the instance was returned by {@link #getDefault()},
+ * then this method may return different values at different points in time (if the user
+ * changes the default subscription id). It will return < 0 if the default subscription id
+ * cannot be determined.
+ *
+ * Additionally, to support legacy applications that are not multi-SIM aware,
+ * if the following are true:
+ * - We are using a multi-SIM device
+ * - A default SMS SIM has not been selected
+ * - At least one SIM subscription is available
+ * then ask the user to set the default SMS SIM.
+ *
+ * @return associated subscription id
+ */
+ public int getSubscriptionId() {
+ final int subId = (mSubId == DEFAULT_SUBSCRIPTION_ID)
+ ? getDefaultSmsSubscriptionId() : mSubId;
+ boolean isSmsSimPickActivityNeeded = false;
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ isSmsSimPickActivityNeeded = iccISms.isSmsSimPickActivityNeeded(subId);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Exception in getSubscriptionId");
+ }
+
+ if (isSmsSimPickActivityNeeded) {
+ Log.d(TAG, "getSubscriptionId isSmsSimPickActivityNeeded is true");
+ // ask the user for a default SMS SIM.
+ Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.sim.SimDialogActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK);
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException anfe) {
+ // If Settings is not installed, only log the error as we do not want to break
+ // legacy applications.
+ Log.e(TAG, "Unable to launch Settings application.");
+ }
+ }
+
+ return subId;
+ }
+
+ /**
+ * Returns the ISms service, or throws an UnsupportedOperationException if
+ * the service does not exist.
+ */
+ private static ISms getISmsServiceOrThrow() {
+ ISms iccISms = getISmsService();
+ if (iccISms == null) {
+ throw new UnsupportedOperationException("Sms is not supported");
+ }
+ return iccISms;
+ }
+
+ private static ISms getISmsService() {
+ return ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ }
+
+ /**
+ * Copy a raw SMS PDU to the ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param smsc the SMSC for this message, or NULL for the default SMSC
+ * @param pdu the raw PDU to store
+ * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
+ * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
+ * @return true for success
+ *
+ * @throws IllegalArgumentException if pdu is NULL
+ * {@hide}
+ */
+ public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
+ boolean success = false;
+
+ if (null == pdu) {
+ throw new IllegalArgumentException("pdu is NULL");
+ }
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.copyMessageToIccEfForSubscriber(getSubscriptionId(),
+ ActivityThread.currentPackageName(),
+ status, pdu, smsc);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Delete the specified message from the ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param messageIndex is the record index of the message on ICC
+ * @return true for success
+ *
+ * {@hide}
+ */
+ public boolean
+ deleteMessageFromIcc(int messageIndex) {
+ boolean success = false;
+ byte[] pdu = new byte[SMS_RECORD_LENGTH-1];
+ Arrays.fill(pdu, (byte)0xff);
+
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
+ ActivityThread.currentPackageName(),
+ messageIndex, STATUS_ON_ICC_FREE, pdu);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Update the specified message on the ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @param messageIndex record index of message to update
+ * @param newStatus new message status (STATUS_ON_ICC_READ,
+ * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
+ * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
+ * @param pdu the raw PDU to store
+ * @return true for success
+ *
+ * {@hide}
+ */
+ public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
+ ActivityThread.currentPackageName(),
+ messageIndex, newStatus, pdu);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Retrieves all messages currently stored on ICC.
+ * ICC (Integrated Circuit Card) is the card of the device.
+ * For example, this can be the SIM or USIM for GSM.
+ *
+ * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+ *
+ * {@hide}
+ */
+ public ArrayList<SmsMessage> getAllMessagesFromIcc() {
+ List<SmsRawData> records = null;
+
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ records = iccISms.getAllMessagesFromIccEfForSubscriber(
+ getSubscriptionId(),
+ ActivityThread.currentPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return createMessageListFromRawRecords(records);
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier and RAN type. The RAN type specify this message ID
+ * belong to 3GPP (GSM) or 3GPP2(CDMA).Note that if two different clients
+ * enable the same message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param ranType as defined in class SmsManager, the value can be one of these:
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcast(int, int)
+ *
+ * {@hide}
+ */
+ public boolean enableCellBroadcast(int messageIdentifier, int ranType) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcastForSubscriber(
+ getSubscriptionId(), messageIdentifier, ranType);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier and RAN type. The RAN type specify this message ID
+ * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
+ * enable the same message identifier, they must both disable it for the
+ * device to stop receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param ranType as defined in class SmsManager, the value can be one of these:
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcast(int, int)
+ *
+ * {@hide}
+ */
+ public boolean disableCellBroadcast(int messageIdentifier, int ranType) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcastForSubscriber(
+ getSubscriptionId(), messageIdentifier, ranType);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range and RAN type. The RAN type specify this message ID
+ * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable
+ * the same message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param ranType as defined in class SmsManager, the value can be one of these:
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcastRange(int, int, int)
+ *
+ * @throws IllegalArgumentException if endMessageId < startMessageId
+ * {@hide}
+ */
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) {
+ boolean success = false;
+
+ if (endMessageId < startMessageId) {
+ throw new IllegalArgumentException("endMessageId < startMessageId");
+ }
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(),
+ startMessageId, endMessageId, ranType);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range and RAN type. The RAN type specify this message
+ * ID range belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different
+ * clients enable the same message identifier, they must both disable it for
+ * the device to stop receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
+ * or C.R1001-G (3GPP2)
+ * @param ranType as defined in class SmsManager, the value can be one of these:
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
+ * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcastRange(int, int, int)
+ *
+ * @throws IllegalArgumentException if endMessageId < startMessageId
+ * {@hide}
+ */
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) {
+ boolean success = false;
+
+ if (endMessageId < startMessageId) {
+ throw new IllegalArgumentException("endMessageId < startMessageId");
+ }
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(),
+ startMessageId, endMessageId, ranType);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
+ * records returned by <code>getAllMessagesFromIcc()</code>
+ *
+ * @param records SMS EF records, returned by
+ * <code>getAllMessagesFromIcc</code>
+ * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
+ */
+ private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+ ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
+ if (records != null) {
+ int count = records.size();
+ for (int i = 0; i < count; i++) {
+ SmsRawData data = records.get(i);
+ // List contains all records, including "free" records (null)
+ if (data != null) {
+ SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+ if (sms != null) {
+ messages.add(sms);
+ }
+ }
+ }
+ }
+ return messages;
+ }
+
+ /**
+ * SMS over IMS is supported if IMS is registered and SMS is supported
+ * on IMS.
+ *
+ * @return true if SMS over IMS is supported, false otherwise
+ *
+ * @see #getImsSmsFormat()
+ *
+ * @hide
+ */
+ public boolean isImsSmsSupported() {
+ boolean boSupported = false;
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ boSupported = iccISms.isImsSmsSupportedForSubscriber(getSubscriptionId());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return boSupported;
+ }
+
+ /**
+ * Gets SMS format supported on IMS. SMS over IMS format is
+ * either 3GPP or 3GPP2.
+ *
+ * @return SmsMessage.FORMAT_3GPP,
+ * SmsMessage.FORMAT_3GPP2
+ * or SmsMessage.FORMAT_UNKNOWN
+ *
+ * @see #isImsSmsSupported()
+ *
+ * @hide
+ */
+ public String getImsSmsFormat() {
+ String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
+ try {
+ ISms iccISms = getISmsService();
+ if (iccISms != null) {
+ format = iccISms.getImsSmsFormatForSubscriber(getSubscriptionId());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return format;
+ }
+
+ /**
+ * Get default sms subscription id
+ *
+ * @return the default SMS subscription id
+ */
+ public static int getDefaultSmsSubscriptionId() {
+ ISms iccISms = null;
+ try {
+ iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ return iccISms.getPreferredSmsSubscription();
+ } catch (RemoteException ex) {
+ return -1;
+ } catch (NullPointerException ex) {
+ return -1;
+ }
+ }
+
+ /**
+ * Get SMS prompt property, enabled or not
+ *
+ * @return true if enabled, false otherwise
+ * @hide
+ */
+ public boolean isSMSPromptEnabled() {
+ ISms iccISms = null;
+ try {
+ iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ return iccISms.isSMSPromptEnabled();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ // see SmsMessage.getStatusOnIcc
+
+ /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_FREE = 0;
+
+ /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_READ = 1;
+
+ /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_UNREAD = 3;
+
+ /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_SENT = 5;
+
+ /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+ static public final int STATUS_ON_ICC_UNSENT = 7;
+
+ // SMS send failure result codes
+
+ /** Generic failure cause */
+ static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
+ /** Failed because radio was explicitly turned off */
+ static public final int RESULT_ERROR_RADIO_OFF = 2;
+ /** Failed because no pdu provided */
+ static public final int RESULT_ERROR_NULL_PDU = 3;
+ /** Failed because service is currently unavailable */
+ static public final int RESULT_ERROR_NO_SERVICE = 4;
+ /** Failed because we reached the sending queue limit. */
+ static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5;
+ /** Failed because FDN is enabled. {@hide} */
+ static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6;
+ /** Failed because user denied the sending of this short code. */
+ static public final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
+ /** Failed because the user has denied this app ever send premium short codes. */
+ static public final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+
+ static private final String PHONE_PACKAGE_NAME = "com.android.phone";
+
+ /**
+ * Send an MMS message
+ *
+ * @param context application context
+ * @param contentUri the content Uri from which the message pdu will be read
+ * @param locationUrl the optional location url where message should be sent to
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * sending the message.
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed
+ * @throws IllegalArgumentException if contentUri is empty
+ */
+ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
+ Bundle configOverrides, PendingIntent sentIntent) {
+ if (contentUri == null) {
+ throw new IllegalArgumentException("Uri contentUri null");
+ }
+ try {
+ final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms == null) {
+ return;
+ }
+
+ iMms.sendMessage(getSubscriptionId(), ActivityThread.currentPackageName(), contentUri,
+ locationUrl, configOverrides, sentIntent);
+ } catch (RemoteException e) {
+ // Ignore it
+ }
+ }
+
+ /**
+ * Download an MMS message from carrier by a given location URL
+ *
+ * @param context application context
+ * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+ * from the MMS WAP push notification
+ * @param contentUri the content uri to which the downloaded pdu will be written
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * downloading the message.
+ * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is downloaded, or the download is failed
+ * @throws IllegalArgumentException if locationUrl or contentUri is empty
+ */
+ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
+ Bundle configOverrides, PendingIntent downloadedIntent) {
+ if (TextUtils.isEmpty(locationUrl)) {
+ throw new IllegalArgumentException("Empty MMS location URL");
+ }
+ if (contentUri == null) {
+ throw new IllegalArgumentException("Uri contentUri null");
+ }
+ try {
+ final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms == null) {
+ return;
+ }
+ iMms.downloadMessage(
+ getSubscriptionId(), ActivityThread.currentPackageName(), locationUrl,
+ contentUri, configOverrides, downloadedIntent);
+ } catch (RemoteException e) {
+ // Ignore it
+ }
+ }
+
+ // MMS send/download failure result codes
+ public static final int MMS_ERROR_UNSPECIFIED = 1;
+ public static final int MMS_ERROR_INVALID_APN = 2;
+ public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3;
+ public static final int MMS_ERROR_HTTP_FAILURE = 4;
+ public static final int MMS_ERROR_IO_ERROR = 5;
+ public static final int MMS_ERROR_RETRY = 6;
+ public static final int MMS_ERROR_CONFIGURATION_ERROR = 7;
+ public static final int MMS_ERROR_NO_DATA_NETWORK = 8;
+
+ /** Intent extra name for MMS sending result data in byte array type */
+ public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA";
+ /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */
+ public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS";
+
+ /**
+ * Import a text message into system's SMS store
+ *
+ * Only default SMS apps can import SMS
+ *
+ * @param address the destination(source) address of the sent(received) message
+ * @param type the type of the message
+ * @param text the message text
+ * @param timestampMillis the message timestamp in milliseconds
+ * @param seen if the message is seen
+ * @param read if the message is read
+ * @return the message URI, null if failed
+ * @hide
+ */
+ public Uri importTextMessage(String address, int type, String text, long timestampMillis,
+ boolean seen, boolean read) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.importTextMessage(ActivityThread.currentPackageName(),
+ address, type, text, timestampMillis, seen, read);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+
+ /** Represents the received SMS message for importing {@hide} */
+ public static final int SMS_TYPE_INCOMING = 0;
+ /** Represents the sent SMS message for importing {@hide} */
+ public static final int SMS_TYPE_OUTGOING = 1;
+
+ /**
+ * Import a multimedia message into system's MMS store. Only the following PDU type is
+ * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind
+ *
+ * Only default SMS apps can import MMS
+ *
+ * @param contentUri the content uri from which to read the PDU of the message to import
+ * @param messageId the optional message id. Use null if not specifying
+ * @param timestampSecs the optional message timestamp. Use -1 if not specifying
+ * @param seen if the message is seen
+ * @param read if the message is read
+ * @return the message URI, null if failed
+ * @throws IllegalArgumentException if pdu is empty
+ * {@hide}
+ */
+ public Uri importMultimediaMessage(Uri contentUri, String messageId, long timestampSecs,
+ boolean seen, boolean read) {
+ if (contentUri == null) {
+ throw new IllegalArgumentException("Uri contentUri null");
+ }
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.importMultimediaMessage(ActivityThread.currentPackageName(),
+ contentUri, messageId, timestampSecs, seen, read);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+
+ /**
+ * Delete a system stored SMS or MMS message
+ *
+ * Only default SMS apps can delete system stored SMS and MMS messages
+ *
+ * @param messageUri the URI of the stored message
+ * @return true if deletion is successful, false otherwise
+ * @throws IllegalArgumentException if messageUri is empty
+ * {@hide}
+ */
+ public boolean deleteStoredMessage(Uri messageUri) {
+ if (messageUri == null) {
+ throw new IllegalArgumentException("Empty message URI");
+ }
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return false;
+ }
+
+ /**
+ * Delete a system stored SMS or MMS thread
+ *
+ * Only default SMS apps can delete system stored SMS and MMS conversations
+ *
+ * @param conversationId the ID of the message conversation
+ * @return true if deletion is successful, false otherwise
+ * {@hide}
+ */
+ public boolean deleteStoredConversation(long conversationId) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.deleteStoredConversation(
+ ActivityThread.currentPackageName(), conversationId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return false;
+ }
+
+ /**
+ * Update the status properties of a system stored SMS or MMS message, e.g.
+ * the read status of a message, etc.
+ *
+ * @param messageUri the URI of the stored message
+ * @param statusValues a list of status properties in key-value pairs to update
+ * @return true if update is successful, false otherwise
+ * @throws IllegalArgumentException if messageUri is empty
+ * {@hide}
+ */
+ public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) {
+ if (messageUri == null) {
+ throw new IllegalArgumentException("Empty message URI");
+ }
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(),
+ messageUri, statusValues);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return false;
+ }
+
+ /** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */
+ public static final String MESSAGE_STATUS_SEEN = "seen";
+ /** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */
+ public static final String MESSAGE_STATUS_READ = "read";
+
+ /**
+ * Archive or unarchive a stored conversation
+ *
+ * @param conversationId the ID of the message conversation
+ * @param archived true to archive the conversation, false to unarchive
+ * @return true if update is successful, false otherwise
+ * {@hide}
+ */
+ public boolean archiveStoredConversation(long conversationId, boolean archived) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.archiveStoredConversation(ActivityThread.currentPackageName(),
+ conversationId, archived);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return false;
+ }
+
+ /**
+ * Add a text message draft to system SMS store
+ *
+ * Only default SMS apps can add SMS draft
+ *
+ * @param address the destination address of message
+ * @param text the body of the message to send
+ * @return the URI of the stored draft message
+ * {@hide}
+ */
+ public Uri addTextMessageDraft(String address, String text) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+
+ /**
+ * Add a multimedia message draft to system MMS store
+ *
+ * Only default SMS apps can add MMS draft
+ *
+ * @param contentUri the content uri from which to read the PDU data of the draft MMS
+ * @return the URI of the stored draft message
+ * @throws IllegalArgumentException if pdu is empty
+ * {@hide}
+ */
+ public Uri addMultimediaMessageDraft(Uri contentUri) {
+ if (contentUri == null) {
+ throw new IllegalArgumentException("Uri contentUri null");
+ }
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(),
+ contentUri);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+
+ /**
+ * Send a system stored text message.
+ *
+ * You can only send a failed text message or a draft text message.
+ *
+ * @param messageUri the URI of the stored message
+ * @param scAddress is the service center address or null to use the current default SMSC
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if messageUri is empty
+ * {@hide}
+ */
+ public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
+ if (messageUri == null) {
+ throw new IllegalArgumentException("Empty message URI");
+ }
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendStoredText(
+ getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
+ scAddress, sentIntent, deliveryIntent);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Send a system stored multi-part text message.
+ *
+ * You can only send a failed text message or a draft text message.
+ * The provided <code>PendingIntent</code> lists should match the part number of the
+ * divided text of the stored message by using <code>divideMessage</code>
+ *
+ * @param messageUri the URI of the stored message
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if messageUri is empty
+ * {@hide}
+ */
+ public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+ if (messageUri == null) {
+ throw new IllegalArgumentException("Empty message URI");
+ }
+ try {
+ ISms iccISms = getISmsServiceOrThrow();
+ iccISms.sendStoredMultipartText(
+ getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
+ scAddress, sentIntents, deliveryIntents);
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Send a system stored MMS message
+ *
+ * This is used for sending a previously sent, but failed-to-send, message or
+ * for sending a text message that has been stored as a draft.
+ *
+ * @param messageUri the URI of the stored message
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * sending the message.
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed
+ * @throws IllegalArgumentException if messageUri is empty
+ * {@hide}
+ */
+ public void sendStoredMultimediaMessage(Uri messageUri, Bundle configOverrides,
+ PendingIntent sentIntent) {
+ if (messageUri == null) {
+ throw new IllegalArgumentException("Empty message URI");
+ }
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ iMms.sendStoredMessage(
+ getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
+ configOverrides, sentIntent);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system
+ *
+ * When this flag is on, all SMS/MMS sent/received are stored by system automatically
+ * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
+ * automatically
+ *
+ * This flag can only be changed by default SMS apps
+ *
+ * @param enabled Whether to enable message auto persisting
+ * {@hide}
+ */
+ public void setAutoPersisting(boolean enabled) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Get the value of the flag to automatically write sent/received SMS/MMS messages into system
+ *
+ * When this flag is on, all SMS/MMS sent/received are stored by system automatically
+ * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
+ * automatically
+ *
+ * @return the current value of the auto persist flag
+ * {@hide}
+ */
+ public boolean getAutoPersisting() {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.getAutoPersisting();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return false;
+ }
+
+ /**
+ * Get carrier-dependent configuration values.
+ *
+ * @return bundle key/values pairs of configuration values
+ */
+ public Bundle getCarrierConfigValues() {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.getCarrierConfigValues(getSubscriptionId());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+
+ /**
+ * Create a single use app specific incoming SMS request for the the calling package.
+ *
+ * This method returns a token that if included in a subsequent incoming SMS message will cause
+ * {@code intent} to be sent with the SMS data.
+ *
+ * The token is only good for one use, after an SMS has been received containing the token all
+ * subsequent SMS messages with the token will be routed as normal.
+ *
+ * An app can only have one request at a time, if the app already has a request pending it will
+ * be replaced with a new request.
+ *
+ * @return Token to include in an SMS message. The token will be 11 characters long.
+ * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+ */
+ public String createAppSpecificSmsToken(PendingIntent intent) {
+ try {
+ ISms iccSms = getISmsServiceOrThrow();
+ return iccSms.createAppSpecificSmsToken(getSubscriptionId(),
+ ActivityThread.currentPackageName(), intent);
+
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
+ * Filters a bundle to only contain MMS config variables.
+ *
+ * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
+ * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
+ * supplied bundle.
+ *
+ * @param config a Bundle that contains MMS config variables and possibly more.
+ * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
+ * @hide
+ */
+ public static Bundle getMmsConfig(BaseBundle config) {
+ Bundle filtered = new Bundle();
+ filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID,
+ config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID));
+ filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED,
+ config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
+ config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO,
+ config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO));
+ filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED,
+ config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
+ config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
+ config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
+ filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
+ config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
+ filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED,
+ config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
+ config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
+ filtered.putBoolean(MMS_CONFIG_CLOSE_CONNECTION,
+ config.getBoolean(MMS_CONFIG_CLOSE_CONNECTION));
+ filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE));
+ filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH));
+ filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT));
+ filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT));
+ filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS));
+ filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS));
+ filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
+ config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
+ filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
+ config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
+ filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
+ config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
+ filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH,
+ config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH));
+ filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
+ config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
+ filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME,
+ config.getString(MMS_CONFIG_UA_PROF_TAG_NAME));
+ filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT));
+ filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL));
+ filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS));
+ filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
+ config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
+ filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX));
+ filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
+ config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
+ filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
+ config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
+ return filtered;
+ }
+
+}
diff --git a/android/telephony/SmsMessage.java b/android/telephony/SmsMessage.java
new file mode 100644
index 00000000..dcdda868
--- /dev/null
+++ b/android/telephony/SmsMessage.java
@@ -0,0 +1,926 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Binder;
+import android.os.Parcel;
+import android.content.res.Resources;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+ */
+public class SmsMessage {
+ private static final String LOG_TAG = "SmsMessage";
+
+ /**
+ * SMS Class enumeration.
+ * See TS 23.038.
+ *
+ */
+ public enum MessageClass{
+ UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+ }
+
+ /** User data text encoding code unit size */
+ public static final int ENCODING_UNKNOWN = 0;
+ public static final int ENCODING_7BIT = 1;
+ public static final int ENCODING_8BIT = 2;
+ public static final int ENCODING_16BIT = 3;
+ /**
+ * @hide This value is not defined in global standard. Only in Korea, this is used.
+ */
+ public static final int ENCODING_KSC5601 = 4;
+
+ /** The maximum number of payload bytes per message */
+ public static final int MAX_USER_DATA_BYTES = 140;
+
+ /**
+ * The maximum number of payload bytes per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ */
+ public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+ /** The maximum number of payload septets per message */
+ public static final int MAX_USER_DATA_SEPTETS = 160;
+
+ /**
+ * The maximum number of payload septets per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ */
+ public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+ /**
+ * Indicates a 3GPP format SMS message.
+ * @hide pending API council approval
+ */
+ public static final String FORMAT_3GPP = "3gpp";
+
+ /**
+ * Indicates a 3GPP2 format SMS message.
+ * @hide pending API council approval
+ */
+ public static final String FORMAT_3GPP2 = "3gpp2";
+
+ /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+ *
+ * @hide
+ */
+ public SmsMessageBase mWrappedSmsMessage;
+
+ /** Indicates the subId
+ *
+ * @hide
+ */
+ private int mSubId = 0;
+
+ /** set Subscription information
+ *
+ * @hide
+ */
+ public void setSubId(int subId) {
+ mSubId = subId;
+ }
+
+ /** get Subscription information
+ *
+ * @hide
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ public static class SubmitPdu {
+
+ public byte[] encodedScAddress; // Null if not applicable.
+ public byte[] encodedMessage;
+
+ @Override
+ public String toString() {
+ return "SubmitPdu: encodedScAddress = "
+ + Arrays.toString(encodedScAddress)
+ + ", encodedMessage = "
+ + Arrays.toString(encodedMessage);
+ }
+
+ /**
+ * @hide
+ */
+ protected SubmitPdu(SubmitPduBase spb) {
+ this.encodedMessage = spb.encodedMessage;
+ this.encodedScAddress = spb.encodedScAddress;
+ }
+
+ }
+
+ /**
+ * @hide
+ */
+ public SmsMessage(SmsMessageBase smb) {
+ mWrappedSmsMessage = smb;
+ }
+
+ /**
+ * Create an SmsMessage from a raw PDU. Guess format based on Voice
+ * technology first, if it fails use other format.
+ * All applications which handle
+ * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+ * intent <b>must</b> now pass the new {@code format} String extra from the intent
+ * into the new method {@code createFromPdu(byte[], String)} which takes an
+ * extra format parameter. This is required in order to correctly decode the PDU on
+ * devices that require support for both 3GPP and 3GPP2 formats at the same time,
+ * such as dual-mode GSM/CDMA and CDMA/LTE phones.
+ * @deprecated Use {@link #createFromPdu(byte[], String)} instead.
+ */
+ @Deprecated
+ public static SmsMessage createFromPdu(byte[] pdu) {
+ SmsMessage message = null;
+
+ // cdma(3gpp2) vs gsm(3gpp) format info was not given,
+ // guess from active voice phone type
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+ String format = (PHONE_TYPE_CDMA == activePhone) ?
+ SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
+ message = createFromPdu(pdu, format);
+
+ if (null == message || null == message.mWrappedSmsMessage) {
+ // decoding pdu failed based on activePhone type, must be other format
+ format = (PHONE_TYPE_CDMA == activePhone) ?
+ SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2;
+ message = createFromPdu(pdu, format);
+ }
+ return message;
+ }
+
+ /**
+ * Create an SmsMessage from a raw PDU with the specified message format. The
+ * message format is passed in the
+ * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} as the {@code format}
+ * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+ * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+ *
+ * @param pdu the message PDU from the
+ * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+ * @param format the format extra from the
+ * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+ */
+ public static SmsMessage createFromPdu(byte[] pdu, String format) {
+ SmsMessageBase wrappedMessage;
+
+ if (SmsConstants.FORMAT_3GPP2.equals(format)) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+ } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+ } else {
+ Rlog.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
+ return null;
+ }
+
+ if (wrappedMessage != null) {
+ return new SmsMessage(wrappedMessage);
+ } else {
+ Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
+ return null;
+ }
+ }
+
+ /**
+ * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
+ * +CMT unsolicited response (PDU mode, of course)
+ * +CMT: [&lt;alpha>],<length><CR><LF><pdu>
+ *
+ * Only public for debugging and for RIL
+ *
+ * {@hide}
+ */
+ public static SmsMessage newFromCMT(byte[] pdu) {
+ // received SMS in 3GPP format
+ SmsMessageBase wrappedMessage =
+ com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu);
+
+ if (wrappedMessage != null) {
+ return new SmsMessage(wrappedMessage);
+ } else {
+ Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null");
+ return null;
+ }
+ }
+
+ /**
+ * Create an SmsMessage from an SMS EF record.
+ *
+ * @param index Index of SMS record. This should be index in ArrayList
+ * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param data Record data.
+ * @return An SmsMessage representing the record.
+ *
+ * @hide
+ */
+ public static SmsMessage createFromEfRecord(int index, byte[] data) {
+ SmsMessageBase wrappedMessage;
+
+ if (isCdmaVoice()) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+ index, data);
+ } else {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+ index, data);
+ }
+
+ if (wrappedMessage != null) {
+ return new SmsMessage(wrappedMessage);
+ } else {
+ Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null");
+ return null;
+ }
+ }
+
+ /**
+ * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+ * length in bytes (not hex chars) less the SMSC header
+ *
+ * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
+ * We should probably deprecate it and remove the obsolete test case.
+ */
+ public static int getTPLayerLengthForPDU(String pdu) {
+ if (isCdmaVoice()) {
+ return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+ } else {
+ return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+ }
+ }
+
+ /*
+ * TODO(cleanup): It would make some sense if the result of
+ * preprocessing a message to determine the proper encoding (i.e.
+ * the resulting data structure from calculateLength) could be
+ * passed as an argument to the actual final encoding function.
+ * This would better ensure that the logic behind size calculation
+ * actually matched the encoding.
+ */
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message.
+ *
+ * @param msgBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the
+ * radio-specific 7-bit encoding are counted as single
+ * space chars. If false, and if the messageBody contains
+ * non-7-bit encodable characters, length is calculated
+ * using a 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's
+ * required, int[1] the number of code units used, and
+ * int[2] is the number of code units remaining until the
+ * next message. int[3] is an indicator of the encoding
+ * code unit size (see the ENCODING_* definitions in SmsConstants)
+ */
+ public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
+ // this function is for MO SMS
+ TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly,
+ true) :
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ int ret[] = new int[4];
+ ret[0] = ted.msgCount;
+ ret[1] = ted.codeUnitCount;
+ ret[2] = ted.codeUnitsRemaining;
+ ret[3] = ted.codeUnitSize;
+ return ret;
+ }
+
+ /**
+ * Divide a message text into several fragments, none bigger than
+ * the maximum SMS message text size.
+ *
+ * @param text text, must not be null.
+ * @return an <code>ArrayList</code> of strings that, in order,
+ * comprise the original msg text
+ *
+ * @hide
+ */
+ public static ArrayList<String> fragmentText(String text) {
+ // This function is for MO SMS
+ TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false, true) :
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
+
+ // TODO(cleanup): The code here could be rolled into the logic
+ // below cleanly if these MAX_* constants were defined more
+ // flexibly...
+
+ int limit;
+ if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+ int udhLength;
+ if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
+ udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+ } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
+ udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+ } else {
+ udhLength = 0;
+ }
+
+ if (ted.msgCount > 1) {
+ udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+ }
+
+ if (udhLength != 0) {
+ udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
+ }
+
+ limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
+ } else {
+ if (ted.msgCount > 1) {
+ limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+ // If EMS is not supported, break down EMS into single segment SMS
+ // and add page info " x/y".
+ // In the case of UCS2 encoding, we need 8 bytes for this,
+ // but we only have 6 bytes from UDH, so truncate the limit for
+ // each segment by 2 bytes (1 char).
+ // Make sure total number of segments is less than 10.
+ if (!hasEmsSupport() && ted.msgCount < 10) {
+ limit -= 2;
+ }
+ } else {
+ limit = SmsConstants.MAX_USER_DATA_BYTES;
+ }
+ }
+
+ String newMsgBody = null;
+ Resources r = Resources.getSystem();
+ if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
+ newMsgBody = Sms7BitEncodingTranslator.translate(text);
+ }
+ if (TextUtils.isEmpty(newMsgBody)) {
+ newMsgBody = text;
+ }
+ int pos = 0; // Index in code units.
+ int textLen = newMsgBody.length();
+ ArrayList<String> result = new ArrayList<String>(ted.msgCount);
+ while (pos < textLen) {
+ int nextPos = 0; // Counts code units.
+ if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+ if (useCdmaFormatForMoSms() && ted.msgCount == 1) {
+ // For a singleton CDMA message, the encoding must be ASCII...
+ nextPos = pos + Math.min(limit, textLen - pos);
+ } else {
+ // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+ nextPos = GsmAlphabet.findGsmSeptetLimitIndex(newMsgBody, pos, limit,
+ ted.languageTable, ted.languageShiftTable);
+ }
+ } else { // Assume unicode.
+ nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody);
+ }
+ if ((nextPos <= pos) || (nextPos > textLen)) {
+ Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+ nextPos + " >= " + textLen + ")");
+ break;
+ }
+ result.add(newMsgBody.substring(pos, nextPos));
+ pos = nextPos;
+ }
+ return result;
+ }
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message, given the
+ * current encoding.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the radio
+ * specific (GSM / CDMA) alphabet encoding are converted to as a
+ * single space characters. If false, a messageBody containing
+ * non-GSM or non-CDMA alphabet characters are encoded using
+ * 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's required, int[1]
+ * the number of code units used, and int[2] is the number of code
+ * units remaining until the next message. int[3] is the encoding
+ * type that should be used for the message.
+ */
+ public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+ return calculateLength((CharSequence)messageBody, use7bitOnly);
+ }
+
+ /*
+ * TODO(cleanup): It looks like there is now no useful reason why
+ * apps should generate pdus themselves using these routines,
+ * instead of handing the raw data to SMSDispatcher (and thereby
+ * have the phone process do the encoding). Moreover, CDMA now
+ * has shared state (in the form of the msgId system property)
+ * which can only be modified by the phone process, and hence
+ * makes the output of these routines incorrect. Since they now
+ * serve no purpose, they should probably just return null
+ * directly, and be deprecated. Going further in that direction,
+ * the above parsers of serialized pdu data should probably also
+ * be gotten rid of, hiding all but the necessarily visible
+ * structured data from client apps. A possible concern with
+ * doing this is that apps may be using these routines to generate
+ * pdus that are then sent elsewhere, some network server, for
+ * example, and that always returning null would thereby break
+ * otherwise useful apps.
+ */
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * This method will not attempt to use any GSM national language 7 bit encodings.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message, boolean statusReportRequested) {
+ return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
+ SubscriptionManager.getDefaultSmsSubscriptionId());
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * This method will not attempt to use any GSM national language 7 bit encodings.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message String representation of the message payload.
+ * @param statusReportRequested Indicates whether a report is requested for this message.
+ * @param subId Subscription of the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message, boolean statusReportRequested, int subId) {
+ SubmitPduBase spb;
+ if (useCdmaFormatForMoSms(subId)) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested, null);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+ * This method will not attempt to use any GSM national language 7 bit encodings.
+ *
+ * @param scAddress Service Centre address. null == use default
+ * @param destinationAddress the address of the destination for the message
+ * @param destinationPort the port to deliver the message to at the
+ * destination
+ * @param data the data for the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ */
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, short destinationPort, byte[] data,
+ boolean statusReportRequested) {
+ SubmitPduBase spb;
+
+ if (useCdmaFormatForMoSms()) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Returns the address of the SMS service center that relayed this message
+ * or null if there is none.
+ */
+ public String getServiceCenterAddress() {
+ return mWrappedSmsMessage.getServiceCenterAddress();
+ }
+
+ /**
+ * Returns the originating address (sender) of this SMS message in String
+ * form or null if unavailable
+ */
+ public String getOriginatingAddress() {
+ return mWrappedSmsMessage.getOriginatingAddress();
+ }
+
+ /**
+ * Returns the originating address, or email from address if this message
+ * was from an email gateway. Returns null if originating address
+ * unavailable.
+ */
+ public String getDisplayOriginatingAddress() {
+ return mWrappedSmsMessage.getDisplayOriginatingAddress();
+ }
+
+ /**
+ * Returns the message body as a String, if it exists and is text based.
+ * @return message body is there is one, otherwise null
+ */
+ public String getMessageBody() {
+ return mWrappedSmsMessage.getMessageBody();
+ }
+
+ /**
+ * Returns the class of this message.
+ */
+ public MessageClass getMessageClass() {
+ switch(mWrappedSmsMessage.getMessageClass()) {
+ case CLASS_0: return MessageClass.CLASS_0;
+ case CLASS_1: return MessageClass.CLASS_1;
+ case CLASS_2: return MessageClass.CLASS_2;
+ case CLASS_3: return MessageClass.CLASS_3;
+ default: return MessageClass.UNKNOWN;
+
+ }
+ }
+
+ /**
+ * Returns the message body, or email message body if this message was from
+ * an email gateway. Returns null if message body unavailable.
+ */
+ public String getDisplayMessageBody() {
+ return mWrappedSmsMessage.getDisplayMessageBody();
+ }
+
+ /**
+ * Unofficial convention of a subject line enclosed in parens empty string
+ * if not present
+ */
+ public String getPseudoSubject() {
+ return mWrappedSmsMessage.getPseudoSubject();
+ }
+
+ /**
+ * Returns the service centre timestamp in currentTimeMillis() format
+ */
+ public long getTimestampMillis() {
+ return mWrappedSmsMessage.getTimestampMillis();
+ }
+
+ /**
+ * Returns true if message is an email.
+ *
+ * @return true if this message came through an email gateway and email
+ * sender / subject / parsed body are available
+ */
+ public boolean isEmail() {
+ return mWrappedSmsMessage.isEmail();
+ }
+
+ /**
+ * @return if isEmail() is true, body of the email sent through the gateway.
+ * null otherwise
+ */
+ public String getEmailBody() {
+ return mWrappedSmsMessage.getEmailBody();
+ }
+
+ /**
+ * @return if isEmail() is true, email from address of email sent through
+ * the gateway. null otherwise
+ */
+ public String getEmailFrom() {
+ return mWrappedSmsMessage.getEmailFrom();
+ }
+
+ /**
+ * Get protocol identifier.
+ */
+ public int getProtocolIdentifier() {
+ return mWrappedSmsMessage.getProtocolIdentifier();
+ }
+
+ /**
+ * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
+ * SMS
+ */
+ public boolean isReplace() {
+ return mWrappedSmsMessage.isReplace();
+ }
+
+ /**
+ * Returns true for CPHS MWI toggle message.
+ *
+ * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
+ * B.4.2
+ */
+ public boolean isCphsMwiMessage() {
+ return mWrappedSmsMessage.isCphsMwiMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) clear message
+ */
+ public boolean isMWIClearMessage() {
+ return mWrappedSmsMessage.isMWIClearMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) set message
+ */
+ public boolean isMWISetMessage() {
+ return mWrappedSmsMessage.isMWISetMessage();
+ }
+
+ /**
+ * returns true if this message is a "Message Waiting Indication Group:
+ * Discard Message" notification and should not be stored.
+ */
+ public boolean isMwiDontStore() {
+ return mWrappedSmsMessage.isMwiDontStore();
+ }
+
+ /**
+ * returns the user data section minus the user data header if one was
+ * present.
+ */
+ public byte[] getUserData() {
+ return mWrappedSmsMessage.getUserData();
+ }
+
+ /**
+ * Returns the raw PDU for the message.
+ *
+ * @return the raw PDU for the message.
+ */
+ public byte[] getPdu() {
+ return mWrappedSmsMessage.getPdu();
+ }
+
+ /**
+ * Returns the status of the message on the SIM (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the SIM. These are:
+ * SmsManager.STATUS_ON_SIM_FREE
+ * SmsManager.STATUS_ON_SIM_READ
+ * SmsManager.STATUS_ON_SIM_UNREAD
+ * SmsManager.STATUS_ON_SIM_SEND
+ * SmsManager.STATUS_ON_SIM_UNSENT
+ * @deprecated Use getStatusOnIcc instead.
+ */
+ @Deprecated public int getStatusOnSim() {
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the status of the message on the ICC (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the ICC. These are:
+ * SmsManager.STATUS_ON_ICC_FREE
+ * SmsManager.STATUS_ON_ICC_READ
+ * SmsManager.STATUS_ON_ICC_UNREAD
+ * SmsManager.STATUS_ON_ICC_SEND
+ * SmsManager.STATUS_ON_ICC_UNSENT
+ */
+ public int getStatusOnIcc() {
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the SIM (1-based index).
+ * @return the record index of the message on the SIM, or -1 if this
+ * SmsMessage was not created from a SIM SMS EF record.
+ * @deprecated Use getIndexOnIcc instead.
+ */
+ @Deprecated public int getIndexOnSim() {
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the ICC (1-based index).
+ * @return the record index of the message on the ICC, or -1 if this
+ * SmsMessage was not created from a ICC SMS EF record.
+ */
+ public int getIndexOnIcc() {
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * GSM:
+ * For an SMS-STATUS-REPORT message, this returns the status field from
+ * the status report. This field indicates the status of a previously
+ * submitted SMS, if requested. See TS 23.040, 9.2.3.15 TP-Status for a
+ * description of values.
+ * CDMA:
+ * For not interfering with status codes from GSM, the value is
+ * shifted to the bits 31-16.
+ * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+ * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+ *
+ * @return 0 indicates the previously sent message was received.
+ * See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+ * for a description of other possible values.
+ */
+ public int getStatus() {
+ return mWrappedSmsMessage.getStatus();
+ }
+
+ /**
+ * Return true iff the message is a SMS-STATUS-REPORT message.
+ */
+ public boolean isStatusReportMessage() {
+ return mWrappedSmsMessage.isStatusReportMessage();
+ }
+
+ /**
+ * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+ * this message.
+ */
+ public boolean isReplyPathPresent() {
+ return mWrappedSmsMessage.isReplyPathPresent();
+ }
+
+ /**
+ * Determines whether or not to use CDMA format for MO SMS.
+ * If SMS over IMS is supported, then format is based on IMS SMS format,
+ * otherwise format is based on current phone type.
+ *
+ * @return true if Cdma format should be used for MO SMS, false otherwise.
+ */
+ private static boolean useCdmaFormatForMoSms() {
+ // IMS is registered with SMS support, check the SMS format supported
+ return useCdmaFormatForMoSms(SubscriptionManager.getDefaultSmsSubscriptionId());
+ }
+
+ /**
+ * Determines whether or not to use CDMA format for MO SMS.
+ * If SMS over IMS is supported, then format is based on IMS SMS format,
+ * otherwise format is based on current phone type.
+ *
+ * @param subId Subscription for which phone type is returned.
+ *
+ * @return true if Cdma format should be used for MO SMS, false otherwise.
+ */
+ private static boolean useCdmaFormatForMoSms(int subId) {
+ SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+ if (!smsManager.isImsSmsSupported()) {
+ // use Voice technology to determine SMS format.
+ return isCdmaVoice(subId);
+ }
+ // IMS is registered with SMS support, check the SMS format supported
+ return (SmsConstants.FORMAT_3GPP2.equals(smsManager.getImsSmsFormat()));
+ }
+
+ /**
+ * Determines whether or not to current phone type is cdma.
+ *
+ * @return true if current phone type is cdma, false otherwise.
+ */
+ private static boolean isCdmaVoice() {
+ return isCdmaVoice(SubscriptionManager.getDefaultSmsSubscriptionId());
+ }
+
+ /**
+ * Determines whether or not to current phone type is cdma
+ *
+ * @return true if current phone type is cdma, false otherwise.
+ */
+ private static boolean isCdmaVoice(int subId) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
+ return (PHONE_TYPE_CDMA == activePhone);
+ }
+ /**
+ * Decide if the carrier supports long SMS.
+ * {@hide}
+ */
+ public static boolean hasEmsSupport() {
+ if (!isNoEmsSupportConfigListExisted()) {
+ return true;
+ }
+
+ String simOperator;
+ String gid;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
+ gid = TelephonyManager.getDefault().getGroupIdLevel1();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ if (!TextUtils.isEmpty(simOperator)) {
+ for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
+ if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
+ (TextUtils.isEmpty(currentConfig.mGid1) ||
+ (!TextUtils.isEmpty(currentConfig.mGid1) &&
+ currentConfig.mGid1.equalsIgnoreCase(gid)))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check where to add " x/y" in each SMS segment, begin or end.
+ * {@hide}
+ */
+ public static boolean shouldAppendPageNumberAsPrefix() {
+ if (!isNoEmsSupportConfigListExisted()) {
+ return false;
+ }
+
+ String simOperator;
+ String gid;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
+ gid = TelephonyManager.getDefault().getGroupIdLevel1();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
+ if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
+ (TextUtils.isEmpty(currentConfig.mGid1) ||
+ (!TextUtils.isEmpty(currentConfig.mGid1)
+ && currentConfig.mGid1.equalsIgnoreCase(gid)))) {
+ return currentConfig.mIsPrefix;
+ }
+ }
+ return false;
+ }
+
+ private static class NoEmsSupportConfig {
+ String mOperatorNumber;
+ String mGid1;
+ boolean mIsPrefix;
+
+ public NoEmsSupportConfig(String[] config) {
+ mOperatorNumber = config[0];
+ mIsPrefix = "prefix".equals(config[1]);
+ mGid1 = config.length > 2 ? config[2] : null;
+ }
+
+ @Override
+ public String toString() {
+ return "NoEmsSupportConfig { mOperatorNumber = " + mOperatorNumber
+ + ", mIsPrefix = " + mIsPrefix + ", mGid1 = " + mGid1 + " }";
+ }
+ }
+
+ private static NoEmsSupportConfig[] mNoEmsSupportConfigList = null;
+ private static boolean mIsNoEmsSupportConfigListLoaded = false;
+
+ private static boolean isNoEmsSupportConfigListExisted() {
+ if (!mIsNoEmsSupportConfigListLoaded) {
+ Resources r = Resources.getSystem();
+ if (r != null) {
+ String[] listArray = r.getStringArray(
+ com.android.internal.R.array.no_ems_support_sim_operators);
+ if ((listArray != null) && (listArray.length > 0)) {
+ mNoEmsSupportConfigList = new NoEmsSupportConfig[listArray.length];
+ for (int i=0; i<listArray.length; i++) {
+ mNoEmsSupportConfigList[i] = new NoEmsSupportConfig(listArray[i].split(";"));
+ }
+ }
+ mIsNoEmsSupportConfigListLoaded = true;
+ }
+ }
+
+ if (mNoEmsSupportConfigList != null && mNoEmsSupportConfigList.length != 0) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/android/telephony/SubscriptionInfo.java b/android/telephony/SubscriptionInfo.java
new file mode 100644
index 00000000..4e1c15fa
--- /dev/null
+++ b/android/telephony/SubscriptionInfo.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+
+import java.util.Arrays;
+
+/**
+ * A Parcelable class for Subscription Information.
+ */
+public class SubscriptionInfo implements Parcelable {
+
+ /**
+ * Size of text to render on the icon.
+ */
+ private static final int TEXT_SIZE = 16;
+
+ /**
+ * Subscription Identifier, this is a device unique number
+ * and not an index into an array
+ */
+ private int mId;
+
+ /**
+ * The GID for a SIM that maybe associated with this subscription, empty if unknown
+ */
+ private String mIccId;
+
+ /**
+ * The index of the slot that currently contains the subscription
+ * and not necessarily unique and maybe INVALID_SLOT_ID if unknown
+ */
+ private int mSimSlotIndex;
+
+ /**
+ * The name displayed to the user that identifies this subscription
+ */
+ private CharSequence mDisplayName;
+
+ /**
+ * String that identifies SPN/PLMN
+ * TODO : Add a new field that identifies only SPN for a sim
+ */
+ private CharSequence mCarrierName;
+
+ /**
+ * The source of the name, NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE,
+ * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT.
+ */
+ private int mNameSource;
+
+ /**
+ * The color to be used for tinting the icon when displaying to the user
+ */
+ private int mIconTint;
+
+ /**
+ * A number presented to the user identify this subscription
+ */
+ private String mNumber;
+
+ /**
+ * Data roaming state, DATA_RAOMING_ENABLE, DATA_RAOMING_DISABLE
+ */
+ private int mDataRoaming;
+
+ /**
+ * SIM Icon bitmap
+ */
+ private Bitmap mIconBitmap;
+
+ /**
+ * Mobile Country Code
+ */
+ private int mMcc;
+
+ /**
+ * Mobile Network Code
+ */
+ private int mMnc;
+
+ /**
+ * ISO Country code for the subscription's provider
+ */
+ private String mCountryIso;
+
+ /**
+ * Whether the subscription is an embedded one.
+ */
+ private boolean mIsEmbedded;
+
+ /**
+ * The access rules for this subscription, if it is embedded and defines any.
+ */
+ @Nullable
+ private UiccAccessRule[] mAccessRules;
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, int mcc, int mnc, String countryIso) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+ null /* accessRules */);
+ }
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] accessRules) {
+ this.mId = id;
+ this.mIccId = iccId;
+ this.mSimSlotIndex = simSlotIndex;
+ this.mDisplayName = displayName;
+ this.mCarrierName = carrierName;
+ this.mNameSource = nameSource;
+ this.mIconTint = iconTint;
+ this.mNumber = number;
+ this.mDataRoaming = roaming;
+ this.mIconBitmap = icon;
+ this.mMcc = mcc;
+ this.mMnc = mnc;
+ this.mCountryIso = countryIso;
+ this.mIsEmbedded = isEmbedded;
+ this.mAccessRules = accessRules;
+ }
+
+ /**
+ * @return the subscription ID.
+ */
+ public int getSubscriptionId() {
+ return this.mId;
+ }
+
+ /**
+ * @return the ICC ID.
+ */
+ public String getIccId() {
+ return this.mIccId;
+ }
+
+ /**
+ * @return the slot index of this Subscription's SIM card.
+ */
+ public int getSimSlotIndex() {
+ return this.mSimSlotIndex;
+ }
+
+ /**
+ * @return the name displayed to the user that identifies this subscription
+ */
+ public CharSequence getDisplayName() {
+ return this.mDisplayName;
+ }
+
+ /**
+ * Sets the name displayed to the user that identifies this subscription
+ * @hide
+ */
+ public void setDisplayName(CharSequence name) {
+ this.mDisplayName = name;
+ }
+
+ /**
+ * @return the name displayed to the user that identifies Subscription provider name
+ */
+ public CharSequence getCarrierName() {
+ return this.mCarrierName;
+ }
+
+ /**
+ * Sets the name displayed to the user that identifies Subscription provider name
+ * @hide
+ */
+ public void setCarrierName(CharSequence name) {
+ this.mCarrierName = name;
+ }
+
+ /**
+ * @return the source of the name, eg NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE,
+ * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT.
+ * @hide
+ */
+ public int getNameSource() {
+ return this.mNameSource;
+ }
+
+ /**
+ * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a user
+ * interface.
+ *
+ * @param context A {@code Context} to get the {@code DisplayMetrics}s from.
+ *
+ * @return A bitmap icon for this {@code SubscriptionInfo}.
+ */
+ public Bitmap createIconBitmap(Context context) {
+ int width = mIconBitmap.getWidth();
+ int height = mIconBitmap.getHeight();
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+
+ // Create a new bitmap of the same size because it will be modified.
+ Bitmap workingBitmap = Bitmap.createBitmap(metrics, width, height, mIconBitmap.getConfig());
+
+ Canvas canvas = new Canvas(workingBitmap);
+ Paint paint = new Paint();
+
+ // Tint the icon with the color.
+ paint.setColorFilter(new PorterDuffColorFilter(mIconTint, PorterDuff.Mode.SRC_ATOP));
+ canvas.drawBitmap(mIconBitmap, 0, 0, paint);
+ paint.setColorFilter(null);
+
+ // Write the sim slot index.
+ paint.setAntiAlias(true);
+ paint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
+ paint.setColor(Color.WHITE);
+ // Set text size scaled by density
+ paint.setTextSize(TEXT_SIZE * metrics.density);
+ // Convert sim slot index to localized string
+ final String index = String.format("%d", mSimSlotIndex + 1);
+ final Rect textBound = new Rect();
+ paint.getTextBounds(index, 0, 1, textBound);
+ final float xOffset = (width / 2.f) - textBound.centerX();
+ final float yOffset = (height / 2.f) - textBound.centerY();
+ canvas.drawText(index, xOffset, yOffset, paint);
+
+ return workingBitmap;
+ }
+
+ /**
+ * A highlight color to use in displaying information about this {@code PhoneAccount}.
+ *
+ * @return A hexadecimal color value.
+ */
+ public int getIconTint() {
+ return mIconTint;
+ }
+
+ /**
+ * Sets the color displayed to the user that identifies this subscription
+ * @hide
+ */
+ public void setIconTint(int iconTint) {
+ this.mIconTint = iconTint;
+ }
+
+ /**
+ * @return the number of this subscription.
+ */
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * @return the data roaming state for this subscription, either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+ */
+ public int getDataRoaming() {
+ return this.mDataRoaming;
+ }
+
+ /**
+ * @return the MCC.
+ */
+ public int getMcc() {
+ return this.mMcc;
+ }
+
+ /**
+ * @return the MNC.
+ */
+ public int getMnc() {
+ return this.mMnc;
+ }
+
+ /**
+ * @return the ISO country code
+ */
+ public String getCountryIso() {
+ return this.mCountryIso;
+ }
+
+ /**
+ * @return whether the subscription is an embedded one.
+ * @hide
+ *
+ * TODO(b/35851809): Make this public.
+ */
+ public boolean isEmbedded() {
+ return this.mIsEmbedded;
+ }
+
+ /**
+ * Checks whether the app with the given context is authorized to manage this subscription
+ * according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
+ * returns true).
+ *
+ * @param context Context of the application to check.
+ * @return whether the app is authorized to manage this subscription per its metadata.
+ * @throws UnsupportedOperationException if this subscription is not embedded.
+ * @hide
+ *
+ * TODO(b/35851809): Make this public.
+ */
+ public boolean canManageSubscription(Context context) {
+ return canManageSubscription(context, context.getPackageName());
+ }
+
+ /**
+ * Checks whether the given app is authorized to manage this subscription according to its
+ * metadata. Only supported for embedded subscriptions (if {@link #isEmbedded} returns true).
+ *
+ * @param context Any context.
+ * @param packageName Package name of the app to check.
+ * @return whether the app is authorized to manage this subscription per its metadata.
+ * @throws UnsupportedOperationException if this subscription is not embedded.
+ * @hide
+ */
+ public boolean canManageSubscription(Context context, String packageName) {
+ if (!isEmbedded()) {
+ throw new UnsupportedOperationException("Not an embedded subscription");
+ }
+ if (mAccessRules == null) {
+ return false;
+ }
+ PackageManager packageManager = context.getPackageManager();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown package: " + packageName, e);
+ }
+ for (UiccAccessRule rule : mAccessRules) {
+ if (rule.getCarrierPrivilegeStatus(packageInfo)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription.
+ * @throws UnsupportedOperationException if this subscription is not embedded.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public @Nullable UiccAccessRule[] getAccessRules() {
+ if (!isEmbedded()) {
+ throw new UnsupportedOperationException("Not an embedded subscription");
+ }
+ return mAccessRules;
+ }
+
+ public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
+ @Override
+ public SubscriptionInfo createFromParcel(Parcel source) {
+ int id = source.readInt();
+ String iccId = source.readString();
+ int simSlotIndex = source.readInt();
+ CharSequence displayName = source.readCharSequence();
+ CharSequence carrierName = source.readCharSequence();
+ int nameSource = source.readInt();
+ int iconTint = source.readInt();
+ String number = source.readString();
+ int dataRoaming = source.readInt();
+ int mcc = source.readInt();
+ int mnc = source.readInt();
+ String countryIso = source.readString();
+ Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
+ boolean isEmbedded = source.readBoolean();
+ UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+
+ return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
+ nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
+ isEmbedded, accessRules);
+ }
+
+ @Override
+ public SubscriptionInfo[] newArray(int size) {
+ return new SubscriptionInfo[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mIccId);
+ dest.writeInt(mSimSlotIndex);
+ dest.writeCharSequence(mDisplayName);
+ dest.writeCharSequence(mCarrierName);
+ dest.writeInt(mNameSource);
+ dest.writeInt(mIconTint);
+ dest.writeString(mNumber);
+ dest.writeInt(mDataRoaming);
+ dest.writeInt(mMcc);
+ dest.writeInt(mMnc);
+ dest.writeString(mCountryIso);
+ mIconBitmap.writeToParcel(dest, flags);
+ dest.writeBoolean(mIsEmbedded);
+ dest.writeTypedArray(mAccessRules, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ public static String givePrintableIccid(String iccId) {
+ String iccIdToPrint = null;
+ if (iccId != null) {
+ if (iccId.length() > 9 && !Build.IS_DEBUGGABLE) {
+ iccIdToPrint = iccId.substring(0, 9) + Rlog.pii(false, iccId.substring(9));
+ } else {
+ iccIdToPrint = iccId;
+ }
+ }
+ return iccIdToPrint;
+ }
+
+ @Override
+ public String toString() {
+ String iccIdToPrint = givePrintableIccid(mIccId);
+ return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
+ + " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ + " mnc " + mMnc + " isEmbedded " + mIsEmbedded
+ + " accessRules " + Arrays.toString(mAccessRules) + "}";
+ }
+}
diff --git a/android/telephony/SubscriptionManager.java b/android/telephony/SubscriptionManager.java
new file mode 100644
index 00000000..88f4880a
--- /dev/null
+++ b/android/telephony/SubscriptionManager.java
@@ -0,0 +1,1603 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.net.INetworkPolicyManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.DisplayMetrics;
+import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+import com.android.internal.telephony.ISub;
+import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.PhoneConstants;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * SubscriptionManager is the application interface to SubscriptionController
+ * and provides information about the current Telephony Subscriptions.
+ * <p>
+ * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise
+ * specified.
+ */
+@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+public class SubscriptionManager {
+ private static final String LOG_TAG = "SubscriptionManager";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ /** An invalid subscription identifier */
+ public static final int INVALID_SUBSCRIPTION_ID = -1;
+
+ /** Base value for Dummy SUBSCRIPTION_ID's. */
+ /** FIXME: Remove DummySubId's, but for now have them map just below INVALID_SUBSCRIPTION_ID
+ /** @hide */
+ public static final int DUMMY_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1;
+
+ /** An invalid phone identifier */
+ /** @hide */
+ public static final int INVALID_PHONE_INDEX = -1;
+
+ /** An invalid slot identifier */
+ /** @hide */
+ public static final int INVALID_SIM_SLOT_INDEX = -1;
+
+ /** Indicates the caller wants the default sub id. */
+ /** @hide */
+ public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE;
+
+ /**
+ * Indicates the caller wants the default phone id.
+ * Used in SubscriptionController and Phone but do we really need it???
+ * @hide
+ */
+ public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE;
+
+ /** Indicates the caller wants the default slot id. NOT used remove? */
+ /** @hide */
+ public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE;
+
+ /** Minimum possible subid that represents a subscription */
+ /** @hide */
+ public static final int MIN_SUBSCRIPTION_ID_VALUE = 0;
+
+ /** Maximum possible subid that represents a subscription */
+ /** @hide */
+ public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1;
+
+ /** @hide */
+ public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
+
+ /**
+ * TelephonyProvider unique key column name is the subscription id.
+ * <P>Type: TEXT (String)</P>
+ */
+ /** @hide */
+ public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+
+ /**
+ * TelephonyProvider column name for SIM ICC Identifier
+ * <P>Type: TEXT (String)</P>
+ */
+ /** @hide */
+ public static final String ICC_ID = "icc_id";
+
+ /**
+ * TelephonyProvider column name for user SIM_SlOT_INDEX
+ * <P>Type: INTEGER (int)</P>
+ */
+ /** @hide */
+ public static final String SIM_SLOT_INDEX = "sim_id";
+
+ /** SIM is not inserted */
+ /** @hide */
+ public static final int SIM_NOT_INSERTED = -1;
+
+ /**
+ * TelephonyProvider column name for user displayed name.
+ * <P>Type: TEXT (String)</P>
+ */
+ /** @hide */
+ public static final String DISPLAY_NAME = "display_name";
+
+ /**
+ * TelephonyProvider column name for the service provider name for the SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ /** @hide */
+ public static final String CARRIER_NAME = "carrier_name";
+
+ /**
+ * Default name resource
+ * @hide
+ */
+ public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
+
+ /**
+ * TelephonyProvider column name for source of the user displayed name.
+ * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below
+ *
+ * @hide
+ */
+ public static final String NAME_SOURCE = "name_source";
+
+ /**
+ * The name_source is undefined
+ * @hide
+ */
+ public static final int NAME_SOURCE_UNDEFINDED = -1;
+
+ /**
+ * The name_source is the default
+ * @hide
+ */
+ public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
+
+ /**
+ * The name_source is from the SIM
+ * @hide
+ */
+ public static final int NAME_SOURCE_SIM_SOURCE = 1;
+
+ /**
+ * The name_source is from the user
+ * @hide
+ */
+ public static final int NAME_SOURCE_USER_INPUT = 2;
+
+ /**
+ * TelephonyProvider column name for the color of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ /** @hide */
+ public static final String COLOR = "color";
+
+ /** @hide */
+ public static final int COLOR_1 = 0;
+
+ /** @hide */
+ public static final int COLOR_2 = 1;
+
+ /** @hide */
+ public static final int COLOR_3 = 2;
+
+ /** @hide */
+ public static final int COLOR_4 = 3;
+
+ /** @hide */
+ public static final int COLOR_DEFAULT = COLOR_1;
+
+ /**
+ * TelephonyProvider column name for the phone number of a SIM.
+ * <P>Type: TEXT (String)</P>
+ */
+ /** @hide */
+ public static final String NUMBER = "number";
+
+ /**
+ * TelephonyProvider column name for the number display format of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ /** @hide */
+ public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
+
+ /** @hide */
+ public static final int DISPLAY_NUMBER_NONE = 0;
+
+ /** @hide */
+ public static final int DISPLAY_NUMBER_FIRST = 1;
+
+ /** @hide */
+ public static final int DISPLAY_NUMBER_LAST = 2;
+
+ /** @hide */
+ public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
+
+ /**
+ * TelephonyProvider column name for permission for data roaming of a SIM.
+ * <P>Type: INTEGER (int)</P>
+ */
+ /** @hide */
+ public static final String DATA_ROAMING = "data_roaming";
+
+ /** Indicates that data roaming is enabled for a subscription */
+ public static final int DATA_ROAMING_ENABLE = 1;
+
+ /** Indicates that data roaming is disabled for a subscription */
+ public static final int DATA_ROAMING_DISABLE = 0;
+
+ /** @hide */
+ public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
+
+ /** @hide */
+ public static final int SIM_PROVISIONED = 0;
+
+ /**
+ * TelephonyProvider column name for the MCC associated with a SIM.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String MCC = "mcc";
+
+ /**
+ * TelephonyProvider column name for the MNC associated with a SIM.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String MNC = "mnc";
+
+ /**
+ * TelephonyProvider column name for the sim provisioning status associated with a SIM.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
+
+ /**
+ * TelephonyProvider column name for whether a subscription is embedded (that is, present on an
+ * eSIM).
+ * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
+ * @hide
+ */
+ public static final String IS_EMBEDDED = "is_embedded";
+
+ /**
+ * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+ * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
+ * <p>TYPE: BLOB
+ * @hide
+ */
+ public static final String ACCESS_RULES = "access_rules";
+
+ /**
+ * TelephonyProvider column name identifying whether an embedded subscription is on a removable
+ * card. Such subscriptions are marked inaccessible as soon as the current card is removed.
+ * Otherwise, they will remain accessible unless explicitly deleted. Only present if
+ * {@link #IS_EMBEDDED} is 1.
+ * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
+ * @hide
+ */
+ public static final String IS_REMOVABLE = "is_removable";
+
+ /**
+ * TelephonyProvider column name for extreme threat in CB settings
+ * @hide
+ */
+ public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+
+ /**
+ * TelephonyProvider column name for severe threat in CB settings
+ *@hide
+ */
+ public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+
+ /**
+ * TelephonyProvider column name for amber alert in CB settings
+ *@hide
+ */
+ public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+
+ /**
+ * TelephonyProvider column name for emergency alert in CB settings
+ *@hide
+ */
+ public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+
+ /**
+ * TelephonyProvider column name for alert sound duration in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+
+ /**
+ * TelephonyProvider column name for alert reminder interval in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+
+ /**
+ * TelephonyProvider column name for enabling vibrate in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+
+ /**
+ * TelephonyProvider column name for enabling alert speech in CB settings
+ *@hide
+ */
+ public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+
+ /**
+ * TelephonyProvider column name for ETWS test alert in CB settings
+ *@hide
+ */
+ public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+
+ /**
+ * TelephonyProvider column name for enable channel50 alert in CB settings
+ *@hide
+ */
+ public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+
+ /**
+ * TelephonyProvider column name for CMAS test alert in CB settings
+ *@hide
+ */
+ public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
+
+ /**
+ * TelephonyProvider column name for Opt out dialog in CB settings
+ *@hide
+ */
+ public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+
+ /**
+ * Broadcast Action: The user has changed one of the default subs related to
+ * data, phone calls, or sms</p>
+ *
+ * TODO: Change to a listener
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SUB_DEFAULT_CHANGED_ACTION =
+ "android.intent.action.SUB_DEFAULT_CHANGED";
+
+ /**
+ * Broadcast Action: The default subscription has changed. This has the following
+ * extra values:</p>
+ * The {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default subscription index
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED
+ = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
+
+ /**
+ * Broadcast Action: The default sms subscription has changed. This has the following
+ * extra values:</p>
+ * {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default sms
+ * subscription index
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED
+ = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
+
+ /**
+ * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and
+ * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription
+ * which has changed.
+ */
+ public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+
+ private final Context mContext;
+
+ /**
+ * A listener class for monitoring changes to {@link SubscriptionInfo} records.
+ * <p>
+ * Override the onSubscriptionsChanged method in the object that extends this
+ * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
+ * to register your listener and to unregister invoke
+ * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
+ * <p>
+ * Permissions android.Manifest.permission.READ_PHONE_STATE is required
+ * for #onSubscriptionsChanged to be invoked.
+ */
+ public static class OnSubscriptionsChangedListener {
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) {
+ log("handleMessage: invoke the overriden onSubscriptionsChanged()");
+ }
+ OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
+ }
+ };
+
+ /**
+ * Callback invoked when there is any change to any SubscriptionInfo. Typically
+ * this method would invoke {@link #getActiveSubscriptionInfoList}
+ */
+ public void onSubscriptionsChanged() {
+ if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
+ }
+
+ /**
+ * The callback methods need to be called on the handler thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ */
+ IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (DBG) log("callback: received, sendEmptyMessage(0) to handler");
+ mHandler.sendEmptyMessage(0);
+ }
+ };
+
+ private void log(String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+ }
+
+ /** @hide */
+ public SubscriptionManager(Context context) {
+ if (DBG) logd("SubscriptionManager created");
+ mContext = context;
+ }
+
+ /**
+ * Get an instance of the SubscriptionManager from the Context.
+ * This invokes {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}.
+ *
+ * @param context to use.
+ * @return SubscriptionManager instance
+ */
+ public static SubscriptionManager from(Context context) {
+ return (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ }
+
+ /**
+ * Register for changes to the list of active {@link SubscriptionInfo} records or to the
+ * individual records themselves. When a change occurs the onSubscriptionsChanged method of
+ * the listener will be invoked immediately if there has been a notification.
+ *
+ * @param listener an instance of {@link OnSubscriptionsChangedListener} with
+ * onSubscriptionsChanged overridden.
+ */
+ public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (DBG) {
+ logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+ + " listener=" + listener);
+ }
+ try {
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available. Where as SubscriptionController could crash and not be available
+ ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ if (tr != null) {
+ tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ }
+ } catch (RemoteException ex) {
+ // Should not happen
+ }
+ }
+
+ /**
+ * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary
+ * as the listener will automatically be unregistered if an attempt to invoke the listener
+ * fails.
+ *
+ * @param listener that is to be unregistered.
+ */
+ public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (DBG) {
+ logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+ + " listener=" + listener);
+ }
+ try {
+ // We use the TelephonyRegistry as its runs in the system and thus is always
+ // available where as SubscriptionController could crash and not be available
+ ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ if (tr != null) {
+ tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ }
+ } catch (RemoteException ex) {
+ // Should not happen
+ }
+ }
+
+ /**
+ * Get the active SubscriptionInfo with the input subId.
+ *
+ * @param subId The unique SubscriptionInfo key in database.
+ * @return SubscriptionInfo, maybe null if its not active.
+ */
+ public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
+ if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
+ if (!isValidSubscriptionId(subId)) {
+ if (DBG) {
+ logd("[getActiveSubscriptionInfo]- invalid subId");
+ }
+ return null;
+ }
+
+ SubscriptionInfo subInfo = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return subInfo;
+
+ }
+
+ /**
+ * Get the active SubscriptionInfo associated with the iccId
+ * @param iccId the IccId of SIM card
+ * @return SubscriptionInfo, maybe null if its not active
+ * @hide
+ */
+ public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) {
+ if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
+ if (iccId == null) {
+ logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
+ return null;
+ }
+
+ SubscriptionInfo result = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the active SubscriptionInfo associated with the slotIndex
+ * @param slotIndex the slot which the subscription is inserted
+ * @return SubscriptionInfo, maybe null if its not active
+ */
+ public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
+ if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
+ if (!isValidSlotIndex(slotIndex)) {
+ logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIndex");
+ return null;
+ }
+
+ SubscriptionInfo result = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
+ mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * @return List of all SubscriptionInfo records in database,
+ * include those that were inserted before, maybe empty but not null.
+ * @hide
+ */
+ public List<SubscriptionInfo> getAllSubscriptionInfoList() {
+ if (VDBG) logd("[getAllSubscriptionInfoList]+");
+
+ List<SubscriptionInfo> result = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getAllSubInfoList(mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ return result;
+ }
+
+ /**
+ * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
+ * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
+ *
+ * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
+ * <ul>
+ * <li>
+ * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
+ * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
+ * invoked in the future.
+ * </li>
+ * <li>
+ * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
+ * </li>
+ * <li>
+ * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+ * then by {@link SubscriptionInfo#getSubscriptionId}.
+ * </li>
+ * </ul>
+ */
+ public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
+ List<SubscriptionInfo> result = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return result;
+ }
+
+ /**
+ * Gets the SubscriptionInfo(s) of all available subscriptions, if any.
+ *
+ * <p>Available subscriptions include active ones (those with a non-negative
+ * {@link SubscriptionInfo#getSimSlotIndex()}) as well as inactive but installed embedded
+ * subscriptions.
+ *
+ * <p>The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+ * {@link SubscriptionInfo#getSubscriptionId}.
+ *
+ * @return Sorted list of the current {@link SubscriptionInfo} records available on the
+ * device.
+ * <ul>
+ * <li>
+ * If null is returned the current state is unknown but if a
+ * {@link OnSubscriptionsChangedListener} has been registered
+ * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future.
+ * <li>
+ * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
+ * <li>
+ * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+ * then by {@link SubscriptionInfo#getSubscriptionId}.
+ * </ul>
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
+ List<SubscriptionInfo> result = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return result;
+ }
+
+ /**
+ * Gets the SubscriptionInfo(s) of all embedded subscriptions accessible to the calling app, if
+ * any.
+ *
+ * <p>Only those subscriptions for which the calling app has carrier privileges per the
+ * subscription metadata, if any, will be included in the returned list.
+ *
+ * <p>The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+ * {@link SubscriptionInfo#getSubscriptionId}.
+ *
+ * @return Sorted list of the current embedded {@link SubscriptionInfo} records available on the
+ * device which are accessible to the caller.
+ * <ul>
+ * <li>
+ * If null is returned the current state is unknown but if a
+ * {@link OnSubscriptionsChangedListener} has been registered
+ * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future.
+ * <li>
+ * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
+ * <li>
+ * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+ * then by {@link SubscriptionInfo#getSubscriptionId}.
+ * </ul>
+ * @hide
+ *
+ * TODO(b/35851809): Make this public.
+ */
+ public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
+ List<SubscriptionInfo> result = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return result;
+ }
+
+ /**
+ * Request a refresh of the platform cache of profile information.
+ *
+ * <p>Should be called by the EuiccService implementation whenever this information changes due
+ * to an operation done outside the scope of a request initiated by the platform to the
+ * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+ * were made through the EuiccService.
+ *
+ * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public void requestEmbeddedSubscriptionInfoListRefresh() {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.requestEmbeddedSubscriptionInfoListRefresh();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * @return the count of all subscriptions in the database, this includes
+ * all subscriptions that have been seen.
+ * @hide
+ */
+ public int getAllSubscriptionInfoCount() {
+ if (VDBG) logd("[getAllSubscriptionInfoCount]+");
+
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getAllSubInfoCount(mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * @return the current number of active subscriptions. There is no guarantee the value
+ * returned by this method will be the same as the length of the list returned by
+ * {@link #getActiveSubscriptionInfoList}.
+ */
+ public int getActiveSubscriptionInfoCount() {
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getActiveSubInfoCount(mContext.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * @return the maximum number of active subscriptions that will be returned by
+ * {@link #getActiveSubscriptionInfoList} and the value returned by
+ * {@link #getActiveSubscriptionInfoCount}.
+ */
+ public int getActiveSubscriptionInfoCountMax() {
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getActiveSubInfoCountMax();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * Add a new SubscriptionInfo to SubscriptionInfo database if needed
+ * @param iccId the IccId of the SIM card
+ * @param slotIndex the slot which the SIM is inserted
+ * @return the URL of the newly created row or the updated row
+ * @hide
+ */
+ public Uri addSubscriptionInfoRecord(String iccId, int slotIndex) {
+ if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotIndex:" + slotIndex);
+ if (iccId == null) {
+ logd("[addSubscriptionInfoRecord]- null iccId");
+ }
+ if (!isValidSlotIndex(slotIndex)) {
+ logd("[addSubscriptionInfoRecord]- invalid slotIndex");
+ }
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ // FIXME: This returns 1 on success, 0 on error should should we return it?
+ iSub.addSubInfoRecord(iccId, slotIndex);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ // FIXME: Always returns null?
+ return null;
+
+ }
+
+ /**
+ * Set SIM icon tint color by simInfo index
+ * @param tint the RGB value of icon tint color of the SIM
+ * @param subId the unique SubInfoRecord index in database
+ * @return the number of records updated
+ * @hide
+ */
+ public int setIconTint(int tint, int subId) {
+ if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
+ if (!isValidSubscriptionId(subId)) {
+ logd("[setIconTint]- fail");
+ return -1;
+ }
+
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.setIconTint(tint, subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Set display name by simInfo index
+ * @param displayName the display name of SIM card
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ * @hide
+ */
+ public int setDisplayName(String displayName, int subId) {
+ return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
+ }
+
+ /**
+ * Set display name by simInfo index with name source
+ * @param displayName the display name of SIM card
+ * @param subId the unique SubscriptionInfo index in database
+ * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
+ * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
+ * @return the number of records updated or < 0 if invalid subId
+ * @hide
+ */
+ public int setDisplayName(String displayName, int subId, long nameSource) {
+ if (VDBG) {
+ logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId
+ + " nameSource:" + nameSource);
+ }
+ if (!isValidSubscriptionId(subId)) {
+ logd("[setDisplayName]- fail");
+ return -1;
+ }
+
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Set phone number by subId
+ * @param number the phone number of the SIM
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ * @hide
+ */
+ public int setDisplayNumber(String number, int subId) {
+ if (number == null || !isValidSubscriptionId(subId)) {
+ logd("[setDisplayNumber]- fail");
+ return -1;
+ }
+
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.setDisplayNumber(number, subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Set data roaming by simInfo index
+ * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
+ * @param subId the unique SubscriptionInfo index in database
+ * @return the number of records updated
+ * @hide
+ */
+ public int setDataRoaming(int roaming, int subId) {
+ if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
+ if (roaming < 0 || !isValidSubscriptionId(subId)) {
+ logd("[setDataRoaming]- fail");
+ return -1;
+ }
+
+ int result = 0;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.setDataRoaming(roaming, subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+ }
+
+ /**
+ * Get slotIndex associated with the subscription.
+ * @return slotIndex as a positive integer or a negative value if an error either
+ * SIM_NOT_INSERTED or < 0 if an invalid slot index
+ * @hide
+ */
+ public static int getSlotIndex(int subId) {
+ if (!isValidSubscriptionId(subId)) {
+ if (DBG) {
+ logd("[getSlotIndex]- fail");
+ }
+ }
+
+ int result = INVALID_SIM_SLOT_INDEX;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getSlotIndex(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return result;
+
+ }
+
+ /** @hide */
+ public static int[] getSubId(int slotIndex) {
+ if (!isValidSlotIndex(slotIndex)) {
+ logd("[getSubId]- fail");
+ return null;
+ }
+
+ int[] subId = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getSubId(slotIndex);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return subId;
+ }
+
+ /** @hide */
+ public static int getPhoneId(int subId) {
+ if (!isValidSubscriptionId(subId)) {
+ if (DBG) {
+ logd("[getPhoneId]- fail");
+ }
+ return INVALID_PHONE_INDEX;
+ }
+
+ int result = INVALID_PHONE_INDEX;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ result = iSub.getPhoneId(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("[getPhoneId]- phoneId=" + result);
+ return result;
+
+ }
+
+ private static void logd(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ /**
+ * Returns the system's default subscription id.
+ *
+ * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
+ * For a data only device, it will return the getDefaultDataSubscriptionId.
+ * May return an INVALID_SUBSCRIPTION_ID on error.
+ *
+ * @return the "system" default subscription id.
+ */
+ public static int getDefaultSubscriptionId() {
+ int subId = INVALID_SUBSCRIPTION_ID;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getDefaultSubId();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("getDefaultSubId=" + subId);
+ return subId;
+ }
+
+ /**
+ * Returns the system's default voice subscription id.
+ *
+ * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+ *
+ * @return the default voice subscription Id.
+ */
+ public static int getDefaultVoiceSubscriptionId() {
+ int subId = INVALID_SUBSCRIPTION_ID;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getDefaultVoiceSubId();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
+ return subId;
+ }
+
+ /** @hide */
+ public void setDefaultVoiceSubId(int subId) {
+ if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId);
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.setDefaultVoiceSubId(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Return the SubscriptionInfo for default voice subscription.
+ *
+ * Will return null on data only devices, or on error.
+ *
+ * @return the SubscriptionInfo for the default voice subscription.
+ * @hide
+ */
+ public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
+ return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
+ }
+
+ /** @hide */
+ public static int getDefaultVoicePhoneId() {
+ return getPhoneId(getDefaultVoiceSubscriptionId());
+ }
+
+ /**
+ * Returns the system's default SMS subscription id.
+ *
+ * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+ *
+ * @return the default SMS subscription Id.
+ */
+ public static int getDefaultSmsSubscriptionId() {
+ int subId = INVALID_SUBSCRIPTION_ID;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getDefaultSmsSubId();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
+ return subId;
+ }
+
+ /** @hide */
+ public void setDefaultSmsSubId(int subId) {
+ if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.setDefaultSmsSubId(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Return the SubscriptionInfo for default voice subscription.
+ *
+ * Will return null on data only devices, or on error.
+ *
+ * @return the SubscriptionInfo for the default SMS subscription.
+ * @hide
+ */
+ public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
+ return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId());
+ }
+
+ /** @hide */
+ public int getDefaultSmsPhoneId() {
+ return getPhoneId(getDefaultSmsSubscriptionId());
+ }
+
+ /**
+ * Returns the system's default data subscription id.
+ *
+ * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
+ *
+ * @return the default data subscription Id.
+ */
+ public static int getDefaultDataSubscriptionId() {
+ int subId = INVALID_SUBSCRIPTION_ID;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getDefaultDataSubId();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
+ return subId;
+ }
+
+ /** @hide */
+ public void setDefaultDataSubId(int subId) {
+ if (VDBG) logd("setDataSubscription sub id = " + subId);
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.setDefaultDataSubId(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Return the SubscriptionInfo for default data subscription.
+ *
+ * Will return null on voice only devices, or on error.
+ *
+ * @return the SubscriptionInfo for the default data subscription.
+ * @hide
+ */
+ public SubscriptionInfo getDefaultDataSubscriptionInfo() {
+ return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
+ }
+
+ /** @hide */
+ public int getDefaultDataPhoneId() {
+ return getPhoneId(getDefaultDataSubscriptionId());
+ }
+
+ /** @hide */
+ public void clearSubscriptionInfo() {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.clearSubInfo();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return;
+ }
+
+ //FIXME this is vulnerable to race conditions
+ /** @hide */
+ public boolean allDefaultsSelected() {
+ if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) {
+ return false;
+ }
+ if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) {
+ return false;
+ }
+ if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * If a default is set to subscription which is not active, this will reset that default back to
+ * an invalid subscription id, i.e. < 0.
+ * @hide
+ */
+ public void clearDefaultsForInactiveSubIds() {
+ if (VDBG) logd("clearDefaultsForInactiveSubIds");
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.clearDefaultsForInactiveSubIds();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * @return true if a valid subId else false
+ * @hide
+ */
+ public static boolean isValidSubscriptionId(int subId) {
+ return subId > INVALID_SUBSCRIPTION_ID ;
+ }
+
+ /**
+ * @return true if subId is an usable subId value else false. A
+ * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
+ * @hide
+ */
+ public static boolean isUsableSubIdValue(int subId) {
+ return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
+ }
+
+ /** @hide */
+ public static boolean isValidSlotIndex(int slotIndex) {
+ return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSimCount();
+ }
+
+ /** @hide */
+ public static boolean isValidPhoneId(int phoneId) {
+ return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
+ }
+
+ /** @hide */
+ public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (subIds != null && subIds.length > 0) {
+ putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
+ } else {
+ logd("putPhoneIdAndSubIdExtra: no valid subs");
+ }
+ }
+
+ /** @hide */
+ public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
+ if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
+ intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+ //FIXME this is using phoneId and slotIndex interchangeably
+ //Eventually, this should be removed as it is not the slot id
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+ }
+
+ /**
+ * @return the list of subId's that are active,
+ * is never null but the length maybe 0.
+ * @hide
+ */
+ public @NonNull int[] getActiveSubscriptionIdList() {
+ int[] subId = null;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getActiveSubIdList();
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (subId == null) {
+ subId = new int[0];
+ }
+
+ return subId;
+
+ }
+
+ /**
+ * Returns true if the device is considered roaming on the current
+ * network for a subscription.
+ * <p>
+ * Availability: Only when user registered to a network.
+ *
+ * @param subId The subscription ID
+ * @return true if the network for the subscription is roaming, false otherwise
+ */
+ public boolean isNetworkRoaming(int subId) {
+ final int phoneId = getPhoneId(subId);
+ if (phoneId < 0) {
+ // What else can we do?
+ return false;
+ }
+ return TelephonyManager.getDefault().isNetworkRoaming(subId);
+ }
+
+ /**
+ * Returns a constant indicating the state of sim for the slot index.
+ *
+ * @param slotIndex
+ *
+ * {@See TelephonyManager#SIM_STATE_UNKNOWN}
+ * {@See TelephonyManager#SIM_STATE_ABSENT}
+ * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
+ * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
+ * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
+ * {@See TelephonyManager#SIM_STATE_READY}
+ * {@See TelephonyManager#SIM_STATE_NOT_READY}
+ * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
+ * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
+ *
+ * {@hide}
+ */
+ public static int getSimStateForSlotIndex(int slotIndex) {
+ int simState = TelephonyManager.SIM_STATE_UNKNOWN;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ simState = iSub.getSimStateForSlotIndex(slotIndex);
+ }
+ } catch (RemoteException ex) {
+ }
+
+ return simState;
+ }
+
+ /**
+ * Store properties associated with SubscriptionInfo in database
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in database associated with SubscriptionInfo
+ * @param propValue Value to store in DB for particular subId & column name
+ * @hide
+ */
+ public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ iSub.setSubscriptionProperty(subId, propKey, propValue);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
+ * Store properties associated with SubscriptionInfo in database
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @return Value associated with subId and propKey column in database
+ * @hide
+ */
+ private static String getSubscriptionProperty(int subId, String propKey,
+ Context context) {
+ String resultValue = null;
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ resultValue = iSub.getSubscriptionProperty(subId, propKey,
+ context.getOpPackageName());
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return resultValue;
+ }
+
+ /**
+ * Returns boolean value corresponding to query result.
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @param defValue Default boolean value to be returned
+ * @return boolean result value to be returned
+ * @hide
+ */
+ public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
+ boolean defValue, Context context) {
+ String result = getSubscriptionProperty(subId, propKey, context);
+ if (result != null) {
+ try {
+ return Integer.parseInt(result) == 1;
+ } catch (NumberFormatException err) {
+ logd("getBooleanSubscriptionProperty NumberFormat exception");
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Returns integer value corresponding to query result.
+ * @param subId Subscription Id of Subscription
+ * @param propKey Column name in SubscriptionInfo database
+ * @param defValue Default integer value to be returned
+ * @return integer result value to be returned
+ * @hide
+ */
+ public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
+ Context context) {
+ String result = getSubscriptionProperty(subId, propKey, context);
+ if (result != null) {
+ try {
+ return Integer.parseInt(result);
+ } catch (NumberFormatException err) {
+ logd("getBooleanSubscriptionProperty NumberFormat exception");
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Returns the resources associated with Subscription.
+ * @param context Context object
+ * @param subId Subscription Id of Subscription who's resources are required
+ * @return Resources associated with Subscription.
+ * @hide
+ */
+ public static Resources getResourcesForSubId(Context context, int subId) {
+ final SubscriptionInfo subInfo =
+ SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
+
+ Configuration config = context.getResources().getConfiguration();
+ Configuration newConfig = new Configuration();
+ newConfig.setTo(config);
+ if (subInfo != null) {
+ newConfig.mcc = subInfo.getMcc();
+ newConfig.mnc = subInfo.getMnc();
+ if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
+ }
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ DisplayMetrics newMetrics = new DisplayMetrics();
+ newMetrics.setTo(metrics);
+ return new Resources(context.getResources().getAssets(), newMetrics, newConfig);
+ }
+
+ /**
+ * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
+ * and the SIM providing the subscription is present in a slot and in "LOADED" state.
+ * @hide
+ */
+ public boolean isActiveSubId(int subId) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.isActiveSubId(subId);
+ }
+ } catch (RemoteException ex) {
+ }
+ return false;
+ }
+
+ /**
+ * Get the description of the billing relationship plan between a carrier
+ * and a specific subscriber.
+ * <p>
+ * This method is only accessible to the following narrow set of apps:
+ * <ul>
+ * <li>The carrier app for this subscriberId, as determined by
+ * {@link TelephonyManager#hasCarrierPrivileges(int)}.
+ * <li>The carrier app explicitly delegated access through
+ * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+ * </ul>
+ *
+ * @param subId the subscriber this relationship applies to
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
+ final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+ .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ try {
+ SubscriptionPlan[] subscriptionPlans =
+ npm.getSubscriptionPlans(subId, mContext.getOpPackageName());
+ return subscriptionPlans == null
+ ? Collections.emptyList() : Arrays.asList(subscriptionPlans);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the description of the billing relationship plan between a carrier
+ * and a specific subscriber.
+ * <p>
+ * This method is only accessible to the following narrow set of apps:
+ * <ul>
+ * <li>The carrier app for this subscriberId, as determined by
+ * {@link TelephonyManager#hasCarrierPrivileges(int)}.
+ * <li>The carrier app explicitly delegated access through
+ * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+ * </ul>
+ *
+ * @param subId the subscriber this relationship applies to
+ * @param plans the list of plans. The first plan is always the primary and
+ * most important plan. Any additional plans are secondary and
+ * may not be displayed or used by decision making logic.
+ * @hide
+ */
+ @SystemApi
+ public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
+ final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+ .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ try {
+ npm.setSubscriptionPlans(subId, plans.toArray(new SubscriptionPlan[plans.size()]),
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/android/telephony/SubscriptionPlan.java b/android/telephony/SubscriptionPlan.java
new file mode 100644
index 00000000..265e3e7c
--- /dev/null
+++ b/android/telephony/SubscriptionPlan.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+import android.util.RecurrenceRule;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Period;
+import java.time.ZonedDateTime;
+import java.util.Iterator;
+
+/**
+ * Description of a billing relationship plan between a carrier and a specific
+ * subscriber. This information is used to present more useful UI to users, such
+ * as explaining how much mobile data they have remaining, and what will happen
+ * when they run out.
+ *
+ * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
+ * @see SubscriptionManager#getSubscriptionPlans(int)
+ * @hide
+ */
+@SystemApi
+public final class SubscriptionPlan implements Parcelable {
+ /** {@hide} */
+ @IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
+ LIMIT_BEHAVIOR_UNKNOWN,
+ LIMIT_BEHAVIOR_DISABLED,
+ LIMIT_BEHAVIOR_BILLED,
+ LIMIT_BEHAVIOR_THROTTLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LimitBehavior {}
+
+ /** When a resource limit is hit, the behavior is unknown. */
+ public static final int LIMIT_BEHAVIOR_UNKNOWN = -1;
+ /** When a resource limit is hit, access is disabled. */
+ public static final int LIMIT_BEHAVIOR_DISABLED = 0;
+ /** When a resource limit is hit, the user is billed automatically. */
+ public static final int LIMIT_BEHAVIOR_BILLED = 1;
+ /** When a resource limit is hit, access is throttled to a slower rate. */
+ public static final int LIMIT_BEHAVIOR_THROTTLED = 2;
+
+ /** Value indicating a number of bytes is unknown. */
+ public static final long BYTES_UNKNOWN = -1;
+ /** Value indicating a number of bytes is unlimited. */
+ public static final long BYTES_UNLIMITED = Long.MAX_VALUE;
+
+ /** Value indicating a timestamp is unknown. */
+ public static final long TIME_UNKNOWN = -1;
+
+ private final RecurrenceRule cycleRule;
+ private CharSequence title;
+ private CharSequence summary;
+ private long dataLimitBytes = BYTES_UNKNOWN;
+ private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
+ private long dataUsageBytes = BYTES_UNKNOWN;
+ private long dataUsageTime = TIME_UNKNOWN;
+
+ private SubscriptionPlan(RecurrenceRule cycleRule) {
+ this.cycleRule = Preconditions.checkNotNull(cycleRule);
+ }
+
+ private SubscriptionPlan(Parcel source) {
+ cycleRule = source.readParcelable(null);
+ title = source.readCharSequence();
+ summary = source.readCharSequence();
+ dataLimitBytes = source.readLong();
+ dataLimitBehavior = source.readInt();
+ dataUsageBytes = source.readLong();
+ dataUsageTime = source.readLong();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(cycleRule, flags);
+ dest.writeCharSequence(title);
+ dest.writeCharSequence(summary);
+ dest.writeLong(dataLimitBytes);
+ dest.writeInt(dataLimitBehavior);
+ dest.writeLong(dataUsageBytes);
+ dest.writeLong(dataUsageTime);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("SubscriptionPlan{")
+ .append("cycleRule=").append(cycleRule)
+ .append(" title=").append(title)
+ .append(" summary=").append(summary)
+ .append(" dataLimitBytes=").append(dataLimitBytes)
+ .append(" dataLimitBehavior=").append(dataLimitBehavior)
+ .append(" dataUsageBytes=").append(dataUsageBytes)
+ .append(" dataUsageTime=").append(dataUsageTime)
+ .append("}").toString();
+ }
+
+ public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
+ @Override
+ public SubscriptionPlan createFromParcel(Parcel source) {
+ return new SubscriptionPlan(source);
+ }
+
+ @Override
+ public SubscriptionPlan[] newArray(int size) {
+ return new SubscriptionPlan[size];
+ }
+ };
+
+ /** {@hide} */
+ public @NonNull RecurrenceRule getCycleRule() {
+ return cycleRule;
+ }
+
+ /** Return the short title of this plan. */
+ public @Nullable CharSequence getTitle() {
+ return title;
+ }
+
+ /** Return the short summary of this plan. */
+ public @Nullable CharSequence getSummary() {
+ return summary;
+ }
+
+ /**
+ * Return the usage threshold at which data access changes according to
+ * {@link #getDataLimitBehavior()}.
+ */
+ public @BytesLong long getDataLimitBytes() {
+ return dataLimitBytes;
+ }
+
+ /**
+ * Return the behavior of data access when usage reaches
+ * {@link #getDataLimitBytes()}.
+ */
+ public @LimitBehavior int getDataLimitBehavior() {
+ return dataLimitBehavior;
+ }
+
+ /**
+ * Return a snapshot of currently known mobile data usage at
+ * {@link #getDataUsageTime()}.
+ */
+ public @BytesLong long getDataUsageBytes() {
+ return dataUsageBytes;
+ }
+
+ /**
+ * Return the time at which {@link #getDataUsageBytes()} was valid.
+ */
+ public @CurrentTimeMillisLong long getDataUsageTime() {
+ return dataUsageTime;
+ }
+
+ /**
+ * Return an iterator that will return all valid data usage cycles based on
+ * any recurrence rules. The iterator starts from the currently active cycle
+ * and walks backwards through time.
+ */
+ public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+ return cycleRule.cycleIterator();
+ }
+
+ /**
+ * Builder for a {@link SubscriptionPlan}.
+ */
+ public static class Builder {
+ private final SubscriptionPlan plan;
+
+ /** {@hide} */
+ public Builder(ZonedDateTime start, ZonedDateTime end, Period period) {
+ plan = new SubscriptionPlan(new RecurrenceRule(start, end, period));
+ }
+
+ /**
+ * Start defining a {@link SubscriptionPlan} that covers a very specific
+ * window of time, and never automatically recurs.
+ */
+ public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
+ if (!end.isAfter(start)) {
+ throw new IllegalArgumentException(
+ "End " + end + " isn't after start " + start);
+ }
+ return new Builder(start, end, null);
+ }
+
+ /**
+ * Start defining a {@link SubscriptionPlan} that will recur
+ * automatically every month. It will always recur on the same day of a
+ * particular month. When a particular month ends before the defined
+ * recurrence day, the plan will recur on the last instant of that
+ * month.
+ */
+ public static Builder createRecurringMonthly(ZonedDateTime start) {
+ return new Builder(start, null, Period.ofMonths(1));
+ }
+
+ /**
+ * Start defining a {@link SubscriptionPlan} that will recur
+ * automatically every week.
+ */
+ public static Builder createRecurringWeekly(ZonedDateTime start) {
+ return new Builder(start, null, Period.ofDays(7));
+ }
+
+ /**
+ * Start defining a {@link SubscriptionPlan} that will recur
+ * automatically every day.
+ */
+ public static Builder createRecurringDaily(ZonedDateTime start) {
+ return new Builder(start, null, Period.ofDays(1));
+ }
+
+ public SubscriptionPlan build() {
+ return plan;
+ }
+
+ /** Set the short title of this plan. */
+ public Builder setTitle(@Nullable CharSequence title) {
+ plan.title = title;
+ return this;
+ }
+
+ /** Set the short summary of this plan. */
+ public Builder setSummary(@Nullable CharSequence summary) {
+ plan.summary = summary;
+ return this;
+ }
+
+ /**
+ * Set the usage threshold at which data access changes.
+ *
+ * @param dataLimitBytes the usage threshold at which data access
+ * changes
+ * @param dataLimitBehavior the behavior of data access when usage
+ * reaches the threshold
+ */
+ public Builder setDataLimit(@BytesLong long dataLimitBytes,
+ @LimitBehavior int dataLimitBehavior) {
+ if (dataLimitBytes < 0) {
+ throw new IllegalArgumentException("Limit bytes must be positive");
+ }
+ if (dataLimitBehavior < 0) {
+ throw new IllegalArgumentException("Limit behavior must be defined");
+ }
+ plan.dataLimitBytes = dataLimitBytes;
+ plan.dataLimitBehavior = dataLimitBehavior;
+ return this;
+ }
+
+ /**
+ * Set a snapshot of currently known mobile data usage.
+ *
+ * @param dataUsageBytes the currently known mobile data usage
+ * @param dataUsageTime the time at which this snapshot was valid
+ */
+ public Builder setDataUsage(@BytesLong long dataUsageBytes,
+ @CurrentTimeMillisLong long dataUsageTime) {
+ if (dataUsageBytes < 0) {
+ throw new IllegalArgumentException("Usage bytes must be positive");
+ }
+ if (dataUsageTime < 0) {
+ throw new IllegalArgumentException("Usage time must be positive");
+ }
+ plan.dataUsageBytes = dataUsageBytes;
+ plan.dataUsageTime = dataUsageTime;
+ return this;
+ }
+ }
+}
diff --git a/android/telephony/TelephonyHistogram.java b/android/telephony/TelephonyHistogram.java
new file mode 100644
index 00000000..e1c3d7b3
--- /dev/null
+++ b/android/telephony/TelephonyHistogram.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Parcelable class to store Telephony histogram.
+ * @hide
+ */
+@SystemApi
+public final class TelephonyHistogram implements Parcelable {
+ // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with
+ // RIL calls. Similarly we can have any other Telephony histogram.
+ private final int mCategory;
+
+ // Unique Id identifying a sample within particular category of histogram
+ private final int mId;
+
+ // Min time taken in ms
+ private int mMinTimeMs;
+
+ // Max time taken in ms
+ private int mMaxTimeMs;
+
+ // Average time taken in ms
+ private int mAverageTimeMs;
+
+ // Total count of samples
+ private int mSampleCount;
+
+ // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
+ private int[] mInitialTimings;
+
+ // Total number of time ranges expected (must be greater than 1)
+ private final int mBucketCount;
+
+ // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
+ // after totalTimeCount is #RANGE_CALCULATION_COUNT.
+ private final int[] mBucketEndPoints;
+
+ // Array storing counts for each time range starting from smallest value range
+ private final int[] mBucketCounters;
+
+ /**
+ * Constant for Telephony category
+ */
+ public static final int TELEPHONY_CATEGORY_RIL = 1;
+
+ // Count of Histogram samples after which time buckets are created.
+ private static final int RANGE_CALCULATION_COUNT = 10;
+
+
+ // Constant used to indicate #initialTimings is null while parceling
+ private static final int ABSENT = 0;
+
+ // Constant used to indicate #initialTimings is not null while parceling
+ private static final int PRESENT = 1;
+
+ // Throws exception if #totalBuckets is not greater than one.
+ public TelephonyHistogram (int category, int id, int bucketCount) {
+ if (bucketCount <= 1) {
+ throw new IllegalArgumentException("Invalid number of buckets");
+ }
+ mCategory = category;
+ mId = id;
+ mMinTimeMs = Integer.MAX_VALUE;
+ mMaxTimeMs = 0;
+ mAverageTimeMs = 0;
+ mSampleCount = 0;
+ mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+ mBucketCount = bucketCount;
+ mBucketEndPoints = new int[bucketCount - 1];
+ mBucketCounters = new int[bucketCount];
+ }
+
+ public TelephonyHistogram(TelephonyHistogram th) {
+ mCategory = th.getCategory();
+ mId = th.getId();
+ mMinTimeMs = th.getMinTime();
+ mMaxTimeMs = th.getMaxTime();
+ mAverageTimeMs = th.getAverageTime();
+ mSampleCount = th.getSampleCount();
+ mInitialTimings = th.getInitialTimings();
+ mBucketCount = th.getBucketCount();
+ mBucketEndPoints = th.getBucketEndPoints();
+ mBucketCounters = th.getBucketCounters();
+ }
+
+ public int getCategory() {
+ return mCategory;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getMinTime() {
+ return mMinTimeMs;
+ }
+
+ public int getMaxTime() {
+ return mMaxTimeMs;
+ }
+
+ public int getAverageTime() {
+ return mAverageTimeMs;
+ }
+
+ public int getSampleCount () {
+ return mSampleCount;
+ }
+
+ private int[] getInitialTimings() {
+ return mInitialTimings;
+ }
+
+ public int getBucketCount() {
+ return mBucketCount;
+ }
+
+ public int[] getBucketEndPoints() {
+ if (mSampleCount > 1 && mSampleCount < 10) {
+ int[] tempEndPoints = new int[mBucketCount - 1];
+ calculateBucketEndPoints(tempEndPoints);
+ return tempEndPoints;
+ } else {
+ return getDeepCopyOfArray(mBucketEndPoints);
+ }
+ }
+
+ public int[] getBucketCounters() {
+ if (mSampleCount > 1 && mSampleCount < 10) {
+ int[] tempEndPoints = new int[mBucketCount - 1];
+ int[] tempBucketCounters = new int[mBucketCount];
+ calculateBucketEndPoints(tempEndPoints);
+ for (int j = 0; j < mSampleCount; j++) {
+ addToBucketCounter(tempEndPoints, tempBucketCounters, mInitialTimings[j]);
+ }
+ return tempBucketCounters;
+ } else {
+ return getDeepCopyOfArray(mBucketCounters);
+ }
+ }
+
+ private int[] getDeepCopyOfArray(int[] array) {
+ int[] clone = new int[array.length];
+ System.arraycopy(array, 0, clone, 0, array.length);
+ return clone;
+ }
+
+ private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
+ int i;
+ for (i = 0; i < bucketEndPoints.length; i++) {
+ if (time <= bucketEndPoints[i]) {
+ bucketCounters[i]++;
+ return;
+ }
+ }
+ bucketCounters[i]++;
+ }
+
+ private void calculateBucketEndPoints(int[] bucketEndPoints) {
+ for (int i = 1; i < mBucketCount; i++) {
+ int endPt = mMinTimeMs + (i * (mMaxTimeMs - mMinTimeMs)) / mBucketCount;
+ bucketEndPoints[i - 1] = endPt;
+ }
+ }
+
+ // Add new value of time taken
+ // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is
+ // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When
+ // totalTimeCount = RANGE_CALCULATION_COUNT, based on the min, max time & the number of buckets
+ // expected, bucketEndPoints[] would be calculated. Then bucketCounters[] would be filled up
+ // using values stored in initialTimings[]. Thereafter bucketCounters[] will always be updated.
+ public void addTimeTaken(int time) {
+ // Initialize all fields if its first entry or if integer overflow is going to occur while
+ // trying to calculate averageTime
+ if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
+ if (mSampleCount == 0) {
+ mMinTimeMs = time;
+ mMaxTimeMs = time;
+ mAverageTimeMs = time;
+ } else {
+ mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+ }
+ mSampleCount = 1;
+ Arrays.fill(mInitialTimings, 0);
+ mInitialTimings[0] = time;
+ Arrays.fill(mBucketEndPoints, 0);
+ Arrays.fill(mBucketCounters, 0);
+ } else {
+ if (time < mMinTimeMs) {
+ mMinTimeMs = time;
+ }
+ if (time > mMaxTimeMs) {
+ mMaxTimeMs = time;
+ }
+ long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
+ mAverageTimeMs = (int)(totalTime/++mSampleCount);
+
+ if (mSampleCount < RANGE_CALCULATION_COUNT) {
+ mInitialTimings[mSampleCount - 1] = time;
+ } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
+ mInitialTimings[mSampleCount - 1] = time;
+
+ // Calculate bucket endpoints based on bucketCount expected
+ calculateBucketEndPoints(mBucketEndPoints);
+
+ // Use values stored in initialTimings[] to update bucketCounters
+ for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
+ addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
+ }
+ mInitialTimings = null;
+ } else {
+ addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
+ }
+
+ }
+ }
+
+ public String toString() {
+ String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
+ + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
+ if (mSampleCount < RANGE_CALCULATION_COUNT) {
+ return basic;
+ } else {
+ StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
+ for (int i = 0; i < mBucketEndPoints.length; i++) {
+ intervals.append(" " + mBucketEndPoints[i]);
+ }
+ intervals.append(" Interval counters:");
+ for (int i = 0; i < mBucketCounters.length; i++) {
+ intervals.append(" " + mBucketCounters[i]);
+ }
+ return basic + intervals;
+ }
+ }
+
+ public static final Parcelable.Creator<TelephonyHistogram> CREATOR =
+ new Parcelable.Creator<TelephonyHistogram> () {
+
+ @Override
+ public TelephonyHistogram createFromParcel(Parcel in) {
+ return new TelephonyHistogram(in);
+ }
+
+ @Override
+ public TelephonyHistogram[] newArray(int size) {
+ return new TelephonyHistogram[size];
+ }
+ };
+
+ public TelephonyHistogram(Parcel in) {
+ mCategory = in.readInt();
+ mId = in.readInt();
+ mMinTimeMs = in.readInt();
+ mMaxTimeMs = in.readInt();
+ mAverageTimeMs = in.readInt();
+ mSampleCount = in.readInt();
+ if (in.readInt() == PRESENT) {
+ mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+ in.readIntArray(mInitialTimings);
+ }
+ mBucketCount = in.readInt();
+ mBucketEndPoints = new int[mBucketCount - 1];
+ in.readIntArray(mBucketEndPoints);
+ mBucketCounters = new int[mBucketCount];
+ in.readIntArray(mBucketCounters);
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCategory);
+ out.writeInt(mId);
+ out.writeInt(mMinTimeMs);
+ out.writeInt(mMaxTimeMs);
+ out.writeInt(mAverageTimeMs);
+ out.writeInt(mSampleCount);
+ if (mInitialTimings == null) {
+ out.writeInt(ABSENT);
+ } else {
+ out.writeInt(PRESENT);
+ out.writeIntArray(mInitialTimings);
+ }
+ out.writeInt(mBucketCount);
+ out.writeIntArray(mBucketEndPoints);
+ out.writeIntArray(mBucketCounters);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/android/telephony/TelephonyManager.java b/android/telephony/TelephonyManager.java
new file mode 100644
index 00000000..cde0bdfd
--- /dev/null
+++ b/android/telephony/TelephonyManager.java
@@ -0,0 +1,6853 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.WorkerThread;
+import android.app.ActivityThread;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkStats;
+import android.net.Uri;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.service.carrier.CarrierIdentifier;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.Log;
+
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telecom.ITelecomService;
+import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.IPhoneSubInfo;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.OperatorInfo;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.TelephonyProperties;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Provides access to information about the telephony services on
+ * the device. Applications can use the methods in this class to
+ * determine telephony services and states, as well as to access some
+ * types of subscriber information. Applications can also register
+ * a listener to receive notification of telephony state changes.
+ * <p>
+ * The returned TelephonyManager will use the default subscription for all calls.
+ * To call an API for a specific subscription, use {@link #createForSubscriptionId(int)}. e.g.
+ * <code>
+ * telephonyManager = defaultSubTelephonyManager.createForSubscriptionId(subId);
+ * </code>
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application cannot access the protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * the methods through which you access the protected information.
+ */
+@SystemService(Context.TELEPHONY_SERVICE)
+public class TelephonyManager {
+ private static final String TAG = "TelephonyManager";
+
+ /**
+ * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
+ * into the ResultReceiver Bundle.
+ * @hide
+ */
+ public static final String MODEM_ACTIVITY_RESULT_KEY =
+ BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
+
+ private static ITelephonyRegistry sRegistry;
+
+ /**
+ * The allowed states of Wi-Fi calling.
+ *
+ * @hide
+ */
+ public interface WifiCallingChoices {
+ /** Always use Wi-Fi calling */
+ static final int ALWAYS_USE = 0;
+ /** Ask the user whether to use Wi-Fi on every call */
+ static final int ASK_EVERY_TIME = 1;
+ /** Never use Wi-Fi calling */
+ static final int NEVER_USE = 2;
+ }
+
+ /** The otaspMode passed to PhoneStateListener#onOtaspChanged */
+ /** @hide */
+ static public final int OTASP_UNINITIALIZED = 0;
+ /** @hide */
+ static public final int OTASP_UNKNOWN = 1;
+ /** @hide */
+ static public final int OTASP_NEEDED = 2;
+ /** @hide */
+ static public final int OTASP_NOT_NEEDED = 3;
+ /* OtaUtil has conflict enum 4: OtaUtils.OTASP_FAILURE_SPC_RETRIES */
+ /** @hide */
+ static public final int OTASP_SIM_UNPROVISIONED = 5;
+
+
+ /** @hide */
+ static public final int KEY_TYPE_EPDG = 1;
+
+ /** @hide */
+ static public final int KEY_TYPE_WLAN = 2;
+
+ private final Context mContext;
+ private final int mSubId;
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyScanManager mTelephonyScanManager;
+
+ private static String multiSimConfig =
+ SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG);
+
+ /** Enum indicating multisim variants
+ * DSDS - Dual SIM Dual Standby
+ * DSDA - Dual SIM Dual Active
+ * TSTS - Triple SIM Triple Standby
+ **/
+ /** @hide */
+ public enum MultiSimVariants {
+ DSDS,
+ DSDA,
+ TSTS,
+ UNKNOWN
+ };
+
+ /** @hide */
+ public TelephonyManager(Context context) {
+ this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ }
+
+ /** @hide */
+ public TelephonyManager(Context context, int subId) {
+ mSubId = subId;
+ Context appContext = context.getApplicationContext();
+ if (appContext != null) {
+ mContext = appContext;
+ } else {
+ mContext = context;
+ }
+ mSubscriptionManager = SubscriptionManager.from(mContext);
+
+ if (sRegistry == null) {
+ sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ }
+ }
+
+ /** @hide */
+ private TelephonyManager() {
+ mContext = null;
+ mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ private static TelephonyManager sInstance = new TelephonyManager();
+
+ /** @hide
+ /* @deprecated - use getSystemService as described above */
+ public static TelephonyManager getDefault() {
+ return sInstance;
+ }
+
+ private String getOpPackageName() {
+ // For legacy reasons the TelephonyManager has API for getting
+ // a static instance with no context set preventing us from
+ // getting the op package name. As a workaround we do a best
+ // effort and get the context from the current activity thread.
+ if (mContext != null) {
+ return mContext.getOpPackageName();
+ }
+ return ActivityThread.currentOpPackageName();
+ }
+
+ /**
+ * Returns the multi SIM variant
+ * Returns DSDS for Dual SIM Dual Standby
+ * Returns DSDA for Dual SIM Dual Active
+ * Returns TSTS for Triple SIM Triple Standby
+ * Returns UNKNOWN for others
+ */
+ /** {@hide} */
+ public MultiSimVariants getMultiSimConfiguration() {
+ String mSimConfig =
+ SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG);
+ if (mSimConfig.equals("dsds")) {
+ return MultiSimVariants.DSDS;
+ } else if (mSimConfig.equals("dsda")) {
+ return MultiSimVariants.DSDA;
+ } else if (mSimConfig.equals("tsts")) {
+ return MultiSimVariants.TSTS;
+ } else {
+ return MultiSimVariants.UNKNOWN;
+ }
+ }
+
+
+ /**
+ * Returns the number of phones available.
+ * Returns 0 if none of voice, sms, data is not supported
+ * Returns 1 for Single standby mode (Single SIM functionality)
+ * Returns 2 for Dual standby mode.(Dual SIM functionality)
+ */
+ public int getPhoneCount() {
+ int phoneCount = 1;
+ switch (getMultiSimConfiguration()) {
+ case UNKNOWN:
+ // if voice or sms or data is supported, return 1 otherwise 0
+ if (isVoiceCapable() || isSmsCapable()) {
+ phoneCount = 1;
+ } else {
+ // todo: try to clean this up further by getting rid of the nested conditions
+ if (mContext == null) {
+ phoneCount = 1;
+ } else {
+ // check for data support
+ ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm == null) {
+ phoneCount = 1;
+ } else {
+ if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+ phoneCount = 1;
+ } else {
+ phoneCount = 0;
+ }
+ }
+ }
+ }
+ break;
+ case DSDS:
+ case DSDA:
+ phoneCount = PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+ break;
+ case TSTS:
+ phoneCount = PhoneConstants.MAX_PHONE_COUNT_TRI_SIM;
+ break;
+ }
+ return phoneCount;
+ }
+
+ /** {@hide} */
+ public static TelephonyManager from(Context context) {
+ return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ /**
+ * Create a new TelephonyManager object pinned to the given subscription ID.
+ *
+ * @return a TelephonyManager that uses the given subId for all calls.
+ */
+ public TelephonyManager createForSubscriptionId(int subId) {
+ // Don't reuse any TelephonyManager objects.
+ return new TelephonyManager(mContext, subId);
+ }
+
+ /**
+ * Create a new TelephonyManager object pinned to the subscription ID associated with the given
+ * phone account.
+ *
+ * @return a TelephonyManager that uses the given phone account for all calls, or {@code null}
+ * if the phone account does not correspond to a valid subscription ID.
+ */
+ @Nullable
+ public TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ int subId = getSubIdForPhoneAccountHandle(phoneAccountHandle);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return null;
+ }
+ return new TelephonyManager(mContext, subId);
+ }
+
+ /** {@hide} */
+ public boolean isMultiSimEnabled() {
+ return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") ||
+ multiSimConfig.equals("tsts"));
+ }
+
+ //
+ // Broadcast Intent actions
+ //
+
+ /**
+ * Broadcast intent action indicating that the call state
+ * on the device has changed.
+ *
+ * <p>
+ * The {@link #EXTRA_STATE} extra indicates the new call state.
+ * If the new state is RINGING, a second extra
+ * {@link #EXTRA_INCOMING_NUMBER} provides the incoming phone number as
+ * a String.
+ *
+ * <p class="note">
+ * This was a {@link android.content.Context#sendStickyBroadcast sticky}
+ * broadcast in version 1.0, but it is no longer sticky.
+ * Instead, use {@link #getCallState} to synchronously query the current call state.
+ *
+ * @see #EXTRA_STATE
+ * @see #EXTRA_INCOMING_NUMBER
+ * @see #getCallState
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final String ACTION_PHONE_STATE_CHANGED =
+ "android.intent.action.PHONE_STATE";
+
+ /**
+ * The Phone app sends this intent when a user opts to respond-via-message during an incoming
+ * call. By default, the device's default SMS app consumes this message and sends a text message
+ * to the caller. A third party app can also provide this functionality by consuming this Intent
+ * with a {@link android.app.Service} and sending the message using its own messaging system.
+ * <p>The intent contains a URI (available from {@link android.content.Intent#getData})
+ * describing the recipient, using either the {@code sms:}, {@code smsto:}, {@code mms:},
+ * or {@code mmsto:} URI schema. Each of these URI schema carry the recipient information the
+ * same way: the path part of the URI contains the recipient's phone number or a comma-separated
+ * set of phone numbers if there are multiple recipients. For example, {@code
+ * smsto:2065551234}.</p>
+ *
+ * <p>The intent may also contain extras for the message text (in {@link
+ * android.content.Intent#EXTRA_TEXT}) and a message subject
+ * (in {@link android.content.Intent#EXTRA_SUBJECT}).</p>
+ *
+ * <p class="note"><strong>Note:</strong>
+ * The intent-filter that consumes this Intent needs to be in a {@link android.app.Service}
+ * that requires the
+ * permission {@link android.Manifest.permission#SEND_RESPOND_VIA_MESSAGE}.</p>
+ * <p>For example, the service that receives this intent can be declared in the manifest file
+ * with an intent filter like this:</p>
+ * <pre>
+ * &lt;!-- Service that delivers SMS messages received from the phone "quick response" -->
+ * &lt;service android:name=".HeadlessSmsSendService"
+ * android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
+ * android:exported="true" >
+ * &lt;intent-filter>
+ * &lt;action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+ * &lt;category android:name="android.intent.category.DEFAULT" />
+ * &lt;data android:scheme="sms" />
+ * &lt;data android:scheme="smsto" />
+ * &lt;data android:scheme="mms" />
+ * &lt;data android:scheme="mmsto" />
+ * &lt;/intent-filter>
+ * &lt;/service></pre>
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String ACTION_RESPOND_VIA_MESSAGE =
+ "android.intent.action.RESPOND_VIA_MESSAGE";
+
+ /**
+ * The emergency dialer may choose to present activities with intent filters for this
+ * action as emergency assistance buttons that launch the activity when clicked.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_EMERGENCY_ASSISTANCE =
+ "android.telephony.action.EMERGENCY_ASSISTANCE";
+
+ /**
+ * A boolean meta-data value indicating whether the voicemail settings should be hidden in the
+ * call settings page launched by
+ * {@link android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS}.
+ * Dialer implementations (see {@link android.telecom.TelecomManager#getDefaultDialerPackage()})
+ * which would also like to manage voicemail settings should set this meta-data to {@code true}
+ * in the manifest registration of their application.
+ *
+ * @see android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS
+ * @see #ACTION_CONFIGURE_VOICEMAIL
+ * @see #EXTRA_HIDE_PUBLIC_SETTINGS
+ */
+ public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU =
+ "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
+
+ /**
+ * Open the voicemail settings activity to make changes to voicemail configuration.
+ *
+ * <p>
+ * The {@link #EXTRA_HIDE_PUBLIC_SETTINGS} hides settings the dialer will modify through public
+ * API if set.
+ *
+ * @see #EXTRA_HIDE_PUBLIC_SETTINGS
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CONFIGURE_VOICEMAIL =
+ "android.telephony.action.CONFIGURE_VOICEMAIL";
+
+ /**
+ * The boolean value indicating whether the voicemail settings activity launched by {@link
+ * #ACTION_CONFIGURE_VOICEMAIL} should hide settings accessible through public API. This is
+ * used by dialer implementations which provides their own voicemail settings UI, but still
+ * needs to expose device specific voicemail settings to the user.
+ *
+ * @see #ACTION_CONFIGURE_VOICEMAIL
+ * @see #METADATA_HIDE_VOICEMAIL_SETTINGS_MENU
+ */
+ public static final String EXTRA_HIDE_PUBLIC_SETTINGS =
+ "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
+
+ /**
+ * @hide
+ */
+ public static final boolean EMERGENCY_ASSISTANCE_ENABLED = true;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+ * for a String containing the new call state.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String)}.
+ *
+ * @see #EXTRA_STATE_IDLE
+ * @see #EXTRA_STATE_RINGING
+ * @see #EXTRA_STATE_OFFHOOK
+ */
+ public static final String EXTRA_STATE = PhoneConstants.STATE_KEY;
+
+ /**
+ * Value used with {@link #EXTRA_STATE} corresponding to
+ * {@link #CALL_STATE_IDLE}.
+ */
+ public static final String EXTRA_STATE_IDLE = PhoneConstants.State.IDLE.toString();
+
+ /**
+ * Value used with {@link #EXTRA_STATE} corresponding to
+ * {@link #CALL_STATE_RINGING}.
+ */
+ public static final String EXTRA_STATE_RINGING = PhoneConstants.State.RINGING.toString();
+
+ /**
+ * Value used with {@link #EXTRA_STATE} corresponding to
+ * {@link #CALL_STATE_OFFHOOK}.
+ */
+ public static final String EXTRA_STATE_OFFHOOK = PhoneConstants.State.OFFHOOK.toString();
+
+ /**
+ * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+ * for a String containing the incoming phone number.
+ * Only valid when the new call state is RINGING.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String)}.
+ */
+ public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
+
+ /**
+ * Broadcast intent action indicating that a precise call state
+ * (cellular) on the device has changed.
+ *
+ * <p>
+ * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
+ * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
+ * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
+ * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+ * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @see #EXTRA_RINGING_CALL_STATE
+ * @see #EXTRA_FOREGROUND_CALL_STATE
+ * @see #EXTRA_BACKGROUND_CALL_STATE
+ * @see #EXTRA_DISCONNECT_CAUSE
+ * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PRECISE_CALL_STATE_CHANGED =
+ "android.intent.action.PRECISE_CALL_STATE";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current ringing call.
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current foreground call.
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current background call.
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the disconnect cause.
+ *
+ * @see DisconnectCause
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the disconnect cause provided by the RIL.
+ *
+ * @see PreciseDisconnectCause
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
+
+ /**
+ * Broadcast intent action indicating a data connection has changed,
+ * providing precise information about the connection.
+ *
+ * <p>
+ * The {@link #EXTRA_DATA_STATE} extra indicates the connection state.
+ * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
+ * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
+ * The {@link #EXTRA_DATA_APN} extra indicates the APN.
+ * The {@link #EXTRA_DATA_CHANGE_REASON} extra indicates the connection change reason.
+ * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
+ * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @see #EXTRA_DATA_STATE
+ * @see #EXTRA_DATA_NETWORK_TYPE
+ * @see #EXTRA_DATA_APN_TYPE
+ * @see #EXTRA_DATA_APN
+ * @see #EXTRA_DATA_CHANGE_REASON
+ * @see #EXTRA_DATA_IFACE
+ * @see #EXTRA_DATA_FAILURE_CAUSE
+ * @hide
+ *
+ * @deprecated If the app is running in the background, it won't be able to receive this
+ * broadcast. Apps should use ConnectivityManager {@link #registerNetworkCallback(
+ * android.net.NetworkRequest, ConnectivityManager.NetworkCallback)} to listen for network
+ * changes.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
+ public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
+ "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current data connection.
+ *
+ * @see TelephonyManager#DATA_UNKNOWN
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_STATE = PhoneConstants.STATE_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an integer containing the network type.
+ *
+ * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+ * @see TelephonyManager#NETWORK_TYPE_GPRS
+ * @see TelephonyManager#NETWORK_TYPE_EDGE
+ * @see TelephonyManager#NETWORK_TYPE_UMTS
+ * @see TelephonyManager#NETWORK_TYPE_CDMA
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+ * @see TelephonyManager#NETWORK_TYPE_1xRTT
+ * @see TelephonyManager#NETWORK_TYPE_HSDPA
+ * @see TelephonyManager#NETWORK_TYPE_HSUPA
+ * @see TelephonyManager#NETWORK_TYPE_HSPA
+ * @see TelephonyManager#NETWORK_TYPE_IDEN
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+ * @see TelephonyManager#NETWORK_TYPE_LTE
+ * @see TelephonyManager#NETWORK_TYPE_EHRPD
+ * @see TelephonyManager#NETWORK_TYPE_HSPAP
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_NETWORK_TYPE = PhoneConstants.DATA_NETWORK_TYPE_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String containing the data APN type.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_APN_TYPE = PhoneConstants.DATA_APN_TYPE_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String containing the data APN.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String representation of the change reason.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_CHANGE_REASON = PhoneConstants.STATE_CHANGE_REASON_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String representation of the data interface.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_LINK_PROPERTIES_KEY = PhoneConstants.DATA_LINK_PROPERTIES_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for the data connection fail cause.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
+
+ /**
+ * Broadcast intent action for letting the default dialer to know to show voicemail
+ * notification.
+ *
+ * <p>
+ * The {@link #EXTRA_PHONE_ACCOUNT_HANDLE} extra indicates which {@link PhoneAccountHandle} the
+ * voicemail is received on.
+ * The {@link #EXTRA_NOTIFICATION_COUNT} extra indicates the total numbers of unheard
+ * voicemails.
+ * The {@link #EXTRA_VOICEMAIL_NUMBER} extra indicates the voicemail number if available.
+ * The {@link #EXTRA_CALL_VOICEMAIL_INTENT} extra is a {@link android.app.PendingIntent} that
+ * will call the voicemail number when sent. This extra will be empty if the voicemail number
+ * is not set, and {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} will be set instead.
+ * The {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} extra is a
+ * {@link android.app.PendingIntent} that will launch the voicemail settings. This extra is only
+ * available when the voicemail number is not set.
+ * The {@link #EXTRA_IS_REFRESH} extra indicates whether the notification is a refresh or a new
+ * notification.
+ *
+ * @see #EXTRA_PHONE_ACCOUNT_HANDLE
+ * @see #EXTRA_NOTIFICATION_COUNT
+ * @see #EXTRA_VOICEMAIL_NUMBER
+ * @see #EXTRA_CALL_VOICEMAIL_INTENT
+ * @see #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
+ * @see #EXTRA_IS_REFRESH
+ */
+ public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION =
+ "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+
+ /**
+ * The extra used with an {@link #ACTION_SHOW_VOICEMAIL_NOTIFICATION} {@code Intent} to specify
+ * the {@link PhoneAccountHandle} the notification is for.
+ * <p class="note">
+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
+ "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
+
+ /**
+ * The number of voice messages associated with the notification.
+ */
+ public static final String EXTRA_NOTIFICATION_COUNT =
+ "android.telephony.extra.NOTIFICATION_COUNT";
+
+ /**
+ * The voicemail number.
+ */
+ public static final String EXTRA_VOICEMAIL_NUMBER =
+ "android.telephony.extra.VOICEMAIL_NUMBER";
+
+ /**
+ * The intent to call voicemail.
+ */
+ public static final String EXTRA_CALL_VOICEMAIL_INTENT =
+ "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+
+ /**
+ * The intent to launch voicemail settings.
+ */
+ public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT =
+ "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+
+ /**
+ * Boolean value representing whether the {@link
+ * TelephonyManager#ACTION_SHOW_VOICEMAIL_NOTIFICATION} is new or a refresh of an existing
+ * notification. Notification refresh happens after reboot or connectivity changes. The user has
+ * already been notified for the voicemail so it should not alert the user, and should not be
+ * shown again if the user has dismissed it.
+ */
+ public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that an IMS call has be
+ * successfully handed over from WIFI to LTE.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE =
+ "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that an IMS call failed to be
+ * handed over from LTE to WIFI.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_HANDOVER_TO_WIFI_FAILED =
+ "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to
+ * audio because the data limit was reached.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_DOWNGRADE_DATA_LIMIT_REACHED =
+ "android.telephony.event.EVENT_DOWNGRADE_DATA_LIMIT_REACHED";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to
+ * audio because the data was disabled.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_DOWNGRADE_DATA_DISABLED =
+ "android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that the InCall UI should notify
+ * the user when an international call is placed while on WFC only.
+ * <p>
+ * Used when the carrier config value
+ * {@link CarrierConfigManager#KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL} is true, the device
+ * is on WFC (VoLTE not available) and an international number is dialed.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC =
+ "android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC";
+
+ /**
+ * {@link android.telecom.Connection} event used to indicate that an outgoing call has been
+ * forwarded to another number.
+ * <p>
+ * Sent in response to an IMS supplementary service notification indicating the call has been
+ * forwarded.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_CALL_FORWARDED =
+ "android.telephony.event.EVENT_CALL_FORWARDED";
+
+ /* Visual voicemail protocols */
+
+ /**
+ * The OMTP protocol.
+ */
+ public static final String VVM_TYPE_OMTP = "vvm_type_omtp";
+
+ /**
+ * A flavor of OMTP protocol with a different mobile originated (MO) format
+ */
+ public static final String VVM_TYPE_CVVM = "vvm_type_cvvm";
+
+ /**
+ * Key in bundle returned by {@link #getVisualVoicemailPackageName()}, indicating whether visual
+ * voicemail was enabled or disabled by the user. If the user never explicitly changed this
+ * setting, this key will not exist.
+ *
+ * @see #getVisualVoicemailSettings()
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL =
+ "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
+
+ /**
+ * Key in bundle returned by {@link #getVisualVoicemailPackageName()}, indicating the voicemail
+ * access PIN scrambled during the auto provisioning process. The user is expected to reset
+ * their PIN if this value is not {@code null}.
+ *
+ * @see #getVisualVoicemailSettings()
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING =
+ "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+
+ /**
+ * @hide
+ */
+ public static final String USSD_RESPONSE = "USSD_RESPONSE";
+
+ /**
+ * USSD return code success.
+ * @hide
+ */
+ public static final int USSD_RETURN_SUCCESS = 100;
+
+ /**
+ * Failed code returned when the mobile network has failed to complete a USSD request.
+ * <p>
+ * Returned via {@link TelephonyManager.UssdResponseCallback#onReceiveUssdResponseFailed(
+ * TelephonyManager, String, int)}.
+ */
+ public static final int USSD_RETURN_FAILURE = -1;
+
+ /**
+ * Failure code returned when a USSD request has failed to execute because the Telephony
+ * service is unavailable.
+ * <p>
+ * Returned via {@link TelephonyManager.UssdResponseCallback#onReceiveUssdResponseFailed(
+ * TelephonyManager, String, int)}.
+ */
+ public static final int USSD_ERROR_SERVICE_UNAVAIL = -2;
+
+ //
+ //
+ // Device Info
+ //
+ //
+
+ /**
+ * Returns the software version number for the device, for example,
+ * the IMEI/SV for GSM phones. Return null if the software version is
+ * not available.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getDeviceSoftwareVersion() {
+ return getDeviceSoftwareVersion(getSlotIndex());
+ }
+
+ /**
+ * Returns the software version number for the device, for example,
+ * the IMEI/SV for GSM phones. Return null if the software version is
+ * not available.
+ *
+ * @param slotIndex of which deviceID is returned
+ */
+ /** {@hide} */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getDeviceSoftwareVersion(int slotIndex) {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
+ try {
+ return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the unique device ID, for example, the IMEI for GSM and the MEID
+ * or ESN for CDMA phones. Return null if device ID is not available.
+ *
+ * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
+ * MEID for CDMA.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getDeviceId() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getDeviceId(mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the unique device ID of a subscription, for example, the IMEI for
+ * GSM and the MEID for CDMA phones. Return null if device ID is not available.
+ *
+ * @param slotIndex of which deviceID is returned
+ *
+ * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
+ * MEID for CDMA.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getDeviceId(int slotIndex) {
+ // FIXME this assumes phoneId == slotIndex
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
+ * available.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getImei() {
+ return getImei(getSlotIndex());
+ }
+
+ /**
+ * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
+ * available.
+ *
+ * @param slotIndex of which IMEI is returned
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getImei(int slotIndex) {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
+ try {
+ return telephony.getImeiForSlot(slotIndex, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getMeid() {
+ return getMeid(getSlotIndex());
+ }
+
+ /**
+ * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+ *
+ * @param slotIndex of which MEID is returned
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getMeid(int slotIndex) {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
+ try {
+ return telephony.getMeidForSlot(slotIndex, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the NAI. Return null if NAI is not available.
+ *
+ */
+ /** {@hide}*/
+ public String getNai() {
+ return getNai(getSlotIndex());
+ }
+
+ /**
+ * Returns the NAI. Return null if NAI is not available.
+ *
+ * @param slotIndex of which Nai is returned
+ */
+ /** {@hide}*/
+ public String getNai(int slotIndex) {
+ int[] subId = SubscriptionManager.getSubId(slotIndex);
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ String nai = info.getNaiForSubscriber(subId[0], mContext.getOpPackageName());
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Rlog.v(TAG, "Nai = " + nai);
+ }
+ return nai;
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the current location of the device.
+ *<p>
+ * If there is only one radio in the device and that radio has an LTE connection,
+ * this method will return null. The implementation must not to try add LTE
+ * identifiers into the existing cdma/gsm classes.
+ *<p>
+ * @return Current location of the device or null if not available.
+ *
+ * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
+ */
+ @Deprecated
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ android.Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public CellLocation getCellLocation() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ Rlog.d(TAG, "getCellLocation returning null because telephony is null");
+ return null;
+ }
+ Bundle bundle = telephony.getCellLocation(mContext.getOpPackageName());
+ if (bundle.isEmpty()) {
+ Rlog.d(TAG, "getCellLocation returning null because bundle is empty");
+ return null;
+ }
+ CellLocation cl = CellLocation.newFromBundle(bundle);
+ if (cl.isEmpty()) {
+ Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty");
+ return null;
+ }
+ return cl;
+ } catch (RemoteException ex) {
+ Rlog.d(TAG, "getCellLocation returning null due to RemoteException " + ex);
+ return null;
+ } catch (NullPointerException ex) {
+ Rlog.d(TAG, "getCellLocation returning null due to NullPointerException " + ex);
+ return null;
+ }
+ }
+
+ /**
+ * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged
+ * PhoneStateListener.onCellLocationChanged} will be called on location updates.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
+ public void enableLocationUpdates() {
+ enableLocationUpdates(getSubId());
+ }
+
+ /**
+ * Enables location update notifications for a subscription.
+ * {@link PhoneStateListener#onCellLocationChanged
+ * PhoneStateListener.onCellLocationChanged} will be called on location updates.
+ *
+ * @param subId for which the location updates are enabled
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
+ public void enableLocationUpdates(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.enableLocationUpdatesForSubscriber(subId);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged
+ * PhoneStateListener.onCellLocationChanged} will be called on location updates.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
+ public void disableLocationUpdates() {
+ disableLocationUpdates(getSubId());
+ }
+
+ /** @hide */
+ public void disableLocationUpdates(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.disableLocationUpdatesForSubscriber(subId);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Returns the neighboring cell information of the device.
+ *
+ * @return List of NeighboringCellInfo or null if info unavailable.
+ *
+ * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
+ * from NeighboringCellInfo.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ public List<NeighboringCellInfo> getNeighboringCellInfo() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getNeighboringCellInfo(mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /** No phone radio. */
+ public static final int PHONE_TYPE_NONE = PhoneConstants.PHONE_TYPE_NONE;
+ /** Phone radio is GSM. */
+ public static final int PHONE_TYPE_GSM = PhoneConstants.PHONE_TYPE_GSM;
+ /** Phone radio is CDMA. */
+ public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
+ /** Phone is via SIP. */
+ public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
+
+ /**
+ * Returns the current phone type.
+ * TODO: This is a last minute change and hence hidden.
+ *
+ * @see #PHONE_TYPE_NONE
+ * @see #PHONE_TYPE_GSM
+ * @see #PHONE_TYPE_CDMA
+ * @see #PHONE_TYPE_SIP
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public int getCurrentPhoneType() {
+ return getCurrentPhoneType(getSubId());
+ }
+
+ /**
+ * Returns a constant indicating the device phone type for a subscription.
+ *
+ * @see #PHONE_TYPE_NONE
+ * @see #PHONE_TYPE_GSM
+ * @see #PHONE_TYPE_CDMA
+ *
+ * @param subId for which phone type is returned
+ * @hide
+ */
+ @SystemApi
+ public int getCurrentPhoneType(int subId) {
+ int phoneId;
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ // if we don't have any sims, we don't have subscriptions, but we
+ // still may want to know what type of phone we've got.
+ phoneId = 0;
+ } else {
+ phoneId = SubscriptionManager.getPhoneId(subId);
+ }
+
+ return getCurrentPhoneTypeForSlot(phoneId);
+ }
+
+ /**
+ * See getCurrentPhoneType.
+ *
+ * @hide
+ */
+ public int getCurrentPhoneTypeForSlot(int slotIndex) {
+ try{
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getActivePhoneTypeForSlot(slotIndex);
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return getPhoneTypeFromProperty(slotIndex);
+ }
+ } catch (RemoteException ex) {
+ // This shouldn't happen in the normal case, as a backup we
+ // read from the system property.
+ return getPhoneTypeFromProperty(slotIndex);
+ } catch (NullPointerException ex) {
+ // This shouldn't happen in the normal case, as a backup we
+ // read from the system property.
+ return getPhoneTypeFromProperty(slotIndex);
+ }
+ }
+
+ /**
+ * Returns a constant indicating the device phone type. This
+ * indicates the type of radio used to transmit voice calls.
+ *
+ * @see #PHONE_TYPE_NONE
+ * @see #PHONE_TYPE_GSM
+ * @see #PHONE_TYPE_CDMA
+ * @see #PHONE_TYPE_SIP
+ */
+ public int getPhoneType() {
+ if (!isVoiceCapable()) {
+ return PHONE_TYPE_NONE;
+ }
+ return getCurrentPhoneType();
+ }
+
+ private int getPhoneTypeFromProperty() {
+ return getPhoneTypeFromProperty(getPhoneId());
+ }
+
+ /** {@hide} */
+ private int getPhoneTypeFromProperty(int phoneId) {
+ String type = getTelephonyProperty(phoneId,
+ TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
+ if (type == null || type.isEmpty()) {
+ return getPhoneTypeFromNetworkType(phoneId);
+ }
+ return Integer.parseInt(type);
+ }
+
+ private int getPhoneTypeFromNetworkType() {
+ return getPhoneTypeFromNetworkType(getPhoneId());
+ }
+
+ /** {@hide} */
+ private int getPhoneTypeFromNetworkType(int phoneId) {
+ // When the system property CURRENT_ACTIVE_PHONE, has not been set,
+ // use the system property for default network type.
+ // This is a fail safe, and can only happen at first boot.
+ String mode = getTelephonyProperty(phoneId, "ro.telephony.default_network", null);
+ if (mode != null && !mode.isEmpty()) {
+ return TelephonyManager.getPhoneType(Integer.parseInt(mode));
+ }
+ return TelephonyManager.PHONE_TYPE_NONE;
+ }
+
+ /**
+ * This function returns the type of the phone, depending
+ * on the network mode.
+ *
+ * @param networkMode
+ * @return Phone Type
+ *
+ * @hide
+ */
+ public static int getPhoneType(int networkMode) {
+ switch(networkMode) {
+ case RILConstants.NETWORK_MODE_CDMA:
+ case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+ case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+ return PhoneConstants.PHONE_TYPE_CDMA;
+
+ case RILConstants.NETWORK_MODE_WCDMA_PREF:
+ case RILConstants.NETWORK_MODE_GSM_ONLY:
+ case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+ case RILConstants.NETWORK_MODE_GSM_UMTS:
+ case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+ case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ return PhoneConstants.PHONE_TYPE_GSM;
+
+ // Use CDMA Phone for the global mode including CDMA
+ case RILConstants.NETWORK_MODE_GLOBAL:
+ case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+ case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ return PhoneConstants.PHONE_TYPE_CDMA;
+
+ case RILConstants.NETWORK_MODE_LTE_ONLY:
+ if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ return PhoneConstants.PHONE_TYPE_CDMA;
+ } else {
+ return PhoneConstants.PHONE_TYPE_GSM;
+ }
+ default:
+ return PhoneConstants.PHONE_TYPE_GSM;
+ }
+ }
+
+ /**
+ * The contents of the /proc/cmdline file
+ */
+ private static String getProcCmdLine()
+ {
+ String cmdline = "";
+ FileInputStream is = null;
+ try {
+ is = new FileInputStream("/proc/cmdline");
+ byte [] buffer = new byte[2048];
+ int count = is.read(buffer);
+ if (count > 0) {
+ cmdline = new String(buffer, 0, count);
+ }
+ } catch (IOException e) {
+ Rlog.d(TAG, "No /proc/cmdline exception=" + e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ Rlog.d(TAG, "/proc/cmdline=" + cmdline);
+ return cmdline;
+ }
+
+ /** Kernel command line */
+ private static final String sKernelCmdLine = getProcCmdLine();
+
+ /** Pattern for selecting the product type from the kernel command line */
+ private static final Pattern sProductTypePattern =
+ Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
+
+ /** The ProductType used for LTE on CDMA devices */
+ private static final String sLteOnCdmaProductType =
+ SystemProperties.get(TelephonyProperties.PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE, "");
+
+ /**
+ * Return if the current radio is LTE on CDMA. This
+ * is a tri-state return value as for a period of time
+ * the mode may be unknown.
+ *
+ * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
+ * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
+ *
+ * @hide
+ */
+ public static int getLteOnCdmaModeStatic() {
+ int retVal;
+ int curVal;
+ String productType = "";
+
+ curVal = SystemProperties.getInt(TelephonyProperties.PROPERTY_LTE_ON_CDMA_DEVICE,
+ PhoneConstants.LTE_ON_CDMA_UNKNOWN);
+ retVal = curVal;
+ if (retVal == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
+ Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
+ if (matcher.find()) {
+ productType = matcher.group(1);
+ if (sLteOnCdmaProductType.equals(productType)) {
+ retVal = PhoneConstants.LTE_ON_CDMA_TRUE;
+ } else {
+ retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
+ }
+ } else {
+ retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
+ }
+ }
+
+ Rlog.d(TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal +
+ " product_type='" + productType +
+ "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
+ return retVal;
+ }
+
+ //
+ //
+ // Current Network
+ //
+ //
+
+ /**
+ * Returns the alphabetic name of current registered operator.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ */
+ public String getNetworkOperatorName() {
+ return getNetworkOperatorName(getSubId());
+ }
+
+ /**
+ * Returns the alphabetic name of current registered operator
+ * for a particular subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ * @param subId
+ * @hide
+ */
+ public String getNetworkOperatorName(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, "");
+ }
+
+ /**
+ * Returns the numeric name (MCC+MNC) of current registered operator.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ */
+ public String getNetworkOperator() {
+ return getNetworkOperatorForPhone(getPhoneId());
+ }
+
+ /**
+ * Returns the numeric name (MCC+MNC) of current registered operator
+ * for a particular subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param subId
+ * @hide
+ */
+ public String getNetworkOperator(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getNetworkOperatorForPhone(phoneId);
+ }
+
+ /**
+ * Returns the numeric name (MCC+MNC) of current registered operator
+ * for a particular subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param phoneId
+ * @hide
+ **/
+ public String getNetworkOperatorForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
+ }
+
+
+ /**
+ * Returns the network specifier of the subscription ID pinned to the TelephonyManager. The
+ * network specifier is used by {@link
+ * android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to create a {@link
+ * android.net.NetworkRequest} that connects through the subscription.
+ *
+ * @see android.net.NetworkRequest.Builder#setNetworkSpecifier(String)
+ * @see #createForSubscriptionId(int)
+ * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+ */
+ public String getNetworkSpecifier() {
+ return String.valueOf(getSubId());
+ }
+
+ /**
+ * Returns the carrier config of the subscription ID pinned to the TelephonyManager. If an
+ * invalid subscription ID is pinned to the TelephonyManager, the returned config will contain
+ * default values.
+ *
+ * @see CarrierConfigManager#getConfigForSubId(int)
+ * @see #createForSubscriptionId(int)
+ * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+ */
+ @WorkerThread
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public PersistableBundle getCarrierConfig() {
+ CarrierConfigManager carrierConfigManager = mContext
+ .getSystemService(CarrierConfigManager.class);
+ return carrierConfigManager.getConfigForSubId(getSubId());
+ }
+
+ /**
+ * Returns true if the device is considered roaming on the current
+ * network, for GSM purposes.
+ * <p>
+ * Availability: Only when user registered to a network.
+ */
+ public boolean isNetworkRoaming() {
+ return isNetworkRoaming(getSubId());
+ }
+
+ /**
+ * Returns true if the device is considered roaming on the current
+ * network for a subscription.
+ * <p>
+ * Availability: Only when user registered to a network.
+ *
+ * @param subId
+ * @hide
+ */
+ public boolean isNetworkRoaming(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return Boolean.parseBoolean(getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, null));
+ }
+
+ /**
+ * Returns the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ */
+ public String getNetworkCountryIso() {
+ return getNetworkCountryIsoForPhone(getPhoneId());
+ }
+
+ /**
+ * Returns the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code) of a subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param subId for which Network CountryIso is returned
+ * @hide
+ */
+ public String getNetworkCountryIso(int subId) {
+ return getNetworkCountryIsoForPhone(getPhoneId(subId));
+ }
+
+ /**
+ * Returns the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code) of a subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param phoneId for which Network CountryIso is returned
+ */
+ /** {@hide} */
+ public String getNetworkCountryIsoForPhone(int phoneId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return "";
+ return telephony.getNetworkCountryIsoForPhone(phoneId);
+ } catch (RemoteException ex) {
+ return "";
+ }
+ }
+
+ /*
+ * When adding a network type to the list below, make sure to add the correct icon to
+ * MobileSignalController.mapIconSets().
+ */
+ /** Network type is unknown */
+ public static final int NETWORK_TYPE_UNKNOWN = 0;
+ /** Current network is GPRS */
+ public static final int NETWORK_TYPE_GPRS = 1;
+ /** Current network is EDGE */
+ public static final int NETWORK_TYPE_EDGE = 2;
+ /** Current network is UMTS */
+ public static final int NETWORK_TYPE_UMTS = 3;
+ /** Current network is CDMA: Either IS95A or IS95B*/
+ public static final int NETWORK_TYPE_CDMA = 4;
+ /** Current network is EVDO revision 0*/
+ public static final int NETWORK_TYPE_EVDO_0 = 5;
+ /** Current network is EVDO revision A*/
+ public static final int NETWORK_TYPE_EVDO_A = 6;
+ /** Current network is 1xRTT*/
+ public static final int NETWORK_TYPE_1xRTT = 7;
+ /** Current network is HSDPA */
+ public static final int NETWORK_TYPE_HSDPA = 8;
+ /** Current network is HSUPA */
+ public static final int NETWORK_TYPE_HSUPA = 9;
+ /** Current network is HSPA */
+ public static final int NETWORK_TYPE_HSPA = 10;
+ /** Current network is iDen */
+ public static final int NETWORK_TYPE_IDEN = 11;
+ /** Current network is EVDO revision B*/
+ public static final int NETWORK_TYPE_EVDO_B = 12;
+ /** Current network is LTE */
+ public static final int NETWORK_TYPE_LTE = 13;
+ /** Current network is eHRPD */
+ public static final int NETWORK_TYPE_EHRPD = 14;
+ /** Current network is HSPA+ */
+ public static final int NETWORK_TYPE_HSPAP = 15;
+ /** Current network is GSM */
+ public static final int NETWORK_TYPE_GSM = 16;
+ /** Current network is TD_SCDMA */
+ public static final int NETWORK_TYPE_TD_SCDMA = 17;
+ /** Current network is IWLAN */
+ public static final int NETWORK_TYPE_IWLAN = 18;
+ /** Current network is LTE_CA {@hide} */
+ public static final int NETWORK_TYPE_LTE_CA = 19;
+ /**
+ * @return the NETWORK_TYPE_xxxx for current data connection.
+ */
+ public int getNetworkType() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getNetworkType();
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ } catch(RemoteException ex) {
+ // This shouldn't happen in the normal case
+ return NETWORK_TYPE_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Returns a constant indicating the radio technology (network type)
+ * currently in use on the device for a subscription.
+ * @return the network type
+ *
+ * @param subId for which network type is returned
+ *
+ * @see #NETWORK_TYPE_UNKNOWN
+ * @see #NETWORK_TYPE_GPRS
+ * @see #NETWORK_TYPE_EDGE
+ * @see #NETWORK_TYPE_UMTS
+ * @see #NETWORK_TYPE_HSDPA
+ * @see #NETWORK_TYPE_HSUPA
+ * @see #NETWORK_TYPE_HSPA
+ * @see #NETWORK_TYPE_CDMA
+ * @see #NETWORK_TYPE_EVDO_0
+ * @see #NETWORK_TYPE_EVDO_A
+ * @see #NETWORK_TYPE_EVDO_B
+ * @see #NETWORK_TYPE_1xRTT
+ * @see #NETWORK_TYPE_IDEN
+ * @see #NETWORK_TYPE_LTE
+ * @see #NETWORK_TYPE_EHRPD
+ * @see #NETWORK_TYPE_HSPAP
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getNetworkType(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ } catch(RemoteException ex) {
+ // This shouldn't happen in the normal case
+ return NETWORK_TYPE_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Returns a constant indicating the radio technology (network type)
+ * currently in use on the device for data transmission.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * @return the network type
+ *
+ * @see #NETWORK_TYPE_UNKNOWN
+ * @see #NETWORK_TYPE_GPRS
+ * @see #NETWORK_TYPE_EDGE
+ * @see #NETWORK_TYPE_UMTS
+ * @see #NETWORK_TYPE_HSDPA
+ * @see #NETWORK_TYPE_HSUPA
+ * @see #NETWORK_TYPE_HSPA
+ * @see #NETWORK_TYPE_CDMA
+ * @see #NETWORK_TYPE_EVDO_0
+ * @see #NETWORK_TYPE_EVDO_A
+ * @see #NETWORK_TYPE_EVDO_B
+ * @see #NETWORK_TYPE_1xRTT
+ * @see #NETWORK_TYPE_IDEN
+ * @see #NETWORK_TYPE_LTE
+ * @see #NETWORK_TYPE_EHRPD
+ * @see #NETWORK_TYPE_HSPAP
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getDataNetworkType() {
+ return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ }
+
+ /**
+ * Returns a constant indicating the radio technology (network type)
+ * currently in use on the device for data transmission for a subscription
+ * @return the network type
+ *
+ * @param subId for which network type is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getDataNetworkType(int subId) {
+ try{
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName());
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ } catch(RemoteException ex) {
+ // This shouldn't happen in the normal case
+ return NETWORK_TYPE_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Returns the NETWORK_TYPE_xxxx for voice
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getVoiceNetworkType() {
+ return getVoiceNetworkType(getSubId());
+ }
+
+ /**
+ * Returns the NETWORK_TYPE_xxxx for voice for a subId
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getVoiceNetworkType(int subId) {
+ try{
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName());
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ } catch(RemoteException ex) {
+ // This shouldn't happen in the normal case
+ return NETWORK_TYPE_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Network Class Definitions.
+ * Do not change this order, it is used for sorting during emergency calling in
+ * {@link TelephonyConnectionService#getFirstPhoneForEmergencyCall()}. Any newer technologies
+ * should be added after the current definitions.
+ */
+ /** Unknown network class. {@hide} */
+ public static final int NETWORK_CLASS_UNKNOWN = 0;
+ /** Class of broadly defined "2G" networks. {@hide} */
+ public static final int NETWORK_CLASS_2_G = 1;
+ /** Class of broadly defined "3G" networks. {@hide} */
+ public static final int NETWORK_CLASS_3_G = 2;
+ /** Class of broadly defined "4G" networks. {@hide} */
+ public static final int NETWORK_CLASS_4_G = 3;
+
+ /**
+ * Return general class of network type, such as "3G" or "4G". In cases
+ * where classification is contentious, this method is conservative.
+ *
+ * @hide
+ */
+ public static int getNetworkClass(int networkType) {
+ switch (networkType) {
+ case NETWORK_TYPE_GPRS:
+ case NETWORK_TYPE_GSM:
+ case NETWORK_TYPE_EDGE:
+ case NETWORK_TYPE_CDMA:
+ case NETWORK_TYPE_1xRTT:
+ case NETWORK_TYPE_IDEN:
+ return NETWORK_CLASS_2_G;
+ case NETWORK_TYPE_UMTS:
+ case NETWORK_TYPE_EVDO_0:
+ case NETWORK_TYPE_EVDO_A:
+ case NETWORK_TYPE_HSDPA:
+ case NETWORK_TYPE_HSUPA:
+ case NETWORK_TYPE_HSPA:
+ case NETWORK_TYPE_EVDO_B:
+ case NETWORK_TYPE_EHRPD:
+ case NETWORK_TYPE_HSPAP:
+ case NETWORK_TYPE_TD_SCDMA:
+ return NETWORK_CLASS_3_G;
+ case NETWORK_TYPE_LTE:
+ case NETWORK_TYPE_IWLAN:
+ case NETWORK_TYPE_LTE_CA:
+ return NETWORK_CLASS_4_G;
+ default:
+ return NETWORK_CLASS_UNKNOWN;
+ }
+ }
+
+ /**
+ * Returns a string representation of the radio technology (network type)
+ * currently in use on the device.
+ * @return the name of the radio technology
+ *
+ * @hide pending API council review
+ */
+ public String getNetworkTypeName() {
+ return getNetworkTypeName(getNetworkType());
+ }
+
+ /**
+ * Returns a string representation of the radio technology (network type)
+ * currently in use on the device.
+ * @param subId for which network type is returned
+ * @return the name of the radio technology
+ *
+ */
+ /** {@hide} */
+ public static String getNetworkTypeName(int type) {
+ switch (type) {
+ case NETWORK_TYPE_GPRS:
+ return "GPRS";
+ case NETWORK_TYPE_EDGE:
+ return "EDGE";
+ case NETWORK_TYPE_UMTS:
+ return "UMTS";
+ case NETWORK_TYPE_HSDPA:
+ return "HSDPA";
+ case NETWORK_TYPE_HSUPA:
+ return "HSUPA";
+ case NETWORK_TYPE_HSPA:
+ return "HSPA";
+ case NETWORK_TYPE_CDMA:
+ return "CDMA";
+ case NETWORK_TYPE_EVDO_0:
+ return "CDMA - EvDo rev. 0";
+ case NETWORK_TYPE_EVDO_A:
+ return "CDMA - EvDo rev. A";
+ case NETWORK_TYPE_EVDO_B:
+ return "CDMA - EvDo rev. B";
+ case NETWORK_TYPE_1xRTT:
+ return "CDMA - 1xRTT";
+ case NETWORK_TYPE_LTE:
+ return "LTE";
+ case NETWORK_TYPE_EHRPD:
+ return "CDMA - eHRPD";
+ case NETWORK_TYPE_IDEN:
+ return "iDEN";
+ case NETWORK_TYPE_HSPAP:
+ return "HSPA+";
+ case NETWORK_TYPE_GSM:
+ return "GSM";
+ case NETWORK_TYPE_TD_SCDMA:
+ return "TD_SCDMA";
+ case NETWORK_TYPE_IWLAN:
+ return "IWLAN";
+ case NETWORK_TYPE_LTE_CA:
+ return "LTE_CA";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ //
+ //
+ // SIM Card
+ //
+ //
+
+ /**
+ * SIM card state: Unknown. Signifies that the SIM is in transition
+ * between states. For example, when the user inputs the SIM pin
+ * under PIN_REQUIRED state, a query for sim status returns
+ * this state before turning to SIM_STATE_READY.
+ *
+ * These are the ordinal value of IccCardConstants.State.
+ */
+ public static final int SIM_STATE_UNKNOWN = 0;
+ /** SIM card state: no SIM card is available in the device */
+ public static final int SIM_STATE_ABSENT = 1;
+ /** SIM card state: Locked: requires the user's SIM PIN to unlock */
+ public static final int SIM_STATE_PIN_REQUIRED = 2;
+ /** SIM card state: Locked: requires the user's SIM PUK to unlock */
+ public static final int SIM_STATE_PUK_REQUIRED = 3;
+ /** SIM card state: Locked: requires a network PIN to unlock */
+ public static final int SIM_STATE_NETWORK_LOCKED = 4;
+ /** SIM card state: Ready */
+ public static final int SIM_STATE_READY = 5;
+ /** SIM card state: SIM Card is NOT READY */
+ public static final int SIM_STATE_NOT_READY = 6;
+ /** SIM card state: SIM Card Error, permanently disabled */
+ public static final int SIM_STATE_PERM_DISABLED = 7;
+ /** SIM card state: SIM Card Error, present but faulty */
+ public static final int SIM_STATE_CARD_IO_ERROR = 8;
+ /** SIM card state: SIM Card restricted, present but not usable due to
+ * carrier restrictions.
+ */
+ public static final int SIM_STATE_CARD_RESTRICTED = 9;
+
+ /**
+ * @return true if a ICC card is present
+ */
+ public boolean hasIccCard() {
+ return hasIccCard(getSlotIndex());
+ }
+
+ /**
+ * @return true if a ICC card is present for a subscription
+ *
+ * @param slotIndex for which icc card presence is checked
+ */
+ /** {@hide} */
+ // FIXME Input argument slotIndex should be of type int
+ public boolean hasIccCard(int slotIndex) {
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return false;
+ return telephony.hasIccCardUsingSlotIndex(slotIndex);
+ } catch (RemoteException ex) {
+ // Assume no ICC card if remote exception which shouldn't happen
+ return false;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return false;
+ }
+ }
+
+ /**
+ * Returns a constant indicating the state of the default SIM card.
+ *
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_ABSENT
+ * @see #SIM_STATE_PIN_REQUIRED
+ * @see #SIM_STATE_PUK_REQUIRED
+ * @see #SIM_STATE_NETWORK_LOCKED
+ * @see #SIM_STATE_READY
+ * @see #SIM_STATE_NOT_READY
+ * @see #SIM_STATE_PERM_DISABLED
+ * @see #SIM_STATE_CARD_IO_ERROR
+ * @see #SIM_STATE_CARD_RESTRICTED
+ */
+ public int getSimState() {
+ int slotIndex = getSlotIndex();
+ // slotIndex may be invalid due to sim being absent. In that case query all slots to get
+ // sim state
+ if (slotIndex < 0) {
+ // query for all slots and return absent if all sim states are absent, otherwise
+ // return unknown
+ for (int i = 0; i < getPhoneCount(); i++) {
+ int simState = getSimState(i);
+ if (simState != SIM_STATE_ABSENT) {
+ Rlog.d(TAG, "getSimState: default sim:" + slotIndex + ", sim state for " +
+ "slotIndex=" + i + " is " + simState + ", return state as unknown");
+ return SIM_STATE_UNKNOWN;
+ }
+ }
+ Rlog.d(TAG, "getSimState: default sim:" + slotIndex + ", all SIMs absent, return " +
+ "state as absent");
+ return SIM_STATE_ABSENT;
+ }
+ return getSimState(slotIndex);
+ }
+
+ /**
+ * Returns a constant indicating the state of the device SIM card in a slot.
+ *
+ * @param slotIndex
+ *
+ * @see #SIM_STATE_UNKNOWN
+ * @see #SIM_STATE_ABSENT
+ * @see #SIM_STATE_PIN_REQUIRED
+ * @see #SIM_STATE_PUK_REQUIRED
+ * @see #SIM_STATE_NETWORK_LOCKED
+ * @see #SIM_STATE_READY
+ * @see #SIM_STATE_NOT_READY
+ * @see #SIM_STATE_PERM_DISABLED
+ * @see #SIM_STATE_CARD_IO_ERROR
+ * @see #SIM_STATE_CARD_RESTRICTED
+ */
+ public int getSimState(int slotIndex) {
+ int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
+ return simState;
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ */
+ public String getSimOperator() {
+ return getSimOperatorNumeric();
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ *
+ * @param subId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperator(int subId) {
+ return getSimOperatorNumeric(subId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ * @hide
+ */
+ public String getSimOperatorNumeric() {
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultSubscriptionId();
+ }
+ }
+ }
+ return getSimOperatorNumeric(subId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ *
+ * @param subId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperatorNumeric(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getSimOperatorNumericForPhone(phoneId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+ * <p>
+ *
+ * @param phoneId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperatorNumericForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
+ }
+
+ /**
+ * Returns the Service Provider Name (SPN).
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ */
+ public String getSimOperatorName() {
+ return getSimOperatorNameForPhone(getPhoneId());
+ }
+
+ /**
+ * Returns the Service Provider Name (SPN).
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ *
+ * @param subId for which SimOperatorName is returned
+ * @hide
+ */
+ public String getSimOperatorName(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getSimOperatorNameForPhone(phoneId);
+ }
+
+ /**
+ * Returns the Service Provider Name (SPN).
+ *
+ * @hide
+ */
+ public String getSimOperatorNameForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ */
+ public String getSimCountryIso() {
+ return getSimCountryIsoForPhone(getPhoneId());
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ *
+ * @param subId for which SimCountryIso is returned
+ * @hide
+ */
+ public String getSimCountryIso(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getSimCountryIsoForPhone(phoneId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ *
+ * @hide
+ */
+ public String getSimCountryIsoForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ }
+
+ /**
+ * Returns the serial number of the SIM, if applicable. Return null if it is
+ * unavailable.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getSimSerialNumber() {
+ return getSimSerialNumber(getSubId());
+ }
+
+ /**
+ * Returns the serial number for the given subscription, if applicable. Return null if it is
+ * unavailable.
+ * <p>
+ * @param subId for which Sim Serial number is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getSimSerialNumber(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Return if the current radio is LTE on CDMA. This
+ * is a tri-state return value as for a period of time
+ * the mode may be unknown.
+ *
+ * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
+ * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getLteOnCdmaMode() {
+ return getLteOnCdmaMode(getSubId());
+ }
+
+ /**
+ * Return if the current radio is LTE on CDMA for Subscription. This
+ * is a tri-state return value as for a period of time
+ * the mode may be unknown.
+ *
+ * @param subId for which radio is LTE on CDMA is returned
+ * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
+ * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getLteOnCdmaMode(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
+ return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ // Assume no ICC card if remote exception which shouldn't happen
+ return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
+ }
+ }
+
+ //
+ //
+ // Subscriber Info
+ //
+ //
+
+ /**
+ * Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
+ * Return null if it is unavailable.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getSubscriberId() {
+ return getSubscriberId(getSubId());
+ }
+
+ /**
+ * Returns the unique subscriber ID, for example, the IMSI for a GSM phone
+ * for a subscription.
+ * Return null if it is unavailable.
+ *
+ * @param subId whose subscriber id is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getSubscriberId(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+ * This includes the public key and the key identifier. For multi-sim devices, if no subId
+ * has been specified, we will return the value for the dafault data sim.
+ * Return null if it is unavailable.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * @param keyType whether the key is being used for wlan or epdg. Valid key types are
+ * {@link TelephonyManager#KEY_TYPE_EPDG} or
+ * {@link TelephonyManager#KEY_TYPE_WLAN}.
+ * @return ImsiEncryptionInfo Carrier specific information that will be used to encrypt the
+ * IMSI and IMPI. This includes the public key and the key identifier. This information
+ * will be stored in the device keystore. The system will return a null when no key was
+ * found, and the carrier does not require a key. The system will throw the following
+ * exceptions:
+ * 1. IllegalArgumentException when an invalid key is sent.
+ * 2. RuntimeException if the key is required but not found; and also if there was an
+ * internal exception.
+ * @hide
+ */
+ public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null) {
+ throw new RuntimeException("IMSI error: Subscriber Info is null");
+ }
+ int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+ if (keyType != KEY_TYPE_EPDG && keyType != KEY_TYPE_WLAN) {
+ throw new IllegalArgumentException("IMSI error: Invalid key type");
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption(
+ subId, keyType, mContext.getOpPackageName());
+ if (imsiEncryptionInfo == null
+ && isImsiEncryptionRequired(subId, keyType)) {
+ Rlog.e(TAG, "IMSI error: key is required but not found");
+ throw new RuntimeException("IMSI error: key is required but not found");
+ }
+ return imsiEncryptionInfo;
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
+ throw new RuntimeException("IMSI error: Remote Exception");
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
+ throw new RuntimeException("IMSI error: Null Pointer exception");
+ }
+ }
+
+ /**
+ * @param keyAvailability bitmask that defines the availabilty of keys for a type.
+ * @param keyType the key type which is being checked. (WLAN, EPDG)
+ * @return true if the digit at position keyType is 1, else false.
+ * @hide
+ */
+ private static boolean isKeyEnabled(int keyAvailability, int keyType) {
+ int returnValue = (keyAvailability >> (keyType - 1)) & 1;
+ return (returnValue == 1) ? true : false;
+ }
+
+ /**
+ * If Carrier requires Imsi to be encrypted.
+ * @hide
+ */
+ private boolean isImsiEncryptionRequired(int subId, int keyType) {
+ CarrierConfigManager configManager =
+ (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) {
+ return false;
+ }
+ PersistableBundle pb = configManager.getConfigForSubId(subId);
+ if (pb == null) {
+ return false;
+ }
+ int keyAvailability = pb.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
+ return isKeyEnabled(keyAvailability, keyType);
+ }
+
+ /**
+ * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
+ * This includes the public key and the key identifier. This information will be stored in the
+ * device keystore.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * @param imsiEncryptionInfo which includes the Key Type, the Public Key
+ * (java.security.PublicKey) and the Key Identifier.and the Key Identifier.
+ * The keyIdentifier Attribute value pair that helps a server locate
+ * the private key to decrypt the permanent identity. This field is
+ * optional and if it is present then it’s always separated from encrypted
+ * permanent identity with “,”. Key identifier AVP is presented in ASCII string
+ * with “name=value” format.
+ * @hide
+ */
+ public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null) return;
+ info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(),
+ imsiEncryptionInfo);
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return;
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setCarrierInfoForImsiEncryption RemoteException", ex);
+ return;
+ }
+ }
+
+ /**
+ * Returns the Group Identifier Level1 for a GSM phone.
+ * Return null if it is unavailable.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getGroupIdLevel1() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getGroupIdLevel1(mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the Group Identifier Level1 for a GSM phone for a particular subscription.
+ * Return null if it is unavailable.
+ *
+ * @param subId whose subscriber id is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getGroupIdLevel1(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the phone number string for line 1, for example, the MSISDN
+ * for a GSM phone. Return null if it is unavailable.
+ * <p>
+ * The default SMS app can also use this.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_NUMBERS
+ })
+ public String getLine1Number() {
+ return getLine1Number(getSubId());
+ }
+
+ /**
+ * Returns the phone number string for line 1, for example, the MSISDN
+ * for a GSM phone for a particular subscription. Return null if it is unavailable.
+ * <p>
+ * The default SMS app can also use this.
+ *
+ * @param subId whose phone number for line 1 is returned
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_NUMBERS
+ })
+ public String getLine1Number(int subId) {
+ String number = null;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ if (number != null) {
+ return number;
+ }
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Set the line 1 phone number string and its alphatag for the current ICCID
+ * for display purpose only, for example, displayed in Phone Status. It won't
+ * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
+ * value.
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param alphaTag alpha-tagging of the dailing nubmer
+ * @param number The dialing number
+ * @return true if the operation was executed correctly.
+ */
+ public boolean setLine1NumberForDisplay(String alphaTag, String number) {
+ return setLine1NumberForDisplay(getSubId(), alphaTag, number);
+ }
+
+ /**
+ * Set the line 1 phone number string and its alphatag for the current ICCID
+ * for display purpose only, for example, displayed in Phone Status. It won't
+ * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
+ * value.
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param subId the subscriber that the alphatag and dialing number belongs to.
+ * @param alphaTag alpha-tagging of the dailing nubmer
+ * @param number The dialing number
+ * @return true if the operation was executed correctly.
+ * @hide
+ */
+ public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setLine1NumberForDisplayForSubscriber(subId, alphaTag, number);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return false;
+ }
+
+ /**
+ * Returns the alphabetic identifier associated with the line 1 number.
+ * Return null if it is unavailable.
+ * @hide
+ * nobody seems to call this.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getLine1AlphaTag() {
+ return getLine1AlphaTag(getSubId());
+ }
+
+ /**
+ * Returns the alphabetic identifier associated with the line 1 number
+ * for a subscription.
+ * Return null if it is unavailable.
+ * @param subId whose alphabetic identifier associated with line 1 is returned
+ * nobody seems to call this.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getLine1AlphaTag(int subId) {
+ String alphaTag = null;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ alphaTag = telephony.getLine1AlphaTagForDisplay(subId,
+ getOpPackageName());
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ if (alphaTag != null) {
+ return alphaTag;
+ }
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Return the set of subscriber IDs that should be considered as "merged
+ * together" for data usage purposes. This is commonly {@code null} to
+ * indicate no merging is required. Any returned subscribers are sorted in a
+ * deterministic order.
+ *
+ * @hide
+ */
+ public @Nullable String[] getMergedSubscriberIds() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getMergedSubscriberIds(getOpPackageName());
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Returns the MSISDN string.
+ * for a GSM phone. Return null if it is unavailable.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getMsisdn() {
+ return getMsisdn(getSubId());
+ }
+
+ /**
+ * Returns the MSISDN string.
+ * for a GSM phone. Return null if it is unavailable.
+ *
+ * @param subId for which msisdn is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getMsisdn(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getMsisdnForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the voice mail number. Return null if it is unavailable.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getVoiceMailNumber() {
+ return getVoiceMailNumber(getSubId());
+ }
+
+ /**
+ * Returns the voice mail number for a subscription.
+ * Return null if it is unavailable.
+ * @param subId whose voice mail number is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getVoiceMailNumber(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the complete voice mail number. Return null if it is unavailable.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CALL_PRIVILEGED)
+ public String getCompleteVoiceMailNumber() {
+ return getCompleteVoiceMailNumber(getSubId());
+ }
+
+ /**
+ * Returns the complete voice mail number. Return null if it is unavailable.
+ *
+ * @param subId
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CALL_PRIVILEGED)
+ public String getCompleteVoiceMailNumber(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getCompleteVoiceMailNumberForSubscriber(subId);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Sets the voice mail number.
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param alphaTag The alpha tag to display.
+ * @param number The voicemail number.
+ */
+ public boolean setVoiceMailNumber(String alphaTag, String number) {
+ return setVoiceMailNumber(getSubId(), alphaTag, number);
+ }
+
+ /**
+ * Sets the voicemail number for the given subscriber.
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription id.
+ * @param alphaTag The alpha tag to display.
+ * @param number The voicemail number.
+ * @hide
+ */
+ public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setVoiceMailNumber(subId, alphaTag, number);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return false;
+ }
+
+ /**
+ * Enables or disables the visual voicemail client for a phone account.
+ *
+ * <p>Requires that the calling app is the default dialer, or has carrier privileges, or
+ * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @see #hasCarrierPrivileges
+ *
+ * @param phoneAccountHandle the phone account to change the client state
+ * @param enabled the new state of the client
+ * @hide
+ * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should
+ * be implemented instead.
+ */
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
+ }
+
+ /**
+ * Returns whether the visual voicemail client is enabled.
+ *
+ * @param phoneAccountHandle the phone account to check for.
+ * @return {@code true} when the visual voicemail client is enabled for this client
+ * @hide
+ * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should
+ * be implemented instead.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @SuppressLint("Doclava125")
+ public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
+ return false;
+ }
+
+ /**
+ * Returns an opaque bundle of settings formerly used by the visual voicemail client for the
+ * subscription ID pinned to the TelephonyManager, or {@code null} if the subscription ID is
+ * invalid. This method allows the system dialer to migrate settings out of the pre-O visual
+ * voicemail client in telephony.
+ *
+ * <p>Requires the caller to be the system dialer.
+ *
+ * @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL
+ * @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("Doclava125")
+ @Nullable
+ public Bundle getVisualVoicemailSettings(){
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony
+ .getVisualVoicemailSettings(mContext.getOpPackageName(), mSubId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Returns the package responsible of processing visual voicemail for the subscription ID pinned
+ * to the TelephonyManager. Returns {@code null} when there is no package responsible for
+ * processing visual voicemail for the subscription.
+ *
+ * @see #createForSubscriptionId(int)
+ * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+ * @see VisualVoicemailService
+ */
+ @Nullable
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getVisualVoicemailPackageName() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony
+ .getVisualVoicemailPackageName(mContext.getOpPackageName(), getSubId());
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Set the visual voicemail SMS filter settings for the subscription ID pinned
+ * to the TelephonyManager.
+ * When the filter is enabled, {@link
+ * VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be
+ * called when a SMS matching the settings is received. The caller should have
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} and implement a
+ * VisualVoicemailService.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param settings The settings for the filter, or {@code null} to disable the filter.
+ */
+ public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) {
+ if (settings == null) {
+ disableVisualVoicemailSmsFilter(mSubId);
+ } else {
+ enableVisualVoicemailSmsFilter(mSubId, settings);
+ }
+ }
+
+ /**
+ * Send a visual voicemail SMS. The caller must be the current default dialer.
+ * A {@link VisualVoicemailService} uses this method to send a command via SMS to the carrier's
+ * visual voicemail server. Some examples for carriers using the OMTP standard include
+ * activating and deactivating visual voicemail, or requesting the current visual voicemail
+ * provisioning status. See the OMTP Visual Voicemail specification for more information on the
+ * format of these SMS messages.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SEND_SMS SEND_SMS}
+ *
+ * @param number The destination number.
+ * @param port The destination port for data SMS, or 0 for text SMS.
+ * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
+ * @param sentIntent The sent intent passed to the {@link SmsManager}
+ *
+ * @throws SecurityException if the caller is not the current default dialer
+ *
+ * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
+ * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+ */
+ public void sendVisualVoicemailSms(String number, int port, String text,
+ PendingIntent sentIntent) {
+ sendVisualVoicemailSmsForSubscriber(mSubId, number, port, text, sentIntent);
+ }
+
+ /**
+ * Enables the visual voicemail SMS filter for a phone account. When the filter is
+ * enabled, Incoming SMS messages matching the OMTP VVM SMS interface will be redirected to the
+ * visual voicemail client with
+ * {@link android.provider.VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED}.
+ *
+ * <p>This takes effect only when the caller is the default dialer. The enabled status and
+ * settings persist through default dialer changes, but the filter will only honor the setting
+ * set by the current default dialer.
+ *
+ *
+ * @param subId The subscription id of the phone account.
+ * @param settings The settings for the filter.
+ */
+ /** @hide */
+ public void enableVisualVoicemailSmsFilter(int subId,
+ VisualVoicemailSmsFilterSettings settings) {
+ if(settings == null){
+ throw new IllegalArgumentException("Settings cannot be null");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.enableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId,
+ settings);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Disables the visual voicemail SMS filter for a phone account.
+ *
+ * <p>This takes effect only when the caller is the default dialer. The enabled status and
+ * settings persist through default dialer changes, but the filter will only honor the setting
+ * set by the current default dialer.
+ */
+ /** @hide */
+ public void disableVisualVoicemailSmsFilter(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.disableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * @returns the settings of the visual voicemail SMS filter for a phone account, or {@code null}
+ * if the filter is disabled.
+ *
+ * <p>This takes effect only when the caller is the default dialer. The enabled status and
+ * settings persist through default dialer changes, but the filter will only honor the setting
+ * set by the current default dialer.
+ */
+ /** @hide */
+ @Nullable
+ public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony
+ .getVisualVoicemailSmsFilterSettings(mContext.getOpPackageName(), subId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+
+ return null;
+ }
+
+ /**
+ * @returns the settings of the visual voicemail SMS filter for a phone account set by the
+ * current active visual voicemail client, or {@code null} if the filter is disabled.
+ *
+ * <p>Requires the calling app to have READ_PRIVILEGED_PHONE_STATE permission.
+ */
+ /** @hide */
+ @Nullable
+ public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getActiveVisualVoicemailSmsFilterSettings(subId);
+ }
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Send a visual voicemail SMS. The IPC caller must be the current default dialer.
+ *
+ * @param phoneAccountHandle The account to send the SMS with.
+ * @param number The destination number.
+ * @param port The destination port for data SMS, or 0 for text SMS.
+ * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
+ * @param sentIntent The sent intent passed to the {@link SmsManager}
+ *
+ * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
+ * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.SEND_SMS)
+ public void sendVisualVoicemailSmsForSubscriber(int subId, String number, int port,
+ String text, PendingIntent sentIntent) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.sendVisualVoicemailSmsForSubscriber(
+ mContext.getOpPackageName(), subId, number, port, text, sentIntent);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Initial SIM activation state, unknown. Not set by any carrier apps.
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0;
+
+ /**
+ * indicate SIM is under activation procedure now.
+ * intermediate state followed by another state update with activation procedure result:
+ * @see #SIM_ACTIVATION_STATE_ACTIVATED
+ * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see #SIM_ACTIVATION_STATE_RESTRICTED
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1;
+
+ /**
+ * Indicate SIM has been successfully activated with full service
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2;
+
+ /**
+ * Indicate SIM has been deactivated by the carrier so that service is not available
+ * and requires activation service to enable services.
+ * Carrier apps could be signalled to set activation state to deactivated if detected
+ * deactivated sim state and set it back to activated after successfully run activation service.
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3;
+
+ /**
+ * Restricted state indicate SIM has been activated but service are restricted.
+ * note this is currently available for data activation state. For example out of byte sim.
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4;
+
+ /**
+ * Sets the voice activation state for the given subscriber.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription id.
+ * @param activationState The voice activation state of the given subscriber.
+ * @see #SIM_ACTIVATION_STATE_UNKNOWN
+ * @see #SIM_ACTIVATION_STATE_ACTIVATING
+ * @see #SIM_ACTIVATION_STATE_ACTIVATED
+ * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ * @hide
+ */
+ public void setVoiceActivationState(int subId, int activationState) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setVoiceActivationState(subId, activationState);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Sets the data activation state for the given subscriber.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription id.
+ * @param activationState The data activation state of the given subscriber.
+ * @see #SIM_ACTIVATION_STATE_UNKNOWN
+ * @see #SIM_ACTIVATION_STATE_ACTIVATING
+ * @see #SIM_ACTIVATION_STATE_ACTIVATED
+ * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see #SIM_ACTIVATION_STATE_RESTRICTED
+ * @hide
+ */
+ public void setDataActivationState(int subId, int activationState) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setDataActivationState(subId, activationState);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Returns the voice activation state for the given subscriber.
+ *
+ * @param subId The subscription id.
+ *
+ * @return voiceActivationState for the given subscriber
+ * @see #SIM_ACTIVATION_STATE_UNKNOWN
+ * @see #SIM_ACTIVATION_STATE_ACTIVATING
+ * @see #SIM_ACTIVATION_STATE_ACTIVATED
+ * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getVoiceActivationState(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getVoiceActivationState(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return SIM_ACTIVATION_STATE_UNKNOWN;
+ }
+
+ /**
+ * Returns the data activation state for the given subscriber.
+ *
+ * @param subId The subscription id.
+ *
+ * @return dataActivationState for the given subscriber
+ * @see #SIM_ACTIVATION_STATE_UNKNOWN
+ * @see #SIM_ACTIVATION_STATE_ACTIVATING
+ * @see #SIM_ACTIVATION_STATE_ACTIVATED
+ * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see #SIM_ACTIVATION_STATE_RESTRICTED
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getDataActivationState(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getDataActivationState(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return SIM_ACTIVATION_STATE_UNKNOWN;
+ }
+
+ /**
+ * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages
+ * but the count is unknown.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getVoiceMessageCount() {
+ return getVoiceMessageCount(getSubId());
+ }
+
+ /**
+ * Returns the voice mail count for a subscription. Return 0 if unavailable.
+ * @param subId whose voice message count is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getVoiceMessageCount(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return 0;
+ return telephony.getVoiceMessageCountForSubscriber(subId);
+ } catch (RemoteException ex) {
+ return 0;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return 0;
+ }
+ }
+
+ /**
+ * Retrieves the alphabetic identifier associated with the voice
+ * mail number.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getVoiceMailAlphaTag() {
+ return getVoiceMailAlphaTag(getSubId());
+ }
+
+ /**
+ * Retrieves the alphabetic identifier associated with the voice
+ * mail number for a subscription.
+ * @param subId whose alphabetic identifier associated with the
+ * voice mail number is returned
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getVoiceMailAlphaTag(int subId) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Send the special dialer code. The IPC caller must be the current default dialer or has
+ * carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param inputCode The special dialer code to send
+ *
+ * @throws SecurityException if the caller does not have carrier privileges or is not the
+ * current default dialer
+ *
+ * @throws IllegalStateException if telephony service is unavailable.
+ */
+ public void sendDialerSpecialCode(String inputCode) {
+ try {
+ final ITelephony telephony = getITelephony();
+ telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ ex.rethrowFromSystemServer();
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ throw new IllegalStateException("Telephony service unavailable");
+ }
+ }
+
+ /**
+ * Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
+ * @return the IMPI, or null if not present or not loaded
+ * @hide
+ */
+ public String getIsimImpi() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ //get the Isim Impi based on subId
+ return info.getIsimImpi(getSubId());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMS home network domain name that was loaded from the ISIM.
+ * @return the IMS domain name, or null if not present or not loaded
+ * @hide
+ */
+ public String getIsimDomain() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ //get the Isim Domain based on subId
+ return info.getIsimDomain(getSubId());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
+ * @return an array of IMPU strings, with one IMPU per string, or null if
+ * not present or not loaded
+ * @hide
+ */
+ public String[] getIsimImpu() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ //get the Isim Impu based on subId
+ return info.getIsimImpu(getSubId());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private IPhoneSubInfo getSubscriberInfo() {
+ // get it each time because that process crashes a lot
+ return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
+ }
+
+ /** Device call state: No activity. */
+ public static final int CALL_STATE_IDLE = 0;
+ /** Device call state: Ringing. A new call arrived and is
+ * ringing or waiting. In the latter case, another call is
+ * already active. */
+ public static final int CALL_STATE_RINGING = 1;
+ /** Device call state: Off-hook. At least one call exists
+ * that is dialing, active, or on hold, and no calls are ringing
+ * or waiting. */
+ public static final int CALL_STATE_OFFHOOK = 2;
+
+ /**
+ * Returns one of the following constants that represents the current state of all
+ * phone calls.
+ *
+ * {@link TelephonyManager#CALL_STATE_RINGING}
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}
+ * {@link TelephonyManager#CALL_STATE_IDLE}
+ */
+ public int getCallState() {
+ try {
+ ITelecomService telecom = getTelecomService();
+ if (telecom != null) {
+ return telecom.getCallState();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#getCallState", e);
+ }
+ return CALL_STATE_IDLE;
+ }
+
+ /**
+ * Returns a constant indicating the call state (cellular) on the device
+ * for a subscription.
+ *
+ * @param subId whose call state is returned
+ * @hide
+ */
+ public int getCallState(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getCallStateForSlot(phoneId);
+ }
+
+ /**
+ * See getCallState.
+ *
+ * @hide
+ */
+ public int getCallStateForSlot(int slotIndex) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return CALL_STATE_IDLE;
+ return telephony.getCallStateForSlot(slotIndex);
+ } catch (RemoteException ex) {
+ // the phone process is restarting.
+ return CALL_STATE_IDLE;
+ } catch (NullPointerException ex) {
+ // the phone process is restarting.
+ return CALL_STATE_IDLE;
+ }
+ }
+
+
+ /** Data connection activity: No traffic. */
+ public static final int DATA_ACTIVITY_NONE = 0x00000000;
+ /** Data connection activity: Currently receiving IP PPP traffic. */
+ public static final int DATA_ACTIVITY_IN = 0x00000001;
+ /** Data connection activity: Currently sending IP PPP traffic. */
+ public static final int DATA_ACTIVITY_OUT = 0x00000002;
+ /** Data connection activity: Currently both sending and receiving
+ * IP PPP traffic. */
+ public static final int DATA_ACTIVITY_INOUT = DATA_ACTIVITY_IN | DATA_ACTIVITY_OUT;
+ /**
+ * Data connection is active, but physical link is down
+ */
+ public static final int DATA_ACTIVITY_DORMANT = 0x00000004;
+
+ /**
+ * Returns a constant indicating the type of activity on a data connection
+ * (cellular).
+ *
+ * @see #DATA_ACTIVITY_NONE
+ * @see #DATA_ACTIVITY_IN
+ * @see #DATA_ACTIVITY_OUT
+ * @see #DATA_ACTIVITY_INOUT
+ * @see #DATA_ACTIVITY_DORMANT
+ */
+ public int getDataActivity() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return DATA_ACTIVITY_NONE;
+ return telephony.getDataActivity();
+ } catch (RemoteException ex) {
+ // the phone process is restarting.
+ return DATA_ACTIVITY_NONE;
+ } catch (NullPointerException ex) {
+ // the phone process is restarting.
+ return DATA_ACTIVITY_NONE;
+ }
+ }
+
+ /** Data connection state: Unknown. Used before we know the state.
+ * @hide
+ */
+ public static final int DATA_UNKNOWN = -1;
+ /** Data connection state: Disconnected. IP traffic not available. */
+ public static final int DATA_DISCONNECTED = 0;
+ /** Data connection state: Currently setting up a data connection. */
+ public static final int DATA_CONNECTING = 1;
+ /** Data connection state: Connected. IP traffic should be available. */
+ public static final int DATA_CONNECTED = 2;
+ /** Data connection state: Suspended. The connection is up, but IP
+ * traffic is temporarily unavailable. For example, in a 2G network,
+ * data activity may be suspended when a voice call arrives. */
+ public static final int DATA_SUSPENDED = 3;
+
+ /**
+ * Returns a constant indicating the current data connection state
+ * (cellular).
+ *
+ * @see #DATA_DISCONNECTED
+ * @see #DATA_CONNECTING
+ * @see #DATA_CONNECTED
+ * @see #DATA_SUSPENDED
+ */
+ public int getDataState() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return DATA_DISCONNECTED;
+ return telephony.getDataState();
+ } catch (RemoteException ex) {
+ // the phone process is restarting.
+ return DATA_DISCONNECTED;
+ } catch (NullPointerException ex) {
+ return DATA_DISCONNECTED;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private ITelephony getITelephony() {
+ return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ }
+
+ /**
+ * @hide
+ */
+ private ITelecomService getTelecomService() {
+ return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
+ }
+
+ //
+ //
+ // PhoneStateListener
+ //
+ //
+
+ /**
+ * Registers a listener object to receive notification of changes
+ * in specified telephony states.
+ * <p>
+ * To register a listener, pass a {@link PhoneStateListener}
+ * and specify at least one telephony state of interest in
+ * the events argument.
+ *
+ * At registration, and when a specified telephony state
+ * changes, the telephony manager invokes the appropriate
+ * callback method on the listener object and passes the
+ * current (updated) values.
+ * <p>
+ * To unregister a listener, pass the listener object and set the
+ * events argument to
+ * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+ *
+ * @param listener The {@link PhoneStateListener} object to register
+ * (or unregister)
+ * @param events The telephony state(s) of interest to the listener,
+ * as a bitwise-OR combination of {@link PhoneStateListener}
+ * LISTEN_ flags.
+ */
+ public void listen(PhoneStateListener listener, int events) {
+ if (mContext == null) return;
+ try {
+ boolean notifyNow = (getITelephony() != null);
+ // If the listener has not explicitly set the subId (for example, created with the
+ // default constructor), replace the subId so it will listen to the account the
+ // telephony manager is created with.
+ if (listener.mSubId == null) {
+ listener.mSubId = mSubId;
+ }
+ sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(),
+ listener.callback, events, notifyNow);
+ } catch (RemoteException ex) {
+ // system process dead
+ } catch (NullPointerException ex) {
+ // system process dead
+ }
+ }
+
+ /**
+ * Returns the CDMA ERI icon index to display
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getCdmaEriIconIndex() {
+ return getCdmaEriIconIndex(getSubId());
+ }
+
+ /**
+ * Returns the CDMA ERI icon index to display for a subscription
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getCdmaEriIconIndex(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return -1;
+ return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ // the phone process is restarting.
+ return -1;
+ } catch (NullPointerException ex) {
+ return -1;
+ }
+ }
+
+ /**
+ * Returns the CDMA ERI icon mode,
+ * 0 - ON
+ * 1 - FLASHING
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getCdmaEriIconMode() {
+ return getCdmaEriIconMode(getSubId());
+ }
+
+ /**
+ * Returns the CDMA ERI icon mode for a subscription.
+ * 0 - ON
+ * 1 - FLASHING
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getCdmaEriIconMode(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return -1;
+ return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ // the phone process is restarting.
+ return -1;
+ } catch (NullPointerException ex) {
+ return -1;
+ }
+ }
+
+ /**
+ * Returns the CDMA ERI text,
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getCdmaEriText() {
+ return getCdmaEriText(getSubId());
+ }
+
+ /**
+ * Returns the CDMA ERI text, of a subscription
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String getCdmaEriText(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName());
+ } catch (RemoteException ex) {
+ // the phone process is restarting.
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * @return true if the current device is "voice capable".
+ * <p>
+ * "Voice capable" means that this device supports circuit-switched
+ * (i.e. voice) phone calls over the telephony network, and is allowed
+ * to display the in-call UI while a cellular voice call is active.
+ * This will be false on "data only" devices which can't make voice
+ * calls and don't support any in-call UI.
+ * <p>
+ * Note: the meaning of this flag is subtly different from the
+ * PackageManager.FEATURE_TELEPHONY system feature, which is available
+ * on any device with a telephony radio, even if the device is
+ * data-only.
+ */
+ public boolean isVoiceCapable() {
+ if (mContext == null) return true;
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_voice_capable);
+ }
+
+ /**
+ * @return true if the current device supports sms service.
+ * <p>
+ * If true, this means that the device supports both sending and
+ * receiving sms via the telephony network.
+ * <p>
+ * Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
+ * disabled when device doesn't support sms.
+ */
+ public boolean isSmsCapable() {
+ if (mContext == null) return true;
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_sms_capable);
+ }
+
+ /**
+ * Returns all observed cell information from all radios on the
+ * device including the primary and neighboring cells. Calling this method does
+ * not trigger a call to {@link android.telephony.PhoneStateListener#onCellInfoChanged
+ * onCellInfoChanged()}, or change the rate at which
+ * {@link android.telephony.PhoneStateListener#onCellInfoChanged
+ * onCellInfoChanged()} is called.
+ *
+ *<p>
+ * The list can include one or more {@link android.telephony.CellInfoGsm CellInfoGsm},
+ * {@link android.telephony.CellInfoCdma CellInfoCdma},
+ * {@link android.telephony.CellInfoLte CellInfoLte}, and
+ * {@link android.telephony.CellInfoWcdma CellInfoWcdma} objects, in any combination.
+ * On devices with multiple radios it is typical to see instances of
+ * one or more of any these in the list. In addition, zero, one, or more
+ * of the returned objects may be considered registered; that is, their
+ * {@link android.telephony.CellInfo#isRegistered CellInfo.isRegistered()}
+ * methods may return true.
+ *
+ * <p>This method returns valid data for registered cells on devices with
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}. In cases where only
+ * partial information is available for a particular CellInfo entry, unavailable fields
+ * will be reported as Integer.MAX_VALUE. All reported cells will include at least a
+ * valid set of technology-specific identification info and a power level measurement.
+ *
+ *<p>
+ * This method is preferred over using {@link
+ * android.telephony.TelephonyManager#getCellLocation getCellLocation()}.
+ * However, for older devices, <code>getAllCellInfo()</code> may return
+ * null. In these cases, you should call {@link
+ * android.telephony.TelephonyManager#getCellLocation getCellLocation()}
+ * instead.
+ *
+ * @return List of {@link android.telephony.CellInfo}; null if cell
+ * information is unavailable.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ public List<CellInfo> getAllCellInfo() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getAllCellInfo(getOpPackageName());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
+ * PhoneStateListener.onCellInfoChanged} will be invoked.
+ *<p>
+ * The default, 0, means invoke onCellInfoChanged when any of the reported
+ * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
+ * A onCellInfoChanged.
+ *<p>
+ * @param rateInMillis the rate
+ *
+ * @hide
+ */
+ public void setCellInfoListRate(int rateInMillis) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setCellInfoListRate(rateInMillis);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
+
+ /**
+ * Returns the MMS user agent.
+ */
+ public String getMmsUserAgent() {
+ if (mContext == null) return null;
+ return mContext.getResources().getString(
+ com.android.internal.R.string.config_mms_user_agent);
+ }
+
+ /**
+ * Returns the MMS user agent profile URL.
+ */
+ public String getMmsUAProfUrl() {
+ if (mContext == null) return null;
+ return mContext.getResources().getString(
+ com.android.internal.R.string.config_mms_user_agent_profile_url);
+ }
+
+ /**
+ * Opens a logical channel to the ICC card.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHO command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param AID Application id. See ETSI 102.221 and 101.220.
+ * @return an IccOpenLogicalChannelResponse object.
+ * @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)}
+ */
+ @Deprecated
+ public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) {
+ return iccOpenLogicalChannel(getSubId(), AID, -1);
+ }
+
+ /**
+ * Opens a logical channel to the ICC card.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHO command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param AID Application id. See ETSI 102.221 and 101.220.
+ * @param p2 P2 parameter (described in ISO 7816-4).
+ * @return an IccOpenLogicalChannelResponse object.
+ */
+ public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
+ return iccOpenLogicalChannel(getSubId(), AID, p2);
+ }
+
+ /**
+ * Opens a logical channel to the ICC card.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHO command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param AID Application id. See ETSI 102.221 and 101.220.
+ * @param p2 P2 parameter (described in ISO 7816-4).
+ * @return an IccOpenLogicalChannelResponse object.
+ * @hide
+ */
+ public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.iccOpenLogicalChannel(subId, getOpPackageName(), AID, p2);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Closes a previously opened logical channel to the ICC card.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHC command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param channel is the channel id to be closed as retruned by a successful
+ * iccOpenLogicalChannel.
+ * @return true if the channel was closed successfully.
+ */
+ public boolean iccCloseLogicalChannel(int channel) {
+ return iccCloseLogicalChannel(getSubId(), channel);
+ }
+
+ /**
+ * Closes a previously opened logical channel to the ICC card.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHC command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param channel is the channel id to be closed as retruned by a successful
+ * iccOpenLogicalChannel.
+ * @return true if the channel was closed successfully.
+ * @hide
+ */
+ public boolean iccCloseLogicalChannel(int subId, int channel) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.iccCloseLogicalChannel(subId, channel);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return false;
+ }
+
+ /**
+ * Transmit an APDU to the ICC card over a logical channel.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CGLA command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param channel is the channel id to be closed as returned by a successful
+ * iccOpenLogicalChannel.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at
+ * the end.
+ */
+ public String iccTransmitApduLogicalChannel(int channel, int cla,
+ int instruction, int p1, int p2, int p3, String data) {
+ return iccTransmitApduLogicalChannel(getSubId(), channel, cla,
+ instruction, p1, p2, p3, data);
+ }
+
+ /**
+ * Transmit an APDU to the ICC card over a logical channel.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CGLA command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param channel is the channel id to be closed as returned by a successful
+ * iccOpenLogicalChannel.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at
+ * the end.
+ * @hide
+ */
+ public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
+ int instruction, int p1, int p2, int p3, String data) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.iccTransmitApduLogicalChannel(subId, channel, cla,
+ instruction, p1, p2, p3, data);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return "";
+ }
+
+ /**
+ * Transmit an APDU to the ICC card over the basic channel.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CSIM command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at
+ * the end.
+ */
+ public String iccTransmitApduBasicChannel(int cla,
+ int instruction, int p1, int p2, int p3, String data) {
+ return iccTransmitApduBasicChannel(getSubId(), cla,
+ instruction, p1, p2, p3, data);
+ }
+
+ /**
+ * Transmit an APDU to the ICC card over the basic channel.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CSIM command.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at
+ * the end.
+ * @hide
+ */
+ public String iccTransmitApduBasicChannel(int subId, int cla,
+ int instruction, int p1, int p2, int p3, String data) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.iccTransmitApduBasicChannel(subId, cla,
+ instruction, p1, p2, p3, data);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return "";
+ }
+
+ /**
+ * Returns the response APDU for a command APDU sent through SIM_IO.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param fileID
+ * @param command
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command.
+ * @param filePath
+ * @return The APDU response.
+ */
+ public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
+ String filePath) {
+ return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath);
+ }
+
+ /**
+ * Returns the response APDU for a command APDU sent through SIM_IO.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param fileID
+ * @param command
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command.
+ * @param filePath
+ * @return The APDU response.
+ * @hide
+ */
+ public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
+ int p3, String filePath) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.iccExchangeSimIO(subId, fileID, command, p1, p2, p3, filePath);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Send ENVELOPE to the SIM and return the response.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param content String containing SAT/USAT response in hexadecimal
+ * format starting with command tag. See TS 102 223 for
+ * details.
+ * @return The APDU response from the ICC card in hexadecimal format
+ * with the last 4 bytes being the status word. If the command fails,
+ * returns an empty string.
+ */
+ public String sendEnvelopeWithStatus(String content) {
+ return sendEnvelopeWithStatus(getSubId(), content);
+ }
+
+ /**
+ * Send ENVELOPE to the SIM and return the response.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param content String containing SAT/USAT response in hexadecimal
+ * format starting with command tag. See TS 102 223 for
+ * details.
+ * @return The APDU response from the ICC card in hexadecimal format
+ * with the last 4 bytes being the status word. If the command fails,
+ * returns an empty string.
+ * @hide
+ */
+ public String sendEnvelopeWithStatus(int subId, String content) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.sendEnvelopeWithStatus(subId, content);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return "";
+ }
+
+ /**
+ * Read one of the NV items defined in com.android.internal.telephony.RadioNVItems.
+ * Used for device configuration by some CDMA operators.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param itemID the ID of the item to read.
+ * @return the NV item as a String, or null on any failure.
+ *
+ * @hide
+ */
+ public String nvReadItem(int itemID) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.nvReadItem(itemID);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "nvReadItem RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "nvReadItem NPE", ex);
+ }
+ return "";
+ }
+
+ /**
+ * Write one of the NV items defined in com.android.internal.telephony.RadioNVItems.
+ * Used for device configuration by some CDMA operators.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param itemID the ID of the item to read.
+ * @param itemValue the value to write, as a String.
+ * @return true on success; false on any failure.
+ *
+ * @hide
+ */
+ public boolean nvWriteItem(int itemID, String itemValue) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.nvWriteItem(itemID, itemValue);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "nvWriteItem RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "nvWriteItem NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
+ * Used for device configuration by some CDMA operators.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param preferredRoamingList byte array containing the new PRL.
+ * @return true on success; false on any failure.
+ *
+ * @hide
+ */
+ public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.nvWriteCdmaPrl(preferredRoamingList);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "nvWriteCdmaPrl RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "nvWriteCdmaPrl NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Perform the specified type of NV config reset. The radio will be taken offline
+ * and the device must be rebooted after the operation. Used for device
+ * configuration by some CDMA operators.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
+ * @return true on success; false on any failure.
+ *
+ * @hide
+ */
+ public boolean nvResetConfig(int resetType) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.nvResetConfig(resetType);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "nvResetConfig RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "nvResetConfig NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Return an appropriate subscription ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the provided
+ * subId is returned. Otherwise, the default subId will be returned.
+ */
+ private int getSubId() {
+ if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+ return mSubId;
+ }
+ return SubscriptionManager.getDefaultSubscriptionId();
+ }
+
+ /**
+ * Return an appropriate subscription ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the provided
+ * subId is returned. Otherwise, the preferred subId which is based on caller's context is
+ * returned.
+ * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
+ */
+ private int getSubId(int preferredSubId) {
+ if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+ return mSubId;
+ }
+ return preferredSubId;
+ }
+
+ /**
+ * Return an appropriate phone ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+ * associated with the provided subId is returned. Otherwise, the default phoneId associated
+ * with the default subId will be returned.
+ */
+ private int getPhoneId() {
+ return SubscriptionManager.getPhoneId(getSubId());
+ }
+
+ /**
+ * Return an appropriate phone ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+ * associated with the provided subId is returned. Otherwise, return the phoneId associated
+ * with the preferred subId based on caller's context.
+ * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
+ */
+ private int getPhoneId(int preferredSubId) {
+ return SubscriptionManager.getPhoneId(getSubId(preferredSubId));
+ }
+
+ /**
+ * Return an appropriate slot index for any situation.
+ *
+ * if this object has been created with {@link #createForSubscriptionId}, then the slot index
+ * associated with the provided subId is returned. Otherwise, return the slot index associated
+ * with the default subId.
+ * If SIM is not inserted, return default SIM slot index.
+ *
+ * {@hide}
+ */
+ @VisibleForTesting
+ public int getSlotIndex() {
+ int slotIndex = SubscriptionManager.getSlotIndex(getSubId());
+ if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) {
+ slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX;
+ }
+ return slotIndex;
+ }
+
+ /**
+ * Sets the telephony property with the value specified.
+ *
+ * @hide
+ */
+ public static void setTelephonyProperty(int phoneId, String property, String value) {
+ String propVal = "";
+ String p[] = null;
+ String prop = SystemProperties.get(property);
+
+ if (value == null) {
+ value = "";
+ }
+
+ if (prop != null) {
+ p = prop.split(",");
+ }
+
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+ Rlog.d(TAG, "setTelephonyProperty: invalid phoneId=" + phoneId +
+ " property=" + property + " value: " + value + " prop=" + prop);
+ return;
+ }
+
+ for (int i = 0; i < phoneId; i++) {
+ String str = "";
+ if ((p != null) && (i < p.length)) {
+ str = p[i];
+ }
+ propVal = propVal + str + ",";
+ }
+
+ propVal = propVal + value;
+ if (p != null) {
+ for (int i = phoneId + 1; i < p.length; i++) {
+ propVal = propVal + "," + p[i];
+ }
+ }
+
+ if (propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+ Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
+ " property=" + property + " value: " + value + " propVal=" + propVal);
+ return;
+ }
+
+ Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
+ " property=" + property + " value: " + value + " propVal=" + propVal);
+ SystemProperties.set(property, propVal);
+ }
+
+ /**
+ * Convenience function for retrieving a value from the secure settings
+ * value list as an integer. Note that internally setting values are
+ * always stored as strings; this function converts the string to an
+ * integer for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link SettingNotFoundException}.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ * @param index The index of the list
+ *
+ * @throws SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ *
+ * @return The value at the given index of settings.
+ * @hide
+ */
+ public static int getIntAtIndex(android.content.ContentResolver cr,
+ String name, int index)
+ throws android.provider.Settings.SettingNotFoundException {
+ String v = android.provider.Settings.Global.getString(cr, name);
+ if (v != null) {
+ String valArray[] = v.split(",");
+ if ((index >= 0) && (index < valArray.length) && (valArray[index] != null)) {
+ try {
+ return Integer.parseInt(valArray[index]);
+ } catch (NumberFormatException e) {
+ //Log.e(TAG, "Exception while parsing Integer: ", e);
+ }
+ }
+ }
+ throw new android.provider.Settings.SettingNotFoundException(name);
+ }
+
+ /**
+ * Convenience function for updating settings value as coma separated
+ * integer values. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to modify.
+ * @param index The index of the list
+ * @param value The new value for the setting to be added to the list.
+ * @return true if the value was set, false on database errors
+ * @hide
+ */
+ public static boolean putIntAtIndex(android.content.ContentResolver cr,
+ String name, int index, int value) {
+ String data = "";
+ String valArray[] = null;
+ String v = android.provider.Settings.Global.getString(cr, name);
+
+ if (index == Integer.MAX_VALUE) {
+ throw new RuntimeException("putIntAtIndex index == MAX_VALUE index=" + index);
+ }
+ if (index < 0) {
+ throw new RuntimeException("putIntAtIndex index < 0 index=" + index);
+ }
+ if (v != null) {
+ valArray = v.split(",");
+ }
+
+ // Copy the elements from valArray till index
+ for (int i = 0; i < index; i++) {
+ String str = "";
+ if ((valArray != null) && (i < valArray.length)) {
+ str = valArray[i];
+ }
+ data = data + str + ",";
+ }
+
+ data = data + value;
+
+ // Copy the remaining elements from valArray if any.
+ if (valArray != null) {
+ for (int i = index+1; i < valArray.length; i++) {
+ data = data + "," + valArray[i];
+ }
+ }
+ return android.provider.Settings.Global.putString(cr, name, data);
+ }
+
+ /**
+ * Gets the telephony property.
+ *
+ * @hide
+ */
+ public static String getTelephonyProperty(int phoneId, String property, String defaultVal) {
+ String propVal = null;
+ String prop = SystemProperties.get(property);
+ if ((prop != null) && (prop.length() > 0)) {
+ String values[] = prop.split(",");
+ if ((phoneId >= 0) && (phoneId < values.length) && (values[phoneId] != null)) {
+ propVal = values[phoneId];
+ }
+ }
+ return propVal == null ? defaultVal : propVal;
+ }
+
+ /** @hide */
+ public int getSimCount() {
+ // FIXME Need to get it from Telephony Dev Controller when that gets implemented!
+ // and then this method shouldn't be used at all!
+ if(isMultiSimEnabled()) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ * @return IMS Service Table or null if not present or not loaded
+ * @hide
+ */
+ public String getIsimIst() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ //get the Isim Ist based on subId
+ return info.getIsimIst(getSubId());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
+ * @return an array of PCSCF strings with one PCSCF per string, or null if
+ * not present or not loaded
+ * @hide
+ */
+ public String[] getIsimPcscf() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ //get the Isim Pcscf based on subId
+ return info.getIsimPcscf(getSubId());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
+ * Returns the response of ISIM Authetification through RIL.
+ * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
+ * @return the response of ISIM Authetification, or null if not available
+ * @hide
+ * @deprecated
+ * @see getIccAuthentication with appType=PhoneConstants.APPTYPE_ISIM
+ */
+ public String getIsimChallengeResponse(String nonce){
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getIsimChallengeResponse(nonce);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ // ICC SIM Application Types
+ /** UICC application type is SIM */
+ public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
+ /** UICC application type is USIM */
+ public static final int APPTYPE_USIM = PhoneConstants.APPTYPE_USIM;
+ /** UICC application type is RUIM */
+ public static final int APPTYPE_RUIM = PhoneConstants.APPTYPE_RUIM;
+ /** UICC application type is CSIM */
+ public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM;
+ /** UICC application type is ISIM */
+ public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM;
+ // authContext (parameter P2) when doing UICC challenge,
+ // per 3GPP TS 31.102 (Section 7.1.2)
+ /** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */
+ public static final int AUTHTYPE_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM;
+ /** Authentication type for UICC challenge is EAP AKA. See RFC 4187 for details. */
+ public static final int AUTHTYPE_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
+
+ /**
+ * Returns the response of authentication for the default subscription.
+ * Returns null if the authentication hasn't been successful
+ *
+ * <p>Requires that the calling app has carrier privileges or READ_PRIVILEGED_PHONE_STATE
+ * permission.
+ *
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
+ * {@link #AUTHTYPE_EAP_SIM}
+ * @param data authentication challenge data, base64 encoded.
+ * See 3GPP TS 31.102 7.1.2 for more details.
+ * @return the response of authentication, or null if not available
+ *
+ * @see #hasCarrierPrivileges
+ */
+ public String getIccAuthentication(int appType, int authType, String data) {
+ return getIccAuthentication(getSubId(), appType, authType, data);
+ }
+
+ /**
+ * Returns the response of USIM Authentication for specified subId.
+ * Returns null if the authentication hasn't been successful
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ *
+ * @param subId subscription ID used for authentication
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
+ * {@link #AUTHTYPE_EAP_SIM}
+ * @param data authentication challenge data, base64 encoded.
+ * See 3GPP TS 31.102 7.1.2 for more details.
+ * @return the response of authentication, or null if not available
+ *
+ * @see #hasCarrierPrivileges
+ * @hide
+ */
+ public String getIccAuthentication(int subId, int appType, int authType, String data) {
+ try {
+ IPhoneSubInfo info = getSubscriberInfo();
+ if (info == null)
+ return null;
+ return info.getIccSimChallengeResponse(subId, appType, authType, data);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone starts
+ return null;
+ }
+ }
+
+ /**
+ * Returns an array of Forbidden PLMNs from the USIM App
+ * Returns null if the query fails.
+ *
+ * @return an array of forbidden PLMNs or null if not available
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String[] getForbiddenPlmns() {
+ return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
+ }
+
+ /**
+ * Returns an array of Forbidden PLMNs from the specified SIM App
+ * Returns null if the query fails.
+ *
+ * @param subId subscription ID used for authentication
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ * @return fplmns an array of forbidden PLMNs
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public String[] getForbiddenPlmns(int subId, int appType) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getForbiddenPlmns(subId, appType);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone starts
+ return null;
+ }
+ }
+
+ /**
+ * Get P-CSCF address from PCO after data connection is established or modified.
+ * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
+ * @return array of P-CSCF address
+ * @hide
+ */
+ public String[] getPcscfAddress(String apnType) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return new String[0];
+ return telephony.getPcscfAddress(apnType, getOpPackageName());
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
+ /** @hide */
+ @IntDef({ImsFeature.EMERGENCY_MMTEL, ImsFeature.MMTEL, ImsFeature.RCS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Feature {}
+
+ /**
+ * Returns the {@link IImsServiceController} that corresponds to the given slot Id and IMS
+ * feature or {@link null} if the service is not available. If an ImsServiceController is
+ * available, the {@link IImsServiceFeatureListener} callback is registered as a listener for
+ * feature updates.
+ * @param slotIndex The SIM slot that we are requesting the {@link IImsServiceController} for.
+ * @param feature The IMS Feature we are requesting, corresponding to {@link ImsFeature}.
+ * @param callback Listener that will send updates to ImsManager when there are updates to
+ * ImsServiceController.
+ * @return {@link IImsServiceController} interface for the feature specified or {@link null} if
+ * it is unavailable.
+ * @hide
+ */
+ public IImsServiceController getImsServiceControllerAndListen(int slotIndex, @Feature int feature,
+ IImsServiceFeatureListener callback) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getImsServiceControllerAndListen(slotIndex, feature, callback);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "getImsServiceControllerAndListen, RemoteException: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Set IMS registration state
+ *
+ * @param Registration state
+ * @hide
+ */
+ public void setImsRegistrationState(boolean registered) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setImsRegistrationState(registered);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Get the preferred network type.
+ * Used for device configuration by some CDMA operators.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @return the preferred network type, defined in RILConstants.java.
+ * @hide
+ */
+ public int getPreferredNetworkType(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getPreferredNetworkType(subId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPreferredNetworkType NPE", ex);
+ }
+ return -1;
+ }
+
+ /**
+ * Sets the network selection mode to automatic.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @hide
+ * TODO: Add an overload that takes no args.
+ */
+ public void setNetworkSelectionModeAutomatic(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setNetworkSelectionModeAutomatic(subId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setNetworkSelectionModeAutomatic NPE", ex);
+ }
+ }
+
+ /**
+ * Perform a radio scan and return the list of avialble networks.
+ *
+ * The return value is a list of the OperatorInfo of the networks found. Note that this
+ * scan can take a long time (sometimes minutes) to happen.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @hide
+ * TODO: Add an overload that takes no args.
+ */
+ public CellNetworkScanResult getCellNetworkScanResults(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getCellNetworkScanResults(subId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCellNetworkScanResults RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getCellNetworkScanResults NPE", ex);
+ }
+ return null;
+ }
+
+ /**
+ * Request a network scan.
+ *
+ * This method is asynchronous, so the network scan results will be returned by callback.
+ * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param request Contains all the RAT with bands/channels that need to be scanned.
+ * @param callback Returns network scan results or errors.
+ * @return A NetworkScan obj which contains a callback which can stop the scan.
+ * @hide
+ */
+ public NetworkScan requestNetworkScan(
+ NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
+ synchronized (this) {
+ if (mTelephonyScanManager == null) {
+ mTelephonyScanManager = new TelephonyScanManager();
+ }
+ }
+ return mTelephonyScanManager.requestNetworkScan(getSubId(), request, callback);
+ }
+
+ /**
+ * Ask the radio to connect to the input network and change selection mode to manual.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @hide
+ * TODO: Add an overload that takes no args.
+ */
+ public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator,
+ boolean persistSelection) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setNetworkSelectionModeManual(subId, operator, persistSelection);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setNetworkSelectionModeManual NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Set the preferred network type.
+ * Used for device configuration by some CDMA operators.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param subId the id of the subscription to set the preferred network type for.
+ * @param networkType the preferred network type, defined in RILConstants.java.
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ public boolean setPreferredNetworkType(int subId, int networkType) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setPreferredNetworkType(subId, networkType);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+ *
+ * <p>
+ * Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @return true on success; false on any failure.
+ */
+ public boolean setPreferredNetworkTypeToGlobal() {
+ return setPreferredNetworkTypeToGlobal(getSubId());
+ }
+
+ /**
+ * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+ *
+ * <p>
+ * Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ public boolean setPreferredNetworkTypeToGlobal(int subId) {
+ return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ }
+
+ /**
+ * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
+ * SystemProperty, and config_tether_apndata to decide whether DUN APN is required for
+ * tethering.
+ *
+ * @return 0: Not required. 1: required. 2: Not set.
+ * @hide
+ */
+ public int getTetherApnRequired() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getTetherApnRequired();
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "hasMatchedTetherApnSetting RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "hasMatchedTetherApnSetting NPE", ex);
+ }
+ return 2;
+ }
+
+
+ /**
+ * Values used to return status for hasCarrierPrivileges call.
+ */
+ /** @hide */ @SystemApi
+ public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
+ /** @hide */ @SystemApi
+ public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
+ /** @hide */ @SystemApi
+ public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
+ /** @hide */ @SystemApi
+ public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
+
+ /**
+ * Has the calling application been granted carrier privileges by the carrier.
+ *
+ * If any of the packages in the calling UID has carrier privileges, the
+ * call will return true. This access is granted by the owner of the UICC
+ * card and does not depend on the registered carrier.
+ *
+ * @return true if the app has carrier privileges.
+ */
+ public boolean hasCarrierPrivileges() {
+ return hasCarrierPrivileges(getSubId());
+ }
+
+ /**
+ * Has the calling application been granted carrier privileges by the carrier.
+ *
+ * If any of the packages in the calling UID has carrier privileges, the
+ * call will return true. This access is granted by the owner of the UICC
+ * card and does not depend on the registered carrier.
+ *
+ * @param subId The subscription to use.
+ * @return true if the app has carrier privileges.
+ * @hide
+ */
+ public boolean hasCarrierPrivileges(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getCarrierPrivilegeStatus(mSubId) ==
+ CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Override the branding for the current ICCID.
+ *
+ * Once set, whenever the SIM is present in the device, the service
+ * provider name (SPN) and the operator name will both be replaced by the
+ * brand value input. To unset the value, the same function should be
+ * called with a null brand value.
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param brand The brand name to display/set.
+ * @return true if the operation was executed correctly.
+ */
+ public boolean setOperatorBrandOverride(String brand) {
+ return setOperatorBrandOverride(getSubId(), brand);
+ }
+
+ /**
+ * Override the branding for the current ICCID.
+ *
+ * Once set, whenever the SIM is present in the device, the service
+ * provider name (SPN) and the operator name will both be replaced by the
+ * brand value input. To unset the value, the same function should be
+ * called with a null brand value.
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ * @see #hasCarrierPrivileges
+ *
+ * @param subId The subscription to use.
+ * @param brand The brand name to display/set.
+ * @return true if the operation was executed correctly.
+ * @hide
+ */
+ public boolean setOperatorBrandOverride(int subId, String brand) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setOperatorBrandOverride(subId, brand);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setOperatorBrandOverride RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setOperatorBrandOverride NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Override the roaming preference for the current ICCID.
+ *
+ * Using this call, the carrier app (see #hasCarrierPrivileges) can override
+ * the platform's notion of a network operator being considered roaming or not.
+ * The change only affects the ICCID that was active when this call was made.
+ *
+ * If null is passed as any of the input, the corresponding value is deleted.
+ *
+ * <p>Requires that the caller have carrier privilege. See #hasCarrierPrivileges.
+ *
+ * @param gsmRoamingList - List of MCCMNCs to be considered roaming for 3GPP RATs.
+ * @param gsmNonRoamingList - List of MCCMNCs to be considered not roaming for 3GPP RATs.
+ * @param cdmaRoamingList - List of SIDs to be considered roaming for 3GPP2 RATs.
+ * @param cdmaNonRoamingList - List of SIDs to be considered not roaming for 3GPP2 RATs.
+ * @return true if the operation was executed correctly.
+ *
+ * @hide
+ */
+ public boolean setRoamingOverride(List<String> gsmRoamingList,
+ List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
+ List<String> cdmaNonRoamingList) {
+ return setRoamingOverride(getSubId(), gsmRoamingList, gsmNonRoamingList,
+ cdmaRoamingList, cdmaNonRoamingList);
+ }
+
+ /**
+ * Override the roaming preference for the current ICCID.
+ *
+ * Using this call, the carrier app (see #hasCarrierPrivileges) can override
+ * the platform's notion of a network operator being considered roaming or not.
+ * The change only affects the ICCID that was active when this call was made.
+ *
+ * If null is passed as any of the input, the corresponding value is deleted.
+ *
+ * <p>Requires that the caller have carrier privilege. See #hasCarrierPrivileges.
+ *
+ * @param subId for which the roaming overrides apply.
+ * @param gsmRoamingList - List of MCCMNCs to be considered roaming for 3GPP RATs.
+ * @param gsmNonRoamingList - List of MCCMNCs to be considered not roaming for 3GPP RATs.
+ * @param cdmaRoamingList - List of SIDs to be considered roaming for 3GPP2 RATs.
+ * @param cdmaNonRoamingList - List of SIDs to be considered not roaming for 3GPP2 RATs.
+ * @return true if the operation was executed correctly.
+ *
+ * @hide
+ */
+ public boolean setRoamingOverride(int subId, List<String> gsmRoamingList,
+ List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
+ List<String> cdmaNonRoamingList) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setRoamingOverride(subId, gsmRoamingList, gsmNonRoamingList,
+ cdmaRoamingList, cdmaNonRoamingList);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setRoamingOverride RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setRoamingOverride NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Expose the rest of ITelephony to @SystemApi
+ */
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public String getCdmaMdn() {
+ return getCdmaMdn(getSubId());
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public String getCdmaMdn(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getCdmaMdn(subId);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public String getCdmaMin() {
+ return getCdmaMin(getSubId());
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public String getCdmaMin(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getCdmaMin(subId);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public int checkCarrierPrivilegesForPackage(String pkgName) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.checkCarrierPrivilegesForPackage(pkgName);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "checkCarrierPrivilegesForPackage NPE", ex);
+ }
+ return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+
+ /** @hide */
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.checkCarrierPrivilegesForPackageAnyPhone(pkgName);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone NPE", ex);
+ }
+ return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+
+ /** @hide */
+ @SystemApi
+ public List<String> getCarrierPackageNamesForIntent(Intent intent) {
+ return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
+ }
+
+ /** @hide */
+ @SystemApi
+ public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.getCarrierPackageNamesForIntentAndPhone(intent, phoneId);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCarrierPackageNamesForIntentAndPhone RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getCarrierPackageNamesForIntentAndPhone NPE", ex);
+ }
+ return null;
+ }
+
+ /** @hide */
+ public List<String> getPackagesWithCarrierPrivileges() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getPackagesWithCarrierPrivileges();
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPackagesWithCarrierPrivileges RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPackagesWithCarrierPrivileges NPE", ex);
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+ /** @hide */
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public void dial(String number) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.dial(number);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#dial", e);
+ }
+ }
+
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#placeCall(Uri address,
+ * Bundle extras)} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ public void call(String callingPackage, String number) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.call(callingPackage, number);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#call", e);
+ }
+ }
+
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ public boolean endCall() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.endCall();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#endCall", e);
+ }
+ return false;
+ }
+
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void answerRingingCall() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.answerRingingCall();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#answerRingingCall", e);
+ }
+ }
+
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#silenceRinger} instead
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public void silenceRinger() {
+ try {
+ getTelecomService().silenceRinger(getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
+ public boolean isOffhook() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.isOffhook(getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isOffhook", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
+ public boolean isRinging() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.isRinging(getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isRinging", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
+ public boolean isIdle() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.isIdle(getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isIdle", e);
+ }
+ return true;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
+ public boolean isRadioOn() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.isRadioOn(getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isRadioOn", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean supplyPin(String pin) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.supplyPin(pin);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#supplyPin", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean supplyPuk(String puk, String pin) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.supplyPuk(puk, pin);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#supplyPuk", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int[] supplyPinReportResult(String pin) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.supplyPinReportResult(pin);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#supplyPinReportResult", e);
+ }
+ return new int[0];
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int[] supplyPukReportResult(String puk, String pin) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.supplyPukReportResult(puk, pin);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#]", e);
+ }
+ return new int[0];
+ }
+
+ /**
+ * Used to notify callers of
+ * {@link TelephonyManager#sendUssdRequest(String, UssdResponseCallback, Handler)} when the
+ * network either successfully executes a USSD request, or if there was a failure while
+ * executing the request.
+ * <p>
+ * {@link #onReceiveUssdResponse(TelephonyManager, String, CharSequence)} will be called if the
+ * USSD request has succeeded.
+ * {@link #onReceiveUssdResponseFailed(TelephonyManager, String, int)} will be called if the
+ * USSD request has failed.
+ */
+ public static abstract class UssdResponseCallback {
+ /**
+ * Called when a USSD request has succeeded. The {@code response} contains the USSD
+ * response received from the network. The calling app can choose to either display the
+ * response to the user or perform some operation based on the response.
+ * <p>
+ * USSD responses are unstructured text and their content is determined by the mobile network
+ * operator.
+ *
+ * @param telephonyManager the TelephonyManager the callback is registered to.
+ * @param request the USSD request sent to the mobile network.
+ * @param response the response to the USSD request provided by the mobile network.
+ **/
+ public void onReceiveUssdResponse(final TelephonyManager telephonyManager,
+ String request, CharSequence response) {};
+
+ /**
+ * Called when a USSD request has failed to complete.
+ *
+ * @param telephonyManager the TelephonyManager the callback is registered to.
+ * @param request the USSD request sent to the mobile network.
+ * @param failureCode failure code indicating why the request failed. Will be either
+ * {@link TelephonyManager#USSD_RETURN_FAILURE} or
+ * {@link TelephonyManager#USSD_ERROR_SERVICE_UNAVAIL}.
+ **/
+ public void onReceiveUssdResponseFailed(final TelephonyManager telephonyManager,
+ String request, int failureCode) {};
+ }
+
+ /**
+ * Sends an Unstructured Supplementary Service Data (USSD) request to the mobile network and
+ * informs the caller of the response via the supplied {@code callback}.
+ * <p>Carriers define USSD codes which can be sent by the user to request information such as
+ * the user's current data balance or minutes balance.
+ * <p>Requires permission:
+ * {@link android.Manifest.permission#CALL_PHONE}
+ * @param ussdRequest the USSD command to be executed.
+ * @param callback called by the framework to inform the caller of the result of executing the
+ * USSD request (see {@link UssdResponseCallback}).
+ * @param handler the {@link Handler} to run the request on.
+ */
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ public void sendUssdRequest(String ussdRequest,
+ final UssdResponseCallback callback, Handler handler) {
+ checkNotNull(callback, "UssdResponseCallback cannot be null.");
+ final TelephonyManager telephonyManager = this;
+
+ ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle ussdResponse) {
+ Rlog.d(TAG, "USSD:" + resultCode);
+ checkNotNull(ussdResponse, "ussdResponse cannot be null.");
+ UssdResponse response = ussdResponse.getParcelable(USSD_RESPONSE);
+
+ if (resultCode == USSD_RETURN_SUCCESS) {
+ callback.onReceiveUssdResponse(telephonyManager, response.getUssdRequest(),
+ response.getReturnMessage());
+ } else {
+ callback.onReceiveUssdResponseFailed(telephonyManager,
+ response.getUssdRequest(), resultCode);
+ }
+ }
+ };
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.handleUssdRequest(getSubId(), ussdRequest, wrappedCallback);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e);
+ UssdResponse response = new UssdResponse(ussdRequest, "");
+ Bundle returnData = new Bundle();
+ returnData.putParcelable(USSD_RESPONSE, response);
+ wrappedCallback.send(USSD_ERROR_SERVICE_UNAVAIL, returnData);
+ }
+ }
+
+ /**
+ * Whether the device is currently on a technology (e.g. UMTS or LTE) which can support
+ * voice and data simultaneously. This can change based on location or network condition.
+ *
+ * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise.
+ */
+ public boolean isConcurrentVoiceAndDataSupported() {
+ try {
+ ITelephony telephony = getITelephony();
+ return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(
+ getSubId()));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean handlePinMmi(String dialString) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.handlePinMmi(dialString);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#handlePinMmi", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean handlePinMmiForSubscriber(int subId, String dialString) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.handlePinMmiForSubscriber(subId, dialString);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#handlePinMmi", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void toggleRadioOnOff() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.toggleRadioOnOff();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#toggleRadioOnOff", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setRadio(boolean turnOn) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setRadio(turnOn);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setRadio", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setRadioPower(boolean turnOn) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.setRadioPower(turnOn);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setRadioPower", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public void updateServiceLocation() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.updateServiceLocation();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean enableDataConnectivity() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.enableDataConnectivity();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#enableDataConnectivity", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean disableDataConnectivity() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.disableDataConnectivity();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#disableDataConnectivity", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ public boolean isDataConnectivityPossible() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager
+ .getDefaultDataSubscriptionId()));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isDataAllowed", e);
+ }
+ return false;
+ }
+
+ /** @hide */
+ @SystemApi
+ public boolean needsOtaServiceProvisioning() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.needsOtaServiceProvisioning();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#needsOtaServiceProvisioning", e);
+ }
+ return false;
+ }
+
+ /**
+ * Turns mobile data on or off.
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+ * calling app has carrier privileges.
+ *
+ * @param enable Whether to enable mobile data.
+ *
+ * @see #hasCarrierPrivileges
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDataEnabled(boolean enable) {
+ setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDataEnabled(int subId, boolean enable) {
+ try {
+ Log.d(TAG, "setDataEnabled: enabled=" + enable);
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setDataEnabled(subId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
+ }
+ }
+
+
+ /**
+ * @deprecated use {@link #isDataEnabled()} instead.
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ public boolean getDataEnabled() {
+ return isDataEnabled();
+ }
+
+ /**
+ * Returns whether mobile data is enabled or not.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires one of the following permissions:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+ * calling app has carrier privileges.
+ *
+ * <p>Note that this does not take into account any data restrictions that may be present on the
+ * calling app. Such restrictions may be inspected with
+ * {@link ConnectivityManager#getRestrictBackgroundStatus}.
+ *
+ * @return true if mobile data is enabled.
+ *
+ * @see #hasCarrierPrivileges
+ */
+ @SuppressWarnings("deprecation")
+ public boolean isDataEnabled() {
+ return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ }
+
+ /**
+ * @deprecated use {@link #isDataEnabled(int)} instead.
+ * @hide
+ */
+ @SystemApi
+ public boolean getDataEnabled(int subId) {
+ boolean retVal = false;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ retVal = telephony.getDataEnabled(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
+ } catch (NullPointerException e) {
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return -1;
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void enableVideoCalling(boolean enable) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.enableVideoCalling(enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#enableVideoCalling", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
+ public boolean isVideoCallingEnabled() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.isVideoCallingEnabled(getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e);
+ }
+ return false;
+ }
+
+ /**
+ * Whether the device supports configuring the DTMF tone length.
+ *
+ * @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise.
+ */
+ public boolean canChangeDtmfToneLength() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.canChangeDtmfToneLength();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#canChangeDtmfToneLength", e);
+ }
+ return false;
+ }
+
+ /**
+ * Whether the device is a world phone.
+ *
+ * @return {@code true} if the device is a world phone, and {@code false} otherwise.
+ */
+ public boolean isWorldPhone() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isWorldPhone();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e);
+ }
+ return false;
+ }
+
+ /**
+ * @deprecated Use {@link TelecomManager#isTtySupported()} instead
+ * Whether the phone supports TTY mode.
+ *
+ * @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
+ *
+ */
+ @Deprecated
+ public boolean isTtyModeSupported() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isTtyModeSupported();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isTtyModeSupported", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#isTtyModeSupported", e);
+ }
+ return false;
+ }
+
+ /**
+ * Whether the phone supports hearing aid compatibility.
+ *
+ * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
+ * otherwise.
+ */
+ public boolean isHearingAidCompatibilitySupported() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isHearingAidCompatibilitySupported();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isHearingAidCompatibilitySupported", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#isHearingAidCompatibilitySupported", e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the IMS Registration Status
+ * @hide
+ */
+ public boolean isImsRegistered() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return false;
+ return telephony.isImsRegistered();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the IMS Registration Status for a particular Subscription ID
+ *
+ * @param subId Subscription ID
+ * @return true if IMS status is registered, false if the IMS status is not registered or a
+ * RemoteException occurred.
+ *
+ * @hide
+ */
+ public boolean isImsRegistered(int subId) {
+ try {
+ return getITelephony().isImsRegisteredForSubscriber(subId);
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Status of Volte
+ * @hide
+ */
+ public boolean isVolteAvailable() {
+ try {
+ return getITelephony().isVolteAvailable();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Status of video telephony (VT)
+ * @hide
+ */
+ public boolean isVideoTelephonyAvailable() {
+ try {
+ return getITelephony().isVideoTelephonyAvailable();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Status of Wi-Fi Calling
+ * @hide
+ */
+ public boolean isWifiCallingAvailable() {
+ try {
+ return getITelephony().isWifiCallingAvailable();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNumeric(String numeric) {
+ int phoneId = getPhoneId();
+ setSimOperatorNumericForPhone(phoneId, numeric);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNumericForPhone(int phoneId, String numeric) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, numeric);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorName(String name) {
+ int phoneId = getPhoneId();
+ setSimOperatorNameForPhone(phoneId, name);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNameForPhone(int phoneId, String name) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, name);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the default phone.
+ *
+ * @hide
+ */
+ public void setSimCountryIso(String iso) {
+ int phoneId = getPhoneId();
+ setSimCountryIsoForPhone(phoneId, iso);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the given phone.
+ *
+ * @hide
+ */
+ public void setSimCountryIsoForPhone(int phoneId, String iso) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_SIM_STATE for the default phone.
+ *
+ * @hide
+ */
+ public void setSimState(String state) {
+ int phoneId = getPhoneId();
+ setSimStateForPhone(phoneId, state);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_SIM_STATE for the given phone.
+ *
+ * @hide
+ */
+ public void setSimStateForPhone(int phoneId, String state) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SIM_STATE, state);
+ }
+
+ /**
+ * Requested state of SIM
+ *
+ * CARD_POWER_DOWN
+ * Powers down the SIM. SIM must be up prior.
+ *
+ * CARD_POWER_UP
+ * Powers up the SIM normally. SIM must be down prior.
+ *
+ * CARD_POWER_UP_PASS_THROUGH
+ * Powers up the SIM in PASS_THROUGH mode. SIM must be down prior.
+ * When SIM is powered up in PASS_THOUGH mode, the modem does not send
+ * any command to it (for example SELECT of MF, or TERMINAL CAPABILITY),
+ * and the SIM card is controlled completely by Telephony sending APDUs
+ * directly. The SIM card state will be RIL_CARDSTATE_PRESENT and the
+ * number of card apps will be 0.
+ * No new error code is generated. Emergency calls are supported in the
+ * same way as if the SIM card is absent.
+ * The PASS_THROUGH mode is valid only for the specific card session where it
+ * is activated, and normal behavior occurs at the next SIM initialization,
+ * unless PASS_THROUGH mode is requested again. Hence, the last power-up mode
+ * is NOT persistent across boots. On reboot, SIM will power up normally.
+ */
+ /** @hide */
+ public static final int CARD_POWER_DOWN = 0;
+ /** @hide */
+ public static final int CARD_POWER_UP = 1;
+ /** @hide */
+ public static final int CARD_POWER_UP_PASS_THROUGH = 2;
+
+ /**
+ * Set SIM card power state.
+ *
+ * @param state State of SIM (power down, power up, pass through)
+ * @see #CARD_POWER_DOWN
+ * @see #CARD_POWER_UP
+ * @see #CARD_POWER_UP_PASS_THROUGH
+ * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+ * broadcasts to determine success or failure and timeout if needed.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @hide
+ **/
+ public void setSimPowerState(int state) {
+ setSimPowerStateForSlot(getSlotIndex(), state);
+ }
+
+ /**
+ * Set SIM card power state.
+ *
+ * @param slotIndex SIM slot id
+ * @param state State of SIM (power down, power up, pass through)
+ * @see #CARD_POWER_DOWN
+ * @see #CARD_POWER_UP
+ * @see #CARD_POWER_UP_PASS_THROUGH
+ * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+ * broadcasts to determine success or failure and timeout if needed.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @hide
+ **/
+ public void setSimPowerStateForSlot(int slotIndex, int state) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setSimPowerStateForSlot(slotIndex, state);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e);
+ }
+ }
+
+ /**
+ * Set baseband version for the default phone.
+ *
+ * @param version baseband version
+ * @hide
+ */
+ public void setBasebandVersion(String version) {
+ int phoneId = getPhoneId();
+ setBasebandVersionForPhone(phoneId, version);
+ }
+
+ /**
+ * Set baseband version by phone id.
+ *
+ * @param phoneId for which baseband version is set
+ * @param version baseband version
+ * @hide
+ */
+ public void setBasebandVersionForPhone(int phoneId, String version) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
+ ((phoneId == 0) ? "" : Integer.toString(phoneId));
+ SystemProperties.set(prop, version);
+ }
+ }
+
+ /**
+ * Set phone type for the default phone.
+ *
+ * @param type phone type
+ *
+ * @hide
+ */
+ public void setPhoneType(int type) {
+ int phoneId = getPhoneId();
+ setPhoneType(phoneId, type);
+ }
+
+ /**
+ * Set phone type by phone id.
+ *
+ * @param phoneId for which phone type is set
+ * @param type phone type
+ *
+ * @hide
+ */
+ public void setPhoneType(int phoneId, int type) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ TelephonyManager.setTelephonyProperty(phoneId,
+ TelephonyProperties.CURRENT_ACTIVE_PHONE, String.valueOf(type));
+ }
+ }
+
+ /**
+ * Get OTASP number schema for the default phone.
+ *
+ * @param defaultValue default value
+ * @return OTA SP number schema
+ *
+ * @hide
+ */
+ public String getOtaSpNumberSchema(String defaultValue) {
+ int phoneId = getPhoneId();
+ return getOtaSpNumberSchemaForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get OTASP number schema by phone id.
+ *
+ * @param phoneId for which OTA SP number schema is get
+ * @param defaultValue default value
+ * @return OTA SP number schema
+ *
+ * @hide
+ */
+ public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA, defaultValue);
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get SMS receive capable from system property for the default phone.
+ *
+ * @param defaultValue default value
+ * @return SMS receive capable
+ *
+ * @hide
+ */
+ public boolean getSmsReceiveCapable(boolean defaultValue) {
+ int phoneId = getPhoneId();
+ return getSmsReceiveCapableForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get SMS receive capable from system property by phone id.
+ *
+ * @param phoneId for which SMS receive capable is get
+ * @param defaultValue default value
+ * @return SMS receive capable
+ *
+ * @hide
+ */
+ public boolean getSmsReceiveCapableForPhone(int phoneId, boolean defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return Boolean.parseBoolean(TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SMS_RECEIVE, String.valueOf(defaultValue)));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get SMS send capable from system property for the default phone.
+ *
+ * @param defaultValue default value
+ * @return SMS send capable
+ *
+ * @hide
+ */
+ public boolean getSmsSendCapable(boolean defaultValue) {
+ int phoneId = getPhoneId();
+ return getSmsSendCapableForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get SMS send capable from system property by phone id.
+ *
+ * @param phoneId for which SMS send capable is get
+ * @param defaultValue default value
+ * @return SMS send capable
+ *
+ * @hide
+ */
+ public boolean getSmsSendCapableForPhone(int phoneId, boolean defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return Boolean.parseBoolean(TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SMS_SEND, String.valueOf(defaultValue)));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Set the alphabetic name of current registered operator.
+ * @param name the alphabetic name of current registered operator.
+ * @hide
+ */
+ public void setNetworkOperatorName(String name) {
+ int phoneId = getPhoneId();
+ setNetworkOperatorNameForPhone(phoneId, name);
+ }
+
+ /**
+ * Set the alphabetic name of current registered operator.
+ * @param phoneId which phone you want to set
+ * @param name the alphabetic name of current registered operator.
+ * @hide
+ */
+ public void setNetworkOperatorNameForPhone(int phoneId, String name) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, name);
+ }
+ }
+
+ /**
+ * Set the numeric name (MCC+MNC) of current registered operator.
+ * @param operator the numeric name (MCC+MNC) of current registered operator
+ * @hide
+ */
+ public void setNetworkOperatorNumeric(String numeric) {
+ int phoneId = getPhoneId();
+ setNetworkOperatorNumericForPhone(phoneId, numeric);
+ }
+
+ /**
+ * Set the numeric name (MCC+MNC) of current registered operator.
+ * @param phoneId for which phone type is set
+ * @param operator the numeric name (MCC+MNC) of current registered operator
+ * @hide
+ */
+ public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, numeric);
+ }
+
+ /**
+ * Set roaming state of the current network, for GSM purposes.
+ * @param isRoaming is network in romaing state or not
+ * @hide
+ */
+ public void setNetworkRoaming(boolean isRoaming) {
+ int phoneId = getPhoneId();
+ setNetworkRoamingForPhone(phoneId, isRoaming);
+ }
+
+ /**
+ * Set roaming state of the current network, for GSM purposes.
+ * @param phoneId which phone you want to set
+ * @param isRoaming is network in romaing state or not
+ * @hide
+ */
+ public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
+ isRoaming ? "true" : "false");
+ }
+ }
+
+ /**
+ * Set the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * @param iso the ISO country code equivalent of the current registered
+ * @hide
+ */
+ public void setNetworkCountryIso(String iso) {
+ int phoneId = getPhoneId();
+ setNetworkCountryIsoForPhone(phoneId, iso);
+ }
+
+ /**
+ * Set the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * @param phoneId which phone you want to set
+ * @param iso the ISO country code equivalent of the current registered
+ * @hide
+ */
+ public void setNetworkCountryIsoForPhone(int phoneId, String iso) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
+ }
+ }
+
+ /**
+ * Set the network type currently in use on the device for data transmission.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * phoneId associated with the given subId. Otherwise, applies to the phoneId associated with
+ * {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ * @param type the network type currently in use on the device for data transmission
+ * @hide
+ */
+ public void setDataNetworkType(int type) {
+ int phoneId = getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId());
+ setDataNetworkTypeForPhone(phoneId, type);
+ }
+
+ /**
+ * Set the network type currently in use on the device for data transmission.
+ * @param phoneId which phone you want to set
+ * @param type the network type currently in use on the device for data transmission
+ * @hide
+ */
+ public void setDataNetworkTypeForPhone(int phoneId, int type) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToString(type));
+ }
+ }
+
+ /**
+ * Returns the subscription ID for the given phone account.
+ * @hide
+ */
+ public int getSubIdForPhoneAccount(PhoneAccount phoneAccount) {
+ int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ retval = service.getSubIdForPhoneAccount(phoneAccount);
+ }
+ } catch (RemoteException e) {
+ }
+
+ return retval;
+ }
+
+ private int getSubIdForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ try {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ retval = getSubIdForPhoneAccount(service.getPhoneAccount(phoneAccountHandle));
+ }
+ } catch (RemoteException e) {
+ }
+
+ return retval;
+ }
+
+ /**
+ * Resets telephony manager settings back to factory defaults.
+ *
+ * @hide
+ */
+ public void factoryReset(int subId) {
+ try {
+ Log.d(TAG, "factoryReset: subId=" + subId);
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.factoryReset(subId);
+ } catch (RemoteException e) {
+ }
+ }
+
+
+ /** @hide */
+ public String getLocaleFromDefaultSim() {
+ try {
+ final ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getLocaleFromDefaultSim();
+ }
+ } catch (RemoteException ex) {
+ }
+ return null;
+ }
+
+ /**
+ * Requests the modem activity info. The recipient will place the result
+ * in `result`.
+ * @param result The object on which the recipient will send the resulting
+ * {@link android.telephony.ModemActivityInfo} object.
+ * @hide
+ */
+ public void requestModemActivityInfo(ResultReceiver result) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.requestModemActivityInfo(result);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
+ }
+ result.send(0, null);
+ }
+
+ /**
+ * Returns the current {@link ServiceState} information.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public ServiceState getServiceState() {
+ return getServiceStateForSubscriber(getSubId());
+ }
+
+ /**
+ * Returns the service state information on specified subscription. Callers require
+ * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+ * @hide
+ */
+ public ServiceState getServiceStateForSubscriber(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getServiceStateForSubscriber(subId, getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the URI for the per-account voicemail ringtone set in Phone settings.
+ *
+ * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+ * voicemail ringtone.
+ * @return The URI for the ringtone to play when receiving a voicemail from a specific
+ * PhoneAccount.
+ */
+ public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getVoicemailRingtoneUri(accountHandle);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getVoicemailRingtoneUri", e);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the per-account voicemail ringtone.
+ *
+ * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
+ * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ *
+ * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
+ * voicemail ringtone.
+ * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
+ * PhoneAccount.
+ * @see #hasCarrierPrivileges
+ */
+ public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.setVoicemailRingtoneUri(getOpPackageName(), phoneAccountHandle, uri);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setVoicemailRingtoneUri", e);
+ }
+ }
+
+ /**
+ * Returns whether vibration is set for voicemail notification in Phone settings.
+ *
+ * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+ * voicemail vibration setting.
+ * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+ */
+ public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.isVoicemailVibrationEnabled(accountHandle);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e);
+ }
+ return false;
+ }
+
+ /**
+ * Sets the per-account preference whether vibration is enabled for voicemail notifications.
+ *
+ * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
+ * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ *
+ * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
+ * voicemail vibration setting.
+ * @param enabled Whether to enable or disable vibration for voicemail notifications from a
+ * specific PhoneAccount.
+ * @see #hasCarrierPrivileges
+ */
+ public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
+ boolean enabled) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.setVoicemailVibrationEnabled(getOpPackageName(), phoneAccountHandle,
+ enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e);
+ }
+ }
+
+ /**
+ * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param appType the uicc app type like {@link APPTYPE_CSIM}
+ * @return Application ID for specificied app type or null if no uicc or error.
+ * @hide
+ */
+ public String getAidForAppType(int appType) {
+ return getAidForAppType(getSubId(), appType);
+ }
+
+ /**
+ * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @param appType the uicc app type, like {@link APPTYPE_CSIM}
+ * @return Application ID for specificied app type or null if no uicc or error.
+ * @hide
+ */
+ public String getAidForAppType(int subId, int appType) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getAidForAppType(subId, appType);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getAidForAppType", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the Electronic Serial Number.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @return ESN or null if error.
+ * @hide
+ */
+ public String getEsn() {
+ return getEsn(getSubId());
+ }
+
+ /**
+ * Return the Electronic Serial Number.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @return ESN or null if error.
+ * @hide
+ */
+ public String getEsn(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getEsn(subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getEsn", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @return PRLVersion or null if error.
+ * @hide
+ */
+ public String getCdmaPrlVersion() {
+ return getCdmaPrlVersion(getSubId());
+ }
+
+ /**
+ * Return the Preferred Roaming List Version
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param subId the subscription ID that this request applies to.
+ * @return PRLVersion or null if error.
+ * @hide
+ */
+ public String getCdmaPrlVersion(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCdmaPrlVersion(subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getCdmaPrlVersion", e);
+ }
+ return null;
+ }
+
+ /**
+ * Get snapshot of Telephony histograms
+ * @return List of Telephony histograms
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public List<TelephonyHistogram> getTelephonyHistograms() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getTelephonyHistograms();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getTelephonyHistograms", e);
+ }
+ return null;
+ }
+
+ /**
+ * Set the allowed carrier list for slotIndex
+ * Require system privileges. In the future we may add this to carrier APIs.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
+ * <p>This method works only on devices with {@link
+ * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+ *
+ * @return The number of carriers set successfully. Should be length of
+ * carrierList on success; -1 if carrierList null or on error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.setAllowedCarriers(slotIndex, carriers);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+ }
+ return -1;
+ }
+
+ /**
+ * Get the allowed carrier list for slotIndex.
+ * Require system privileges. In the future we may add this to carrier APIs.
+ *
+ * <p>This method returns valid data on devices with {@link
+ * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+ *
+ * @return List of {@link android.telephony.CarrierIdentifier}; empty list
+ * means all carriers are allowed.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getAllowedCarriers(slotIndex);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+ }
+ return new ArrayList<CarrierIdentifier>(0);
+ }
+
+ /**
+ * Action set from carrier signalling broadcast receivers to enable/disable metered apns
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param enabled control enable or disable metered apns.
+ * @hide
+ */
+ public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.carrierActionSetMeteredApnsEnabled(subId, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e);
+ }
+ }
+
+ /**
+ * Action set from carrier signalling broadcast receivers to enable/disable radio
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param enabled control enable or disable radio.
+ * @hide
+ */
+ public void carrierActionSetRadioEnabled(int subId, boolean enabled) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.carrierActionSetRadioEnabled(subId, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#carrierActionSetRadioEnabled", e);
+ }
+ }
+
+ /**
+ * Action set from carrier signalling broadcast receivers to start/stop reporting default
+ * network available events
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * @param subId the subscription ID that this action applies to.
+ * @param report control start/stop reporting network status.
+ * @hide
+ */
+ public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.carrierActionReportDefaultNetworkStatus(subId, report);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#carrierActionReportDefaultNetworkStatus", e);
+ }
+ }
+
+ /**
+ * Get aggregated video call data usage since boot.
+ * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
+ *
+ * @param how one of the NetworkStats.STATS_PER_* constants depending on whether the request is
+ * for data usage per uid or overall usage.
+ * @return Snapshot of video call data usage
+ * @hide
+ */
+ public NetworkStats getVtDataUsage(int how) {
+ boolean perUidStats = (how == NetworkStats.STATS_PER_UID);
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getVtDataUsage(getSubId(), perUidStats);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getVtDataUsage", e);
+ }
+ return null;
+ }
+
+ /**
+ * Policy control of data connection. Usually used when data limit is passed.
+ * @param enabled True if enabling the data, otherwise disabling.
+ * @param subId sub id
+ * @hide
+ */
+ public void setPolicyDataEnabled(boolean enabled, int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.setPolicyDataEnabled(enabled, subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setPolicyDataEnabled", e);
+ }
+ }
+
+ /**
+ * Get Client request stats which will contain statistical information
+ * on each request made by client.
+ * Callers require either READ_PRIVILEGED_PHONE_STATE or
+ * READ_PHONE_STATE to retrieve the information.
+ * @param subId sub id
+ * @return List of Client Request Stats
+ * @hide
+ */
+ public List<ClientRequestStats> getClientRequestStats(int subId) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getClientRequestStats(getOpPackageName(), subId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Check if phone is in emergency callback mode
+ * @return true if phone is in emergency callback mode
+ * @hide
+ */
+ public boolean getEmergencyCallbackMode() {
+ return getEmergencyCallbackMode(getSubId());
+ }
+
+ /**
+ * Check if phone is in emergency callback mode
+ * @return true if phone is in emergency callback mode
+ * @param subId the subscription ID that this action applies to.
+ * @hide
+ */
+ public boolean getEmergencyCallbackMode(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return false;
+ }
+ return telephony.getEmergencyCallbackMode(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getEmergencyCallbackMode", e);
+ }
+ return false;
+ }
+
+ /**
+ * Get the most recently available signal strength information.
+ *
+ * Get the most recent SignalStrength information reported by the modem. Due
+ * to power saving this information may not always be current.
+ * @return the most recent cached signal strength info from the modem
+ */
+ @Nullable
+ public SignalStrength getSignalStrength() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getSignalStrength(getSubId());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#getSignalStrength", e);
+ }
+ return null;
+ }
+}
diff --git a/android/telephony/TelephonyScanManager.java b/android/telephony/TelephonyScanManager.java
new file mode 100644
index 00000000..92a21b6f
--- /dev/null
+++ b/android/telephony/TelephonyScanManager.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.SparseArray;
+import java.util.Arrays;
+import java.util.List;
+
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * Manages the radio access network scan requests and callbacks.
+ * @hide
+ */
+public final class TelephonyScanManager {
+
+ private static final String TAG = "TelephonyScanManager";
+
+ /** @hide */
+ public static final String SCAN_RESULT_KEY = "scanResult";
+
+ /** @hide */
+ public static final int CALLBACK_SCAN_RESULTS = 1;
+ /** @hide */
+ public static final int CALLBACK_SCAN_ERROR = 2;
+ /** @hide */
+ public static final int CALLBACK_SCAN_COMPLETE = 3;
+
+ /**
+ * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
+ * implement and provide this callback so that the scan results or errors can be returned.
+ */
+ public static abstract class NetworkScanCallback {
+ /** Returns the scan results to the user, this callback will be called multiple times. */
+ public void onResults(List<CellInfo> results) {}
+
+ /**
+ * Informs the user that the scan has stopped.
+ *
+ * This callback will be called when the scan is finished or cancelled by the user.
+ * The related NetworkScanRequest will be deleted after this callback.
+ */
+ public void onComplete() {}
+
+ /**
+ * Informs the user that there is some error about the scan.
+ *
+ * This callback will be called whenever there is any error about the scan, but the scan
+ * won't stop unless the onComplete() callback is called.
+ */
+ public void onError(int error) {}
+ }
+
+ private static class NetworkScanInfo {
+ private final NetworkScanRequest mRequest;
+ private final NetworkScanCallback mCallback;
+
+ NetworkScanInfo(NetworkScanRequest request, NetworkScanCallback callback) {
+ mRequest = request;
+ mCallback = callback;
+ }
+ }
+
+ private final Looper mLooper;
+ private final Messenger mMessenger;
+ private SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
+
+ public TelephonyScanManager() {
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mLooper = thread.getLooper();
+ mMessenger = new Messenger(new Handler(mLooper) {
+ @Override
+ public void handleMessage(Message message) {
+ checkNotNull(message, "message cannot be null");
+ NetworkScanInfo nsi;
+ synchronized (mScanInfo) {
+ nsi = mScanInfo.get(message.arg2);
+ }
+ if (nsi == null) {
+ throw new RuntimeException(
+ "Failed to find NetworkScanInfo with id " + message.arg2);
+ }
+ NetworkScanCallback callback = nsi.mCallback;
+ if (callback == null) {
+ throw new RuntimeException(
+ "Failed to find NetworkScanCallback with id " + message.arg2);
+ }
+
+ switch (message.what) {
+ case CALLBACK_SCAN_RESULTS:
+ try {
+ final Bundle b = message.getData();
+ final Parcelable[] parcelables = b.getParcelableArray(SCAN_RESULT_KEY);
+ CellInfo[] ci = new CellInfo[parcelables.length];
+ for (int i = 0; i < parcelables.length; i++) {
+ ci[i] = (CellInfo) parcelables[i];
+ }
+ callback.onResults((List<CellInfo>) Arrays.asList(ci));
+ } catch (Exception e) {
+ Rlog.e(TAG, "Exception in networkscan callback onResults", e);
+ }
+ break;
+ case CALLBACK_SCAN_ERROR:
+ try {
+ callback.onError(message.arg1);
+ } catch (Exception e) {
+ Rlog.e(TAG, "Exception in networkscan callback onError", e);
+ }
+ break;
+ case CALLBACK_SCAN_COMPLETE:
+ try {
+ callback.onComplete();
+ mScanInfo.remove(message.arg2);
+ } catch (Exception e) {
+ Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
+ }
+ break;
+ default:
+ Rlog.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
+ break;
+ }
+ }
+ });
+ }
+
+ /**
+ * Request a network scan.
+ *
+ * This method is asynchronous, so the network scan results will be returned by callback.
+ * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param request Contains all the RAT with bands/channels that need to be scanned.
+ * @param callback Returns network scan results or errors.
+ * @return A NetworkScan obj which contains a callback which can stop the scan.
+ * @hide
+ */
+ public NetworkScan requestNetworkScan(int subId,
+ NetworkScanRequest request, NetworkScanCallback callback) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+ saveScanInfo(scanId, request, callback);
+ return new NetworkScan(scanId, subId);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "requestNetworkScan NPE", ex);
+ }
+ return null;
+ }
+
+ private void saveScanInfo(int id, NetworkScanRequest request, NetworkScanCallback callback) {
+ synchronized (mScanInfo) {
+ mScanInfo.put(id, new NetworkScanInfo(request, callback));
+ }
+ }
+
+ private ITelephony getITelephony() {
+ return ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ }
+}
diff --git a/android/telephony/UiccAccessRule.java b/android/telephony/UiccAccessRule.java
new file mode 100644
index 00000000..e42a7583
--- /dev/null
+++ b/android/telephony/UiccAccessRule.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control
+ * specification.
+ *
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+public final class UiccAccessRule implements Parcelable {
+ private static final String TAG = "UiccAccessRule";
+
+ private static final int ENCODING_VERSION = 1;
+
+ public static final Creator<UiccAccessRule> CREATOR = new Creator<UiccAccessRule>() {
+ @Override
+ public UiccAccessRule createFromParcel(Parcel in) {
+ return new UiccAccessRule(in);
+ }
+
+ @Override
+ public UiccAccessRule[] newArray(int size) {
+ return new UiccAccessRule[size];
+ }
+ };
+
+ /**
+ * Encode these access rules as a byte array which can be parsed with {@link #decodeRules}.
+ * @hide
+ */
+ @Nullable
+ public static byte[] encodeRules(@Nullable UiccAccessRule[] accessRules) {
+ if (accessRules == null) {
+ return null;
+ }
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream output = new DataOutputStream(baos);
+ output.writeInt(ENCODING_VERSION);
+ output.writeInt(accessRules.length);
+ for (UiccAccessRule accessRule : accessRules) {
+ output.writeInt(accessRule.mCertificateHash.length);
+ output.write(accessRule.mCertificateHash);
+ if (accessRule.mPackageName != null) {
+ output.writeBoolean(true);
+ output.writeUTF(accessRule.mPackageName);
+ } else {
+ output.writeBoolean(false);
+ }
+ output.writeLong(accessRule.mAccessType);
+ }
+ output.close();
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new IllegalStateException(
+ "ByteArrayOutputStream should never lead to an IOException", e);
+ }
+ }
+
+ /**
+ * Decodes a byte array generated with {@link #encodeRules}.
+ * @hide
+ */
+ @Nullable
+ public static UiccAccessRule[] decodeRules(@Nullable byte[] encodedRules) {
+ if (encodedRules == null) {
+ return null;
+ }
+ ByteArrayInputStream bais = new ByteArrayInputStream(encodedRules);
+ try (DataInputStream input = new DataInputStream(bais)) {
+ input.readInt(); // version; currently ignored
+ int count = input.readInt();
+ UiccAccessRule[] accessRules = new UiccAccessRule[count];
+ for (int i = 0; i < count; i++) {
+ int certificateHashLength = input.readInt();
+ byte[] certificateHash = new byte[certificateHashLength];
+ input.readFully(certificateHash);
+ String packageName = input.readBoolean() ? input.readUTF() : null;
+ long accessType = input.readLong();
+ accessRules[i] = new UiccAccessRule(certificateHash, packageName, accessType);
+ }
+ input.close();
+ return accessRules;
+ } catch (IOException e) {
+ throw new IllegalStateException(
+ "ByteArrayInputStream should never lead to an IOException", e);
+ }
+ }
+
+ private final byte[] mCertificateHash;
+ private final @Nullable String mPackageName;
+ // This bit is not currently used, but reserved for future use.
+ private final long mAccessType;
+
+ public UiccAccessRule(byte[] certificateHash, @Nullable String packageName, long accessType) {
+ this.mCertificateHash = certificateHash;
+ this.mPackageName = packageName;
+ this.mAccessType = accessType;
+ }
+
+ UiccAccessRule(Parcel in) {
+ mCertificateHash = in.createByteArray();
+ mPackageName = in.readString();
+ mAccessType = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mCertificateHash);
+ dest.writeString(mPackageName);
+ dest.writeLong(mAccessType);
+ }
+
+ /**
+ * Return the package name this rule applies to.
+ *
+ * @return the package name, or null if this rule applies to any package signed with the given
+ * certificate.
+ */
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Returns the carrier privilege status associated with the given package.
+ *
+ * @param packageInfo package info fetched from
+ * {@link android.content.pm.PackageManager#getPackageInfo}.
+ * {@link android.content.pm.PackageManager#GET_SIGNATURES} must have been passed in.
+ * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
+ * {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
+ */
+ public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
+ if (packageInfo.signatures == null || packageInfo.signatures.length == 0) {
+ throw new IllegalArgumentException(
+ "Must use GET_SIGNATURES when looking up package info");
+ }
+
+ for (Signature sig : packageInfo.signatures) {
+ int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName);
+ if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
+ return accessStatus;
+ }
+ }
+
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+
+ /**
+ * Returns the carrier privilege status for the given certificate and package name.
+ *
+ * @param signature The signature of the certificate.
+ * @param packageName name of the package.
+ * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
+ * {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
+ */
+ public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
+ // SHA-1 is for backward compatible support only, strongly discouraged for new use.
+ byte[] certHash = getCertHash(signature, "SHA-1");
+ byte[] certHash256 = getCertHash(signature, "SHA-256");
+ if (matches(certHash, packageName) || matches(certHash256, packageName)) {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
+
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+
+ private boolean matches(byte[] certHash, String packageName) {
+ return certHash != null && Arrays.equals(this.mCertificateHash, certHash) &&
+ (TextUtils.isEmpty(this.mPackageName) || this.mPackageName.equals(packageName));
+ }
+
+ @Override
+ public String toString() {
+ return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
+ mPackageName + " access: " + mAccessType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Converts a Signature into a Certificate hash usable for comparison.
+ */
+ private static byte[] getCertHash(Signature signature, String algo) {
+ try {
+ MessageDigest md = MessageDigest.getInstance(algo);
+ return md.digest(signature.toByteArray());
+ } catch (NoSuchAlgorithmException ex) {
+ Rlog.e(TAG, "NoSuchAlgorithmException: " + ex);
+ }
+ return null;
+ }
+}
diff --git a/android/telephony/UssdResponse.java b/android/telephony/UssdResponse.java
new file mode 100644
index 00000000..5df681d4
--- /dev/null
+++ b/android/telephony/UssdResponse.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Represents the Ussd response, including
+ * the message and the return code.
+ * @hide
+ */
+public final class UssdResponse implements Parcelable {
+ private CharSequence mReturnMessage;
+ private String mUssdRequest;
+
+
+ /**
+ * Implement the Parcelable interface
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mUssdRequest);
+ TextUtils.writeToParcel(mReturnMessage, dest, 0);
+ }
+
+ public String getUssdRequest() {
+ return mUssdRequest;
+ }
+
+ public CharSequence getReturnMessage() {
+ return mReturnMessage;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * * Initialize the object from the request and return message.
+ */
+ public UssdResponse(String ussdRequest, CharSequence returnMessage) {
+ mUssdRequest = ussdRequest;
+ mReturnMessage = returnMessage;
+ }
+
+ public static final Parcelable.Creator<UssdResponse> CREATOR = new Creator<UssdResponse>() {
+
+ @Override
+ public UssdResponse createFromParcel(Parcel in) {
+ String request = in.readString();
+ CharSequence message = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ return new UssdResponse(request, message);
+ }
+
+ @Override
+ public UssdResponse[] newArray(int size) {
+ return new UssdResponse[size];
+ }
+ };
+}
diff --git a/android/telephony/VisualVoicemailService.java b/android/telephony/VisualVoicemailService.java
new file mode 100644
index 00000000..fe30eb7b
--- /dev/null
+++ b/android/telephony/VisualVoicemailService.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.MainThread;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.Log;
+
+/**
+ * This service is implemented by dialer apps that wishes to handle OMTP or similar visual
+ * voicemails. Telephony binds to this service when the cell service is first connected, a visual
+ * voicemail SMS has been received, or when a SIM has been removed. Telephony will only bind to the
+ * default dialer for such events (See {@link TelecomManager#getDefaultDialerPackage()}). The
+ * {@link android.service.carrier.CarrierMessagingService} precedes the VisualVoicemailService in
+ * the SMS filtering chain and may intercept the visual voicemail SMS before it reaches this
+ * service.
+ * <p>
+ * To extend this class, The service must be declared in the manifest file with
+ * the {@link android.Manifest.permission#BIND_VISUAL_VOICEMAIL_SERVICE} permission and include an
+ * intent filter with the {@link #SERVICE_INTERFACE} action.
+ * <p>
+ * Below is an example manifest registration for a {@code VisualVoicemailService}.
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourVisualVoicemailServiceImplementation"
+ * android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telephony.VisualVoicemailService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ */
+public abstract class VisualVoicemailService extends Service {
+
+ private static final String TAG = "VvmService";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telephony.VisualVoicemailService";
+
+ /**
+ * @hide
+ */
+ public static final int MSG_ON_CELL_SERVICE_CONNECTED = 1;
+ /**
+ * @hide
+ */
+ public static final int MSG_ON_SMS_RECEIVED = 2;
+ /**
+ * @hide
+ */
+ public static final int MSG_ON_SIM_REMOVED = 3;
+ /**
+ * @hide
+ */
+ public static final int MSG_TASK_ENDED = 4;
+ /**
+ * @hide
+ */
+ public static final int MSG_TASK_STOPPED = 5;
+
+ /**
+ * @hide
+ */
+ public static final String DATA_PHONE_ACCOUNT_HANDLE = "data_phone_account_handle";
+ /**
+ * @hide
+ */
+ public static final String DATA_SMS = "data_sms";
+
+ /**
+ * Represents a visual voicemail event which needs to be handled. While the task is being
+ * processed telephony will hold a wakelock for the VisualVoicemailService. The service can
+ * unblock the main thread and pass the task to a worker thread. Once the task is finished,
+ * {@link VisualVoicemailTask#finish()} should be called to signal telephony to release the
+ * resources. Telephony will call {@link VisualVoicemailService#onStopped(VisualVoicemailTask)}
+ * when the task is going to be terminated before completion.
+ *
+ * @see #onCellServiceConnected(VisualVoicemailTask, PhoneAccountHandle)
+ * @see #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)
+ * @see #onSimRemoved(VisualVoicemailTask, PhoneAccountHandle)
+ * @see #onStopped(VisualVoicemailTask)
+ */
+ public static class VisualVoicemailTask {
+
+ private final int mTaskId;
+ private final Messenger mReplyTo;
+
+ private VisualVoicemailTask(Messenger replyTo, int taskId) {
+ mTaskId = taskId;
+ mReplyTo = replyTo;
+ }
+
+ /**
+ * Call to signal telephony the task has completed. Must be called for every task.
+ */
+ public final void finish() {
+ Message message = Message.obtain();
+ try {
+ message.what = MSG_TASK_ENDED;
+ message.arg1 = mTaskId;
+ mReplyTo.send(message);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "Cannot send MSG_TASK_ENDED, remote handler no longer exist");
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof VisualVoicemailTask)) {
+ return false;
+ }
+ return mTaskId == ((VisualVoicemailTask) obj).mTaskId;
+ }
+
+ @Override
+ public int hashCode() {
+ return mTaskId;
+ }
+ }
+
+ /**
+ * Handles messages sent by telephony.
+ */
+ private final Messenger mMessenger = new Messenger(new Handler() {
+ @Override
+ public void handleMessage(final Message msg) {
+ final PhoneAccountHandle handle = msg.getData()
+ .getParcelable(DATA_PHONE_ACCOUNT_HANDLE);
+ VisualVoicemailTask task = new VisualVoicemailTask(msg.replyTo, msg.arg1);
+ switch (msg.what) {
+ case MSG_ON_CELL_SERVICE_CONNECTED:
+ onCellServiceConnected(task, handle);
+ break;
+ case MSG_ON_SMS_RECEIVED:
+ VisualVoicemailSms sms = msg.getData().getParcelable(DATA_SMS);
+ onSmsReceived(task, sms);
+ break;
+ case MSG_ON_SIM_REMOVED:
+ onSimRemoved(task, handle);
+ break;
+ case MSG_TASK_STOPPED:
+ onStopped(task);
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+ });
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ /**
+ * Called when the cellular service is connected on a {@link PhoneAccountHandle} for the first
+ * time, or when the carrier config has changed. It will not be called when the signal is lost
+ * then restored.
+ *
+ * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
+ * called when the task is completed.
+ * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
+ */
+ @MainThread
+ public abstract void onCellServiceConnected(VisualVoicemailTask task,
+ PhoneAccountHandle phoneAccountHandle);
+
+ /**
+ * Called when a SMS matching the {@link VisualVoicemailSmsFilterSettings} set by
+ * {@link TelephonyManager#setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings)
+ * }
+ * is received.
+ *
+ * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
+ * called when the task is completed.
+ * @param sms The content of the received SMS.
+ */
+ @MainThread
+ public abstract void onSmsReceived(VisualVoicemailTask task,
+ VisualVoicemailSms sms);
+
+ /**
+ * Called when a SIM is removed.
+ *
+ * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
+ * called when the task is completed.
+ * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
+ */
+ @MainThread
+ public abstract void onSimRemoved(VisualVoicemailTask task,
+ PhoneAccountHandle phoneAccountHandle);
+
+ /**
+ * Called before the system is about to terminate a task. The service should persist any
+ * necessary data and call finish on the task immediately.
+ */
+ @MainThread
+ public abstract void onStopped(VisualVoicemailTask task);
+
+ /**
+ * Set the visual voicemail SMS filter settings for the VisualVoicemailService.
+ * {@link #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be called when
+ * a SMS matching the settings is received. The caller should have
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} and implements a
+ * VisualVoicemailService.
+ * <p>
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ *
+ * @param phoneAccountHandle The account to apply the settings to.
+ * @param settings The settings for the filter, or {@code null} to disable the filter.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final void setSmsFilterSettings(Context context,
+ PhoneAccountHandle phoneAccountHandle,
+ VisualVoicemailSmsFilterSettings settings) {
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ int subId = getSubId(context, phoneAccountHandle);
+ if (settings == null) {
+ telephonyManager.disableVisualVoicemailSmsFilter(subId);
+ } else {
+ telephonyManager.enableVisualVoicemailSmsFilter(subId, settings);
+ }
+ }
+
+ /**
+ * Send a visual voicemail SMS. The caller must be the current default dialer.
+ * <p>
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SEND_SMS SEND_SMS}
+ *
+ * @param phoneAccountHandle The account to send the SMS with.
+ * @param number The destination number.
+ * @param port The destination port for data SMS, or 0 for text SMS.
+ * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
+ * @param sentIntent The sent intent passed to the {@link SmsManager}
+ *
+ * @throws SecurityException if the caller is not the current default dialer
+ *
+ * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
+ * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final void sendVisualVoicemailSms(Context context,
+ PhoneAccountHandle phoneAccountHandle, String number,
+ short port, String text, PendingIntent sentIntent) {
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ telephonyManager.sendVisualVoicemailSmsForSubscriber(getSubId(context, phoneAccountHandle),
+ number, port, text, sentIntent);
+ }
+
+ private static int getSubId(Context context, PhoneAccountHandle phoneAccountHandle) {
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ return telephonyManager
+ .getSubIdForPhoneAccount(telecomManager.getPhoneAccount(phoneAccountHandle));
+ }
+
+}
diff --git a/android/telephony/VisualVoicemailSms.java b/android/telephony/VisualVoicemailSms.java
new file mode 100644
index 00000000..1e6ea4bf
--- /dev/null
+++ b/android/telephony/VisualVoicemailSms.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+
+/**
+ * Represents the content of a visual voicemail SMS. If a incoming SMS matches the {@link
+ * VisualVoicemailSmsFilterSettings} set by the default dialer, {@link
+ * VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be called.
+ */
+public final class VisualVoicemailSms implements Parcelable {
+
+ private final PhoneAccountHandle mPhoneAccountHandle;
+ @Nullable
+ private final String mPrefix;
+
+ @Nullable
+ private final Bundle mFields;
+
+ private final String mMessageBody;
+
+ VisualVoicemailSms(Builder builder) {
+ mPhoneAccountHandle = builder.mPhoneAccountHandle;
+ mPrefix = builder.mPrefix;
+ mFields = builder.mFields;
+ mMessageBody = builder.mMessageBody;
+ }
+
+ /**
+ * The {@link PhoneAccountHandle} that received the SMS.
+ */
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mPhoneAccountHandle;
+ }
+
+ /**
+ * The event type of the SMS or {@code null} if the framework cannot parse the SMS as voicemail
+ * but the carrier pattern indicates it is. Common values are "SYNC" or "STATUS".
+ */
+ public String getPrefix() {
+ return mPrefix;
+ }
+
+ /**
+ * The key-value pairs sent by the SMS, or {@code null} if the framework cannot parse the SMS as
+ * voicemail but the carrier pattern indicates it is. The interpretation of the fields is
+ * carrier dependent.
+ */
+ public Bundle getFields() {
+ return mFields;
+ }
+
+ /**
+ * Raw message body of the received SMS.
+ */
+ public String getMessageBody() {
+ return mMessageBody;
+ }
+
+ /**
+ * Builder for the {@link VisualVoicemailSms}. Internal use only.
+ *
+ * @hide
+ */
+ public static class Builder {
+
+ private PhoneAccountHandle mPhoneAccountHandle;
+ private String mPrefix;
+ private Bundle mFields;
+ private String mMessageBody;
+
+ public VisualVoicemailSms build() {
+ return new VisualVoicemailSms(this);
+ }
+
+ public Builder setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ this.mPhoneAccountHandle = phoneAccountHandle;
+ return this;
+ }
+
+ public Builder setPrefix(String prefix) {
+ this.mPrefix = prefix;
+ return this;
+ }
+
+ public Builder setFields(Bundle fields) {
+ this.mFields = fields;
+ return this;
+ }
+
+ public Builder setMessageBody(String messageBody) {
+ this.mMessageBody = messageBody;
+ return this;
+ }
+
+ }
+
+
+ public static final Creator<VisualVoicemailSms> CREATOR =
+ new Creator<VisualVoicemailSms>() {
+ @Override
+ public VisualVoicemailSms createFromParcel(Parcel in) {
+ return new Builder()
+ .setPhoneAccountHandle((PhoneAccountHandle) in.readParcelable(null))
+ .setPrefix(in.readString())
+ .setFields(in.readBundle())
+ .setMessageBody(in.readString())
+ .build();
+ }
+
+ @Override
+ public VisualVoicemailSms[] newArray(int size) {
+ return new VisualVoicemailSms[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(getPhoneAccountHandle(), flags);
+ dest.writeString(getPrefix());
+ dest.writeBundle(getFields());
+ dest.writeString(getMessageBody());
+ }
+}
diff --git a/android/telephony/VisualVoicemailSmsFilterSettings.java b/android/telephony/VisualVoicemailSmsFilterSettings.java
new file mode 100644
index 00000000..8ed96a3a
--- /dev/null
+++ b/android/telephony/VisualVoicemailSmsFilterSettings.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to represent various settings for the visual voicemail SMS filter. When the filter is
+ * enabled, incoming SMS matching the generalized OMTP format:
+ *
+ * <p>[clientPrefix]:[prefix]:([key]=[value];)*
+ *
+ * <p>will be regarded as a visual voicemail SMS, and removed before reaching the SMS provider. The
+ * {@link VisualVoicemailService} in the current default dialer will be bound and
+ * {@link VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)}
+ * will called with the information extracted from the SMS.
+ *
+ * <p>Use {@link android.telephony.VisualVoicemailSmsFilterSettings.Builder} to construct this
+ * class.
+ *
+ * @see TelephonyManager#setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings)
+ */
+public final class VisualVoicemailSmsFilterSettings implements Parcelable {
+
+
+ /**
+ * The visual voicemail SMS message does not have to be a data SMS, and can be directed to any
+ * port.
+ */
+ public static final int DESTINATION_PORT_ANY = -1;
+
+ /**
+ * The visual voicemail SMS message can be directed to any port, but must be a data SMS.
+ */
+ public static final int DESTINATION_PORT_DATA_SMS = -2;
+
+ /**
+ * @hide
+ */
+ public static final String DEFAULT_CLIENT_PREFIX = "//VVM";
+ /**
+ * @hide
+ */
+ public static final List<String> DEFAULT_ORIGINATING_NUMBERS = Collections.emptyList();
+ /**
+ * @hide
+ */
+ public static final int DEFAULT_DESTINATION_PORT = DESTINATION_PORT_ANY;
+
+ /**
+ * Builder class for {@link VisualVoicemailSmsFilterSettings} objects.
+ */
+ public static class Builder {
+
+ private String mClientPrefix = DEFAULT_CLIENT_PREFIX;
+ private List<String> mOriginatingNumbers = DEFAULT_ORIGINATING_NUMBERS;
+ private int mDestinationPort = DEFAULT_DESTINATION_PORT;
+
+ public VisualVoicemailSmsFilterSettings build() {
+ return new VisualVoicemailSmsFilterSettings(this);
+ }
+
+ /**
+ * Sets the client prefix for the visual voicemail SMS filter. The client prefix will appear
+ * at the start of a visual voicemail SMS message, followed by a colon(:).
+ */
+ public Builder setClientPrefix(String clientPrefix) {
+ if (clientPrefix == null) {
+ throw new IllegalArgumentException("Client prefix cannot be null");
+ }
+ mClientPrefix = clientPrefix;
+ return this;
+ }
+
+ /**
+ * Sets the originating number whitelist for the visual voicemail SMS filter. If the list is
+ * not null only the SMS messages from a number in the list can be considered as a visual
+ * voicemail SMS. Otherwise, messages from any address will be considered.
+ */
+ public Builder setOriginatingNumbers(List<String> originatingNumbers) {
+ if (originatingNumbers == null) {
+ throw new IllegalArgumentException("Originating numbers cannot be null");
+ }
+ mOriginatingNumbers = originatingNumbers;
+ return this;
+ }
+
+ /**
+ * Sets the destination port for the visual voicemail SMS filter.
+ *
+ * @param destinationPort The destination port, or {@link #DESTINATION_PORT_ANY}, or {@link
+ * #DESTINATION_PORT_DATA_SMS}
+ */
+ public Builder setDestinationPort(int destinationPort) {
+ mDestinationPort = destinationPort;
+ return this;
+ }
+
+ }
+
+ /**
+ * The client prefix for the visual voicemail SMS filter. The client prefix will appear at the
+ * start of a visual voicemail SMS message, followed by a colon(:).
+ */
+ public final String clientPrefix;
+
+ /**
+ * The originating number whitelist for the visual voicemail SMS filter of a phone account. If
+ * the list is not null only the SMS messages from a number in the list can be considered as a
+ * visual voicemail SMS. Otherwise, messages from any address will be considered.
+ */
+ public final List<String> originatingNumbers;
+
+ /**
+ * The destination port for the visual voicemail SMS filter, or {@link #DESTINATION_PORT_ANY},
+ * or {@link #DESTINATION_PORT_DATA_SMS}
+ */
+ public final int destinationPort;
+
+ /**
+ * Use {@link Builder} to construct
+ */
+ private VisualVoicemailSmsFilterSettings(Builder builder) {
+ clientPrefix = builder.mClientPrefix;
+ originatingNumbers = builder.mOriginatingNumbers;
+ destinationPort = builder.mDestinationPort;
+ }
+
+ public static final Creator<VisualVoicemailSmsFilterSettings> CREATOR =
+ new Creator<VisualVoicemailSmsFilterSettings>() {
+ @Override
+ public VisualVoicemailSmsFilterSettings createFromParcel(Parcel in) {
+ Builder builder = new Builder();
+ builder.setClientPrefix(in.readString());
+ builder.setOriginatingNumbers(in.createStringArrayList());
+ builder.setDestinationPort(in.readInt());
+
+ return builder.build();
+ }
+
+ @Override
+ public VisualVoicemailSmsFilterSettings[] newArray(int size) {
+ return new VisualVoicemailSmsFilterSettings[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(clientPrefix);
+ dest.writeStringList(originatingNumbers);
+ dest.writeInt(destinationPort);
+ }
+
+ @Override
+ public String toString(){
+ return "[VisualVoicemailSmsFilterSettings "
+ + "clientPrefix=" + clientPrefix
+ + ", originatingNumbers=" + originatingNumbers
+ + ", destinationPort=" + destinationPort
+ + "]";
+ }
+
+}
diff --git a/android/telephony/VoLteServiceState.java b/android/telephony/VoLteServiceState.java
new file mode 100644
index 00000000..afef601b
--- /dev/null
+++ b/android/telephony/VoLteServiceState.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Contains LTE network state related information.
+ *
+ * @hide
+ */
+public final class VoLteServiceState implements Parcelable {
+
+ private static final String LOG_TAG = "VoLteServiceState";
+ private static final boolean DBG = false;
+
+ //Use int max, as -1 is a valid value in signal strength
+ public static final int INVALID = 0x7FFFFFFF;
+
+ public static final int NOT_SUPPORTED = 0;
+ public static final int SUPPORTED = 1;
+
+ // Single Radio Voice Call Continuity(SRVCC) progress state
+ public static final int HANDOVER_STARTED = 0;
+ public static final int HANDOVER_COMPLETED = 1;
+ public static final int HANDOVER_FAILED = 2;
+ public static final int HANDOVER_CANCELED = 3;
+
+ private int mSrvccState;
+
+ /**
+ * Create a new VoLteServiceState from a intent notifier Bundle
+ *
+ * This method is used by PhoneStateIntentReceiver and maybe by
+ * external applications.
+ *
+ * @param m Bundle from intent notifier
+ * @return newly created VoLteServiceState
+ *
+ * @hide
+ */
+ public static VoLteServiceState newFromBundle(Bundle m) {
+ VoLteServiceState ret;
+ ret = new VoLteServiceState();
+ ret.setFromNotifierBundle(m);
+ return ret;
+ }
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public VoLteServiceState() {
+ initialize();
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public VoLteServiceState(int srvccState) {
+ initialize();
+
+ mSrvccState = srvccState;
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source VoLteServiceState
+ *
+ * @hide
+ */
+ public VoLteServiceState(VoLteServiceState s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize values to defaults.
+ *
+ * @hide
+ */
+ private void initialize() {
+ mSrvccState = INVALID;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(VoLteServiceState s) {
+ mSrvccState = s.mSrvccState;
+ }
+
+ /**
+ * Construct a VoLteServiceState object from the given parcel.
+ *
+ * @hide
+ */
+ public VoLteServiceState(Parcel in) {
+ if (DBG) log("Size of VoLteServiceState parcel:" + in.dataSize());
+
+ mSrvccState = in.readInt();
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSrvccState);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable.Creator}
+ *
+ * @hide
+ */
+ public static final Parcelable.Creator<VoLteServiceState> CREATOR = new Parcelable.Creator() {
+ public VoLteServiceState createFromParcel(Parcel in) {
+ return new VoLteServiceState(in);
+ }
+
+ public VoLteServiceState[] newArray(int size) {
+ return new VoLteServiceState[size];
+ }
+ };
+
+ /**
+ * Validate the individual fields as per the range
+ * specified in ril.h
+ * Set to invalid any field that is not in the valid range
+ *
+ * @return
+ * Valid values for all fields
+ * @hide
+ */
+ public void validateInput() {
+ }
+
+ public int hashCode() {
+ int primeNum = 31;
+ return ((mSrvccState * primeNum));
+ }
+
+ /**
+ * @return true if the LTE network states are the same
+ */
+ @Override
+ public boolean equals (Object o) {
+ VoLteServiceState s;
+
+ try {
+ s = (VoLteServiceState) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (mSrvccState == s.mSrvccState);
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return ("VoLteServiceState:"
+ + " " + mSrvccState);
+ }
+
+ /**
+ * Set VoLteServiceState based on intent notifier map
+ *
+ * @param m intent notifier map
+ * @hide
+ */
+ private void setFromNotifierBundle(Bundle m) {
+ mSrvccState = m.getInt("mSrvccState");
+ }
+
+ /**
+ * Set intent notifier Bundle based on VoLteServiceState
+ *
+ * @param m intent notifier Bundle
+ * @hide
+ */
+ public void fillInNotifierBundle(Bundle m) {
+ m.putInt("mSrvccState", mSrvccState);
+ }
+
+ public int getSrvccState() {
+ return mSrvccState;
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/android/telephony/cdma/CdmaCellLocation.java b/android/telephony/cdma/CdmaCellLocation.java
new file mode 100644
index 00000000..7c10569f
--- /dev/null
+++ b/android/telephony/cdma/CdmaCellLocation.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cdma;
+
+import android.os.Bundle;
+import android.telephony.CellLocation;
+
+/**
+ * Represents the cell location on a CDMA phone.
+ */
+public class CdmaCellLocation extends CellLocation {
+ private int mBaseStationId = -1;
+
+ /**
+ * @hide
+ */
+ public final static int INVALID_LAT_LONG = Integer.MAX_VALUE;
+
+ /**
+ * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * It is represented in units of 0.25 seconds and ranges from -1296000
+ * to 1296000, both values inclusive (corresponding to a range of -90
+ * to +90 degrees). Integer.MAX_VALUE is considered invalid value.
+ */
+ private int mBaseStationLatitude = INVALID_LAT_LONG;
+
+ /**
+ * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * It is represented in units of 0.25 seconds and ranges from -2592000
+ * to 2592000, both values inclusive (corresponding to a range of -180
+ * to +180 degrees). Integer.MAX_VALUE is considered invalid value.
+ */
+ private int mBaseStationLongitude = INVALID_LAT_LONG;
+
+ private int mSystemId = -1;
+ private int mNetworkId = -1;
+
+ /**
+ * Empty constructor.
+ * Initializes the BID, SID, NID and base station latitude and longitude
+ * to invalid values.
+ */
+ public CdmaCellLocation() {
+ this.mBaseStationId = -1;
+ this.mBaseStationLatitude = INVALID_LAT_LONG;
+ this.mBaseStationLongitude = INVALID_LAT_LONG;
+ this.mSystemId = -1;
+ this.mNetworkId = -1;
+ }
+
+ /**
+ * Initialize the object from a bundle.
+ */
+ public CdmaCellLocation(Bundle bundle) {
+ this.mBaseStationId = bundle.getInt("baseStationId", mBaseStationId);
+ this.mBaseStationLatitude = bundle.getInt("baseStationLatitude", mBaseStationLatitude);
+ this.mBaseStationLongitude = bundle.getInt("baseStationLongitude", mBaseStationLongitude);
+ this.mSystemId = bundle.getInt("systemId", mSystemId);
+ this.mNetworkId = bundle.getInt("networkId", mNetworkId);
+ }
+
+ /**
+ * @return cdma base station identification number, -1 if unknown
+ */
+ public int getBaseStationId() {
+ return this.mBaseStationId;
+ }
+
+ /**
+ * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf)
+ * It is represented in units of 0.25 seconds and ranges from -1296000
+ * to 1296000, both values inclusive (corresponding to a range of -90
+ * to +90 degrees). Integer.MAX_VALUE is considered invalid value.
+ *
+ * @return cdma base station latitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown
+ */
+ public int getBaseStationLatitude() {
+ return this.mBaseStationLatitude;
+ }
+
+ /**
+ * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+ * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf)
+ * It is represented in units of 0.25 seconds and ranges from -2592000
+ * to 2592000, both values inclusive (corresponding to a range of -180
+ * to +180 degrees). Integer.MAX_VALUE is considered invalid value.
+ *
+ * @return cdma base station longitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown
+ */
+ public int getBaseStationLongitude() {
+ return this.mBaseStationLongitude;
+ }
+
+ /**
+ * @return cdma system identification number, -1 if unknown
+ */
+ public int getSystemId() {
+ return this.mSystemId;
+ }
+
+ /**
+ * @return cdma network identification number, -1 if unknown
+ */
+ public int getNetworkId() {
+ return this.mNetworkId;
+ }
+
+ /**
+ * Invalidate this object. The cell location data is set to invalid values.
+ */
+ @Override
+ public void setStateInvalid() {
+ this.mBaseStationId = -1;
+ this.mBaseStationLatitude = INVALID_LAT_LONG;
+ this.mBaseStationLongitude = INVALID_LAT_LONG;
+ this.mSystemId = -1;
+ this.mNetworkId = -1;
+ }
+
+ /**
+ * Set the cell location data.
+ */
+ public void setCellLocationData(int baseStationId, int baseStationLatitude,
+ int baseStationLongitude) {
+ // The following values have to be written in the correct sequence
+ this.mBaseStationId = baseStationId;
+ this.mBaseStationLatitude = baseStationLatitude; //values[2];
+ this.mBaseStationLongitude = baseStationLongitude; //values[3];
+ }
+
+ /**
+ * Set the cell location data.
+ */
+ public void setCellLocationData(int baseStationId, int baseStationLatitude,
+ int baseStationLongitude, int systemId, int networkId) {
+ // The following values have to be written in the correct sequence
+ this.mBaseStationId = baseStationId;
+ this.mBaseStationLatitude = baseStationLatitude; //values[2];
+ this.mBaseStationLongitude = baseStationLongitude; //values[3];
+ this.mSystemId = systemId;
+ this.mNetworkId = networkId;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.mBaseStationId ^ this.mBaseStationLatitude ^ this.mBaseStationLongitude
+ ^ this.mSystemId ^ this.mNetworkId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ CdmaCellLocation s;
+
+ try {
+ s = (CdmaCellLocation)o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return (equalsHandlesNulls(this.mBaseStationId, s.mBaseStationId) &&
+ equalsHandlesNulls(this.mBaseStationLatitude, s.mBaseStationLatitude) &&
+ equalsHandlesNulls(this.mBaseStationLongitude, s.mBaseStationLongitude) &&
+ equalsHandlesNulls(this.mSystemId, s.mSystemId) &&
+ equalsHandlesNulls(this.mNetworkId, s.mNetworkId)
+ );
+ }
+
+ @Override
+ public String toString() {
+ return "[" + this.mBaseStationId + ","
+ + this.mBaseStationLatitude + ","
+ + this.mBaseStationLongitude + ","
+ + this.mSystemId + ","
+ + this.mNetworkId + "]";
+ }
+
+ /**
+ * Test whether two objects hold the same data values or both are null
+ *
+ * @param a first obj
+ * @param b second obj
+ * @return true if two objects equal or both are null
+ */
+ private static boolean equalsHandlesNulls(Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals (b);
+ }
+
+ /**
+ * Fill the cell location data into the intent notifier Bundle based on service state
+ *
+ * @param bundleToFill intent notifier Bundle
+ */
+ public void fillInNotifierBundle(Bundle bundleToFill) {
+ bundleToFill.putInt("baseStationId", this.mBaseStationId);
+ bundleToFill.putInt("baseStationLatitude", this.mBaseStationLatitude);
+ bundleToFill.putInt("baseStationLongitude", this.mBaseStationLongitude);
+ bundleToFill.putInt("systemId", this.mSystemId);
+ bundleToFill.putInt("networkId", this.mNetworkId);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isEmpty() {
+ return (this.mBaseStationId == -1 &&
+ this.mBaseStationLatitude == INVALID_LAT_LONG &&
+ this.mBaseStationLongitude == INVALID_LAT_LONG &&
+ this.mSystemId == -1 &&
+ this.mNetworkId == -1);
+ }
+
+ /**
+ * Converts latitude or longitude from 0.25 seconds (as defined in the
+ * 3GPP2 C.S0005-A v6.0 standard) to decimal degrees
+ *
+ * @param quartSec latitude or longitude in 0.25 seconds units
+ * @return latitude or longitude in decimal degrees units
+ * @throws IllegalArgumentException if value is less than -2592000,
+ * greater than 2592000, or is not a number.
+ */
+ public static double convertQuartSecToDecDegrees(int quartSec) {
+ if(Double.isNaN(quartSec) || quartSec < -2592000 || quartSec > 2592000){
+ // Invalid value
+ throw new IllegalArgumentException("Invalid coordiante value:" + quartSec);
+ }
+ return ((double)quartSec) / (3600 * 4);
+ }
+
+}
+
+
diff --git a/android/telephony/cdma/CdmaSmsCbProgramData.java b/android/telephony/cdma/CdmaSmsCbProgramData.java
new file mode 100644
index 00000000..010ad2b0
--- /dev/null
+++ b/android/telephony/cdma/CdmaSmsCbProgramData.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cdma;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CDMA Service Category Program Data from SCPT teleservice SMS.
+ * The CellBroadcastReceiver app receives an Intent with action
+ * {@link android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION}
+ * containing an array of these objects to update its list of cell broadcast service categories
+ * to display.
+ *
+ * {@hide}
+ */
+public class CdmaSmsCbProgramData implements Parcelable {
+
+ /** Delete the specified service category from the list of enabled categories. */
+ public static final int OPERATION_DELETE_CATEGORY = 0;
+
+ /** Add the specified service category to the list of enabled categories. */
+ public static final int OPERATION_ADD_CATEGORY = 1;
+
+ /** Clear all service categories from the list of enabled categories. */
+ public static final int OPERATION_CLEAR_CATEGORIES = 2;
+
+ /** Alert option: no alert. */
+ public static final int ALERT_OPTION_NO_ALERT = 0;
+
+ /** Alert option: default alert. */
+ public static final int ALERT_OPTION_DEFAULT_ALERT = 1;
+
+ /** Alert option: vibrate alert once. */
+ public static final int ALERT_OPTION_VIBRATE_ONCE = 2;
+
+ /** Alert option: vibrate alert - repeat. */
+ public static final int ALERT_OPTION_VIBRATE_REPEAT = 3;
+
+ /** Alert option: visual alert once. */
+ public static final int ALERT_OPTION_VISUAL_ONCE = 4;
+
+ /** Alert option: visual alert - repeat. */
+ public static final int ALERT_OPTION_VISUAL_REPEAT = 5;
+
+ /** Alert option: low-priority alert once. */
+ public static final int ALERT_OPTION_LOW_PRIORITY_ONCE = 6;
+
+ /** Alert option: low-priority alert - repeat. */
+ public static final int ALERT_OPTION_LOW_PRIORITY_REPEAT = 7;
+
+ /** Alert option: medium-priority alert once. */
+ public static final int ALERT_OPTION_MED_PRIORITY_ONCE = 8;
+
+ /** Alert option: medium-priority alert - repeat. */
+ public static final int ALERT_OPTION_MED_PRIORITY_REPEAT = 9;
+
+ /** Alert option: high-priority alert once. */
+ public static final int ALERT_OPTION_HIGH_PRIORITY_ONCE = 10;
+
+ /** Alert option: high-priority alert - repeat. */
+ public static final int ALERT_OPTION_HIGH_PRIORITY_REPEAT = 11;
+
+ /** Service category operation (add/delete/clear). */
+ private final int mOperation;
+
+ /** Service category to modify. */
+ private final int mCategory;
+
+ /** Language used for service category name (defined in BearerData.LANGUAGE_*). */
+ private final int mLanguage;
+
+ /** Maximum number of messages to store for this service category. */
+ private final int mMaxMessages;
+
+ /** Service category alert option. */
+ private final int mAlertOption;
+
+ /** Name of service category. */
+ private final String mCategoryName;
+
+ /** Create a new CdmaSmsCbProgramData object with the specified values. */
+ public CdmaSmsCbProgramData(int operation, int category, int language, int maxMessages,
+ int alertOption, String categoryName) {
+ mOperation = operation;
+ mCategory = category;
+ mLanguage = language;
+ mMaxMessages = maxMessages;
+ mAlertOption = alertOption;
+ mCategoryName = categoryName;
+ }
+
+ /** Create a new CdmaSmsCbProgramData object from a Parcel. */
+ CdmaSmsCbProgramData(Parcel in) {
+ mOperation = in.readInt();
+ mCategory = in.readInt();
+ mLanguage = in.readInt();
+ mMaxMessages = in.readInt();
+ mAlertOption = in.readInt();
+ mCategoryName = in.readString();
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mOperation);
+ dest.writeInt(mCategory);
+ dest.writeInt(mLanguage);
+ dest.writeInt(mMaxMessages);
+ dest.writeInt(mAlertOption);
+ dest.writeString(mCategoryName);
+ }
+
+ /**
+ * Returns the service category operation, e.g. {@link #OPERATION_ADD_CATEGORY}.
+ * @return one of the {@code OPERATION_*} values
+ */
+ public int getOperation() {
+ return mOperation;
+ }
+
+ /**
+ * Returns the CDMA service category to modify.
+ * @return a 16-bit CDMA service category value
+ */
+ public int getCategory() {
+ return mCategory;
+ }
+
+ /**
+ * Returns the CDMA language code for this service category.
+ * @return one of the language values defined in BearerData.LANGUAGE_*
+ */
+ public int getLanguage() {
+ return mLanguage;
+ }
+
+ /**
+ * Returns the maximum number of messages to store for this service category.
+ * @return the maximum number of messages to store for this service category
+ */
+ public int getMaxMessages() {
+ return mMaxMessages;
+ }
+
+ /**
+ * Returns the service category alert option, e.g. {@link #ALERT_OPTION_DEFAULT_ALERT}.
+ * @return one of the {@code ALERT_OPTION_*} values
+ */
+ public int getAlertOption() {
+ return mAlertOption;
+ }
+
+ /**
+ * Returns the service category name, in the language specified by {@link #getLanguage()}.
+ * @return an optional service category name
+ */
+ public String getCategoryName() {
+ return mCategoryName;
+ }
+
+ @Override
+ public String toString() {
+ return "CdmaSmsCbProgramData{operation=" + mOperation + ", category=" + mCategory
+ + ", language=" + mLanguage + ", max messages=" + mMaxMessages
+ + ", alert option=" + mAlertOption + ", category name=" + mCategoryName + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Creator for unparcelling objects. */
+ public static final Parcelable.Creator<CdmaSmsCbProgramData>
+ CREATOR = new Parcelable.Creator<CdmaSmsCbProgramData>() {
+ @Override
+ public CdmaSmsCbProgramData createFromParcel(Parcel in) {
+ return new CdmaSmsCbProgramData(in);
+ }
+
+ @Override
+ public CdmaSmsCbProgramData[] newArray(int size) {
+ return new CdmaSmsCbProgramData[size];
+ }
+ };
+}
diff --git a/android/telephony/cdma/CdmaSmsCbProgramResults.java b/android/telephony/cdma/CdmaSmsCbProgramResults.java
new file mode 100644
index 00000000..68bfa3ce
--- /dev/null
+++ b/android/telephony/cdma/CdmaSmsCbProgramResults.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cdma;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CDMA Service Category Program Results from SCPT teleservice SMS.
+ *
+ * {@hide}
+ */
+public class CdmaSmsCbProgramResults implements Parcelable {
+
+ /** Program result: success. */
+ public static final int RESULT_SUCCESS = 0;
+
+ /** Program result: memory limit exceeded. */
+ public static final int RESULT_MEMORY_LIMIT_EXCEEDED = 1;
+
+ /** Program result: limit exceeded. */
+ public static final int RESULT_CATEGORY_LIMIT_EXCEEDED = 2;
+
+ /** Program result: category already opted in. */
+ public static final int RESULT_CATEGORY_ALREADY_ADDED = 3;
+
+ /** Program result: category already opted in. */
+ public static final int RESULT_CATEGORY_ALREADY_DELETED = 4;
+
+ /** Program result: invalid MAX_MESSAGES. */
+ public static final int RESULT_INVALID_MAX_MESSAGES = 5;
+
+ /** Program result: invalid ALERT_OPTION. */
+ public static final int RESULT_INVALID_ALERT_OPTION = 6;
+
+ /** Program result: invalid service category name. */
+ public static final int RESULT_INVALID_CATEGORY_NAME = 7;
+
+ /** Program result: unspecified programming failure. */
+ public static final int RESULT_UNSPECIFIED_FAILURE = 8;
+
+ /** Service category to modify. */
+ private final int mCategory;
+
+ /** Language used for service category name (defined in BearerData.LANGUAGE_*). */
+ private final int mLanguage;
+
+ /** Result of service category programming for this category. */
+ private final int mCategoryResult;
+
+ /** Create a new CdmaSmsCbProgramResults object with the specified values. */
+ public CdmaSmsCbProgramResults(int category, int language, int categoryResult) {
+ mCategory = category;
+ mLanguage = language;
+ mCategoryResult = categoryResult;
+ }
+
+ /** Create a new CdmaSmsCbProgramResults object from a Parcel. */
+ CdmaSmsCbProgramResults(Parcel in) {
+ mCategory = in.readInt();
+ mLanguage = in.readInt();
+ mCategoryResult = in.readInt();
+ }
+
+ /**
+ * Flatten this object into a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written (ignored).
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mCategory);
+ dest.writeInt(mLanguage);
+ dest.writeInt(mCategoryResult);
+ }
+
+ /**
+ * Returns the CDMA service category to modify.
+ * @return a 16-bit CDMA service category value
+ */
+ public int getCategory() {
+ return mCategory;
+ }
+
+ /**
+ * Returns the CDMA language code for this service category.
+ * @return one of the language values defined in BearerData.LANGUAGE_*
+ */
+ public int getLanguage() {
+ return mLanguage;
+ }
+
+ /**
+ * Returns the result of service programming for this category
+ * @return the result of service programming for this category
+ */
+ public int getCategoryResult() {
+ return mCategoryResult;
+ }
+
+ @Override
+ public String toString() {
+ return "CdmaSmsCbProgramResults{category=" + mCategory
+ + ", language=" + mLanguage + ", result=" + mCategoryResult + '}';
+ }
+
+ /**
+ * Describe the kinds of special objects contained in the marshalled representation.
+ * @return a bitmask indicating this Parcelable contains no special objects
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Creator for unparcelling objects. */
+ public static final Parcelable.Creator<CdmaSmsCbProgramResults>
+ CREATOR = new Parcelable.Creator<CdmaSmsCbProgramResults>() {
+ @Override
+ public CdmaSmsCbProgramResults createFromParcel(Parcel in) {
+ return new CdmaSmsCbProgramResults(in);
+ }
+
+ @Override
+ public CdmaSmsCbProgramResults[] newArray(int size) {
+ return new CdmaSmsCbProgramResults[size];
+ }
+ };
+}
diff --git a/android/telephony/euicc/DownloadableSubscription.java b/android/telephony/euicc/DownloadableSubscription.java
new file mode 100644
index 00000000..b5484e34
--- /dev/null
+++ b/android/telephony/euicc/DownloadableSubscription.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.UiccAccessRule;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Information about a subscription which is available for download.
+ *
+ * TODO(b/35851809): Make this public.
+ * @hide
+ */
+public final class DownloadableSubscription implements Parcelable {
+
+ public static final Creator<DownloadableSubscription> CREATOR =
+ new Creator<DownloadableSubscription>() {
+ @Override
+ public DownloadableSubscription createFromParcel(Parcel in) {
+ return new DownloadableSubscription(in);
+ }
+
+ @Override
+ public DownloadableSubscription[] newArray(int size) {
+ return new DownloadableSubscription[size];
+ }
+ };
+
+ /**
+ * Activation code. May be null for subscriptions which are not based on activation codes, e.g.
+ * to download a default subscription assigned to this device.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ @Nullable
+ public final String encodedActivationCode;
+
+ // see getCarrierName and setCarrierName
+ @Nullable
+ private String carrierName;
+ // see getAccessRules and setAccessRules
+ private UiccAccessRule[] accessRules;
+
+ /** @hide */
+ private DownloadableSubscription(String encodedActivationCode) {
+ this.encodedActivationCode = encodedActivationCode;
+ }
+
+ private DownloadableSubscription(Parcel in) {
+ encodedActivationCode = in.readString();
+ carrierName = in.readString();
+ accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
+ }
+
+ /**
+ * Create a DownloadableSubscription for the given activation code.
+ *
+ * @param encodedActivationCode the activation code to use. Must not be null.
+ * @return the {@link DownloadableSubscription} which may be passed to
+ * {@link EuiccManager#downloadSubscription}.
+ */
+ public static DownloadableSubscription forActivationCode(String encodedActivationCode) {
+ Preconditions.checkNotNull(encodedActivationCode, "Activation code may not be null");
+ return new DownloadableSubscription(encodedActivationCode);
+ }
+
+ /**
+ * Set the user-visible carrier name.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public void setCarrierName(String carrierName) {
+ this.carrierName = carrierName;
+ }
+
+ /**
+ * Returns the user-visible carrier name.
+ *
+ * <p>Only present for downloadable subscriptions that were queried from a server (as opposed to
+ * those created with {@link #forActivationCode}). May be populated with
+ * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ @Nullable
+ public String getCarrierName() {
+ return carrierName;
+ }
+
+ /**
+ * Returns the {@link UiccAccessRule}s dictating access to this subscription.
+ *
+ * <p>Only present for downloadable subscriptions that were queried from a server (as opposed to
+ * those created with {@link #forActivationCode}). May be populated with
+ * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public UiccAccessRule[] getAccessRules() {
+ return accessRules;
+ }
+
+ /**
+ * Set the {@link UiccAccessRule}s dictating access to this subscription.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public void setAccessRules(UiccAccessRule[] accessRules) {
+ this.accessRules = accessRules;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(encodedActivationCode);
+ dest.writeString(carrierName);
+ dest.writeTypedArray(accessRules, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/android/telephony/euicc/EuiccInfo.java b/android/telephony/euicc/EuiccInfo.java
new file mode 100644
index 00000000..5bfff08f
--- /dev/null
+++ b/android/telephony/euicc/EuiccInfo.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about an eUICC chip/device.
+ *
+ * @see EuiccManager#getEuiccInfo
+ * @hide
+ *
+ * TODO(b/35851809): Make this public.
+ */
+// WARNING: Do not add any privacy-sensitive fields to this class (such as an eUICC identifier)!
+// This API is accessible to all applications. Privacy-sensitive fields should be returned in their
+// own APIs guarded with appropriate permission checks.
+public final class EuiccInfo implements Parcelable {
+
+ public static final Creator<EuiccInfo> CREATOR =
+ new Creator<EuiccInfo>() {
+ @Override
+ public EuiccInfo createFromParcel(Parcel in) {
+ return new EuiccInfo(in);
+ }
+
+ @Override
+ public EuiccInfo[] newArray(int size) {
+ return new EuiccInfo[size];
+ }
+ };
+
+ /**
+ * Version of the operating system running on the eUICC. This field is hardware-specific and is
+ * not guaranteed to match any particular format.
+ */
+ @Nullable
+ public final String osVersion;
+
+ public EuiccInfo(@Nullable String osVersion) {
+ this.osVersion = osVersion;
+ }
+
+ private EuiccInfo(Parcel in) {
+ osVersion = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(osVersion);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/android/telephony/euicc/EuiccManager.java b/android/telephony/euicc/EuiccManager.java
new file mode 100644
index 00000000..a13af5f4
--- /dev/null
+++ b/android/telephony/euicc/EuiccManager.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.euicc;
+
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.euicc.IEuiccController;
+
+/**
+ * EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
+ *
+ * <p>You do not instantiate this class directly; instead, you retrieve an instance through
+ * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}.
+ *
+ * <p>See {@link #isEnabled} before attempting to use these APIs.
+ *
+ * TODO(b/35851809): Make this public.
+ * @hide
+ */
+public class EuiccManager {
+
+ /**
+ * Intent action to launch the embedded SIM (eUICC) management settings screen.
+ *
+ * <p>This screen shows a list of embedded profiles and offers the user the ability to switch
+ * between them, download new profiles, and delete unused profiles.
+ *
+ * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
+ * {@link #isEnabled} is false.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
+ "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+
+ /**
+ * Intent action to provision an embedded subscription.
+ *
+ * <p>May be called during device provisioning to launch a screen to perform embedded SIM
+ * provisioning, e.g. if no physical SIM is present and the user elects to configure their
+ * embedded SIM.
+ *
+ * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
+ * {@link #isEnabled} is false or if the device is already provisioned.
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
+ "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+
+ /**
+ * Intent action to handle a resolvable error.
+ * @hide
+ */
+ public static final String ACTION_RESOLVE_ERROR =
+ "android.telephony.euicc.action.RESOLVE_ERROR";
+
+ /**
+ * Result code for an operation indicating that the operation succeeded.
+ */
+ public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
+
+ /**
+ * Result code for an operation indicating that the user must take some action before the
+ * operation can continue.
+ *
+ * @see #startResolutionActivity
+ */
+ public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1;
+
+ /**
+ * Result code for an operation indicating that an unresolvable error occurred.
+ *
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} will be populated with a detailed error
+ * code for logging/debugging purposes only.
+ */
+ public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2;
+
+ /**
+ * Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result
+ * code.
+ *
+ * <p>This code is an implementation detail of the embedded subscription manager and is only
+ * intended for logging or debugging purposes.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
+
+ /**
+ * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
+ * callbacks providing the downloadable subscription metadata.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
+
+ /**
+ * Key for an extra set on {@link #getDefaultDownloadableSubscriptionList} PendingIntent result
+ * callbacks providing the list of available downloadable subscriptions.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+
+ /**
+ * Key for an extra set on {@link PendingIntent} result callbacks providing the resolution
+ * pending intent for {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}s.
+ * @hide
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT";
+
+ /**
+ * Key for an extra set on the {@link #EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT} intent
+ * containing the EuiccService action to launch for resolution.
+ * @hide
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION";
+
+ /**
+ * Key for an extra set on the {@link #EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT} intent
+ * providing the callback to execute after resolution is completed.
+ * @hide
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT";
+
+ /**
+ * Key for an extra set on the {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} intent for
+ * whether the user choses to use eUICC to set up network in SUW.
+ * @hide
+ */
+ public static final String EXTRA_FORCE_PROVISION =
+ "android.telephony.euicc.extra.FORCE_PROVISION";
+
+ /**
+ * Optional meta-data attribute for a carrier app providing an icon to use to represent the
+ * carrier. If not provided, the app's launcher icon will be used as a fallback.
+ */
+ public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+
+ private final Context mContext;
+ private final IEuiccController mController;
+
+ /** @hide */
+ public EuiccManager(Context context) {
+ mContext = context;
+ mController = IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
+ }
+
+ /**
+ * Whether embedded subscriptions are currently enabled.
+ *
+ * <p>Even on devices with the {@link PackageManager#FEATURE_TELEPHONY_EUICC} feature, embedded
+ * subscriptions may be turned off, e.g. because of a carrier restriction from an inserted
+ * physical SIM. Therefore, this runtime check should be used before accessing embedded
+ * subscription APIs.
+ *
+ * @return true if embedded subscriptions are currently enabled.
+ */
+ public boolean isEnabled() {
+ // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
+ // restrictions.
+ return mController != null;
+ }
+
+ /**
+ * Returns the EID identifying the eUICC hardware.
+ *
+ * <p>Requires that the calling app has carrier privileges on the active subscription on the
+ * eUICC.
+ *
+ * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready.
+ */
+ @Nullable
+ public String getEid() {
+ if (!isEnabled()) {
+ return null;
+ }
+ try {
+ return mController.getEid();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attempt to download the given {@link DownloadableSubscription}.
+ *
+ * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+ * or the calling app must be authorized to manage both the currently-active subscription and
+ * the subscription to be downloaded according to the subscription metadata. Without the former,
+ * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+ * intent to prompt the user to accept the download.
+ *
+ * @param subscription the subscription to download.
+ * @param switchAfterDownload if true, the profile will be activated upon successful download.
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ */
+ public void downloadSubscription(DownloadableSubscription subscription,
+ boolean switchAfterDownload, PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.downloadSubscription(subscription, switchAfterDownload,
+ mContext.getOpPackageName(), callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Start an activity to resolve a user-resolvable error.
+ *
+ * <p>If an operation returns {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}, this
+ * method may be called to prompt the user to resolve the issue.
+ *
+ * <p>This method may only be called once for a particular error.
+ *
+ * @param activity the calling activity (which should be in the foreground).
+ * @param requestCode an application-specific request code which will be provided to
+ * {@link Activity#onActivityResult} upon completion. Note that the operation may still be
+ * in progress when the resolution activity completes; it is not fully finished until the
+ * callback intent is triggered.
+ * @param resultIntent the Intent provided to the initial callback intent which failed with
+ * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}.
+ * @param callbackIntent a PendingIntent to launch when the operation completes. This is
+ * trigered upon completion of the original operation that required user resolution.
+ * @throws android.content.IntentSender.SendIntentException if called more than once.
+ */
+ public void startResolutionActivity(Activity activity, int requestCode, Intent resultIntent,
+ PendingIntent callbackIntent) throws IntentSender.SendIntentException {
+ PendingIntent resolutionIntent =
+ resultIntent.getParcelableExtra(EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT);
+ if (resolutionIntent == null) {
+ throw new IllegalArgumentException("Invalid result intent");
+ }
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT,
+ callbackIntent);
+ activity.startIntentSenderForResult(resolutionIntent.getIntentSender(), requestCode,
+ fillInIntent, 0 /* flagsMask */, 0 /* flagsValues */, 0 /* extraFlags */);
+ }
+
+ /**
+ * Continue an operation after the user resolves an error.
+ *
+ * <p>To be called by the LUI upon completion of a resolvable error flow.
+ *
+ * @param resolutionIntent The original intent used to start the LUI.
+ * @param resolutionExtras Resolution-specific extras depending on the result of the resolution.
+ * For example, this may indicate whether the user has consented or may include the input
+ * they provided.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
+ if (!isEnabled()) {
+ PendingIntent callbackIntent =
+ resolutionIntent.getParcelableExtra(
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
+ if (callbackIntent != null) {
+ sendUnavailableError(callbackIntent);
+ }
+ return;
+ }
+ try {
+ mController.continueOperation(resolutionIntent, resolutionExtras);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Fills in the metadata for a DownloadableSubscription.
+ *
+ * <p>May be used in cases that a DownloadableSubscription was constructed to download a
+ * profile, but the metadata for the profile is unknown (e.g. we only know the activation code).
+ * The callback will be triggered with an Intent with
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION} set to the
+ * downloadable subscription metadata upon success.
+ *
+ * <p>Requires that the calling app has the
+ * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
+ * internal system use only.
+ *
+ * @param subscription the subscription which needs metadata filled in
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public void getDownloadableSubscriptionMetadata(
+ DownloadableSubscription subscription, PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.getDownloadableSubscriptionMetadata(
+ subscription, mContext.getOpPackageName(), callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets metadata for subscription which are available for download on this device.
+ *
+ * <p>Subscriptions returned here may be passed to {@link #downloadSubscription}. They may have
+ * been pre-assigned to this particular device, for example. The callback will be triggered with
+ * an Intent with {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS} set to the
+ * list of available subscriptions upon success.
+ *
+ * <p>Requires that the calling app has the
+ * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
+ * internal system use only.
+ *
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ * @hide
+ *
+ * TODO(b/35851809): Make this a SystemApi.
+ */
+ public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.getDefaultDownloadableSubscriptionList(
+ mContext.getOpPackageName(), callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns information about the eUICC chip/device.
+ *
+ * @return the {@link EuiccInfo}. May be null if {@link #isEnabled()} is false or the eUICC is
+ * not ready.
+ */
+ @Nullable
+ public EuiccInfo getEuiccInfo() {
+ if (!isEnabled()) {
+ return null;
+ }
+ try {
+ return mController.getEuiccInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Deletes the given subscription.
+ *
+ * <p>If this subscription is currently active, the device will first switch away from it onto
+ * an "empty" subscription.
+ *
+ * <p>Requires that the calling app has carrier privileges according to the metadata of the
+ * profile to be deleted, or the
+ * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @param subscriptionId the ID of the subscription to delete.
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ */
+ public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.deleteSubscription(
+ subscriptionId, mContext.getOpPackageName(), callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Switch to (enable) the given subscription.
+ *
+ * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+ * or the calling app must be authorized to manage both the currently-active subscription and
+ * the subscription to be enabled according to the subscription metadata. Without the former,
+ * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+ * intent to prompt the user to accept the download.
+ *
+ * @param subscriptionId the ID of the subscription to enable. May be
+ * {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
+ * current profile without activating another profile to replace it.
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ */
+ public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.switchToSubscription(
+ subscriptionId, mContext.getOpPackageName(), callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Update the nickname for the given subscription.
+ *
+ * <p>Requires that the calling app has the
+ * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
+ * internal system use only.
+ *
+ * @param subscriptionId the ID of the subscription to update.
+ * @param nickname the new nickname to apply.
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ * @hide
+ */
+ public void updateSubscriptionNickname(
+ int subscriptionId, String nickname, PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.updateSubscriptionNickname(subscriptionId, nickname, callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Erase all subscriptions and reset the eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
+ * internal system use only.
+ *
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ * @hide
+ */
+ public void eraseSubscriptions(PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.eraseSubscriptions(callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Ensure that subscriptions will be retained on the next factory reset.
+ *
+ * <p>By default, all subscriptions on the eUICC are erased the first time a device boots (ever
+ * and after factory resets). This ensures that the data is wiped after a factory reset is
+ * performed via fastboot or recovery mode, as these modes do not support the necessary radio
+ * communication needed to wipe the eSIM.
+ *
+ * <p>However, this method may be called right before a factory reset issued via settings when
+ * the user elects to retain subscriptions. Doing so will mark them for retention so that they
+ * are not cleared after the ensuing reset.
+ *
+ * <p>Requires that the calling app has the {@link android.Manifest.permission#MASTER_CLEAR}
+ * permission. This is for internal system use only.
+ *
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ * @hide
+ */
+ public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.retainSubscriptionsForFactoryReset(callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static void sendUnavailableError(PendingIntent callbackIntent) {
+ try {
+ callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
+ } catch (PendingIntent.CanceledException e) {
+ // Caller canceled the callback; do nothing.
+ }
+ }
+}
diff --git a/android/telephony/gsm/GsmCellLocation.java b/android/telephony/gsm/GsmCellLocation.java
new file mode 100644
index 00000000..17178023
--- /dev/null
+++ b/android/telephony/gsm/GsmCellLocation.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gsm;
+
+import android.os.Bundle;
+import android.telephony.CellLocation;
+
+/**
+ * Represents the cell location on a GSM phone.
+ */
+public class GsmCellLocation extends CellLocation {
+ private int mLac;
+ private int mCid;
+ private int mPsc;
+
+ /**
+ * Empty constructor. Initializes the LAC and CID to -1.
+ */
+ public GsmCellLocation() {
+ mLac = -1;
+ mCid = -1;
+ mPsc = -1;
+ }
+
+ /**
+ * Initialize the object from a bundle.
+ */
+ public GsmCellLocation(Bundle bundle) {
+ mLac = bundle.getInt("lac", -1);
+ mCid = bundle.getInt("cid", -1);
+ mPsc = bundle.getInt("psc", -1);
+ }
+
+ /**
+ * @return gsm location area code, -1 if unknown, 0xffff max legal value
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * @return gsm cell id, -1 if unknown, 0xffff max legal value
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ /**
+ * On a UMTS network, returns the primary scrambling code of the serving
+ * cell.
+ *
+ * @return primary scrambling code for UMTS, -1 if unknown or GSM
+ */
+ public int getPsc() {
+ return mPsc;
+ }
+
+ /**
+ * Invalidate this object. The location area code and the cell id are set to -1.
+ */
+ @Override
+ public void setStateInvalid() {
+ mLac = -1;
+ mCid = -1;
+ mPsc = -1;
+ }
+
+ /**
+ * Set the location area code and the cell id.
+ */
+ public void setLacAndCid(int lac, int cid) {
+ mLac = lac;
+ mCid = cid;
+ }
+
+ /**
+ * Set the primary scrambling code.
+ * @hide
+ */
+ public void setPsc(int psc) {
+ mPsc = psc;
+ }
+
+ @Override
+ public int hashCode() {
+ return mLac ^ mCid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ GsmCellLocation s;
+
+ try {
+ s = (GsmCellLocation)o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return equalsHandlesNulls(mLac, s.mLac) && equalsHandlesNulls(mCid, s.mCid)
+ && equalsHandlesNulls(mPsc, s.mPsc);
+ }
+
+ @Override
+ public String toString() {
+ return "["+ mLac + "," + mCid + "," + mPsc + "]";
+ }
+
+ /**
+ * Test whether two objects hold the same data values or both are null
+ *
+ * @param a first obj
+ * @param b second obj
+ * @return true if two objects equal or both are null
+ */
+ private static boolean equalsHandlesNulls(Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals (b);
+ }
+
+ /**
+ * Set intent notifier Bundle based on service state
+ *
+ * @param m intent notifier Bundle
+ */
+ public void fillInNotifierBundle(Bundle m) {
+ m.putInt("lac", mLac);
+ m.putInt("cid", mCid);
+ m.putInt("psc", mPsc);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isEmpty() {
+ return (mLac == -1 && mCid == -1 && mPsc == -1);
+ }
+}
diff --git a/android/telephony/gsm/SmsManager.java b/android/telephony/gsm/SmsManager.java
new file mode 100644
index 00000000..777802a5
--- /dev/null
+++ b/android/telephony/gsm/SmsManager.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gsm;
+
+import android.app.PendingIntent;
+
+import java.util.ArrayList;
+
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method SmsManager.getDefault().
+ * @deprecated Replaced by android.telephony.SmsManager that supports both GSM and CDMA.
+ */
+@Deprecated public final class SmsManager {
+ private static SmsManager sInstance;
+ private android.telephony.SmsManager mSmsMgrProxy;
+
+ /** Get the default instance of the SmsManager
+ *
+ * @return the default instance of the SmsManager
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public static final SmsManager getDefault() {
+ if (sInstance == null) {
+ sInstance = new SmsManager();
+ }
+ return sInstance;
+ }
+
+ @Deprecated
+ private SmsManager() {
+ mSmsMgrProxy = android.telephony.SmsManager.getDefault();
+ }
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mSmsMgrProxy.sendTextMessage(destinationAddress, scAddress, text,
+ sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Divide a text message into several messages, none bigger than
+ * the maximum SMS message size.
+ *
+ * @param text the original message. Must not be null.
+ * @return an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final ArrayList<String> divideMessage(String text) {
+ return mSmsMgrProxy.divideMessage(text);
+ }
+
+ /**
+ * Send a multi-part text based SMS. The callee should have already
+ * divided the message into correctly sized parts by calling
+ * <code>divideMessage</code>.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param parts an <code>ArrayList</code> of strings that, in order,
+ * comprise the original message
+ * @param sentIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntents if not null, an <code>ArrayList</code> of
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
+ * extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final void sendMultipartTextMessage(
+ String destinationAddress, String scAddress, ArrayList<String> parts,
+ ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+ mSmsMgrProxy.sendMultipartTextMessage(destinationAddress, scAddress, parts,
+ sentIntents, deliveryIntents);
+ }
+
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param destinationPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code>
+ * <code>RESULT_ERROR_RADIO_OFF</code>
+ * <code>RESULT_ERROR_NULL_PDU</code>.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ *
+ * @throws IllegalArgumentException if destinationAddress or data are empty
+ * @deprecated Use android.telephony.SmsManager.
+ */
+ @Deprecated
+ public final void sendDataMessage(
+ String destinationAddress, String scAddress, short destinationPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mSmsMgrProxy.sendDataMessage(destinationAddress, scAddress, destinationPort,
+ data, sentIntent, deliveryIntent);
+ }
+
+ /**
+ * Copy a raw SMS PDU to the SIM.
+ *
+ * @param smsc the SMSC for this message, or NULL for the default SMSC
+ * @param pdu the raw PDU to store
+ * @param status message status (STATUS_ON_SIM_READ, STATUS_ON_SIM_UNREAD,
+ * STATUS_ON_SIM_SENT, STATUS_ON_SIM_UNSENT)
+ * @return true for success
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final boolean copyMessageToSim(byte[] smsc, byte[] pdu, int status) {
+ return mSmsMgrProxy.copyMessageToIcc(smsc, pdu, status);
+ }
+
+ /**
+ * Delete the specified message from the SIM.
+ *
+ * @param messageIndex is the record index of the message on SIM
+ * @return true for success
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final boolean deleteMessageFromSim(int messageIndex) {
+ return mSmsMgrProxy.deleteMessageFromIcc(messageIndex);
+ }
+
+ /**
+ * Update the specified message on the SIM.
+ *
+ * @param messageIndex record index of message to update
+ * @param newStatus new message status (STATUS_ON_SIM_READ,
+ * STATUS_ON_SIM_UNREAD, STATUS_ON_SIM_SENT,
+ * STATUS_ON_SIM_UNSENT, STATUS_ON_SIM_FREE)
+ * @param pdu the raw PDU to store
+ * @return true for success
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final boolean updateMessageOnSim(int messageIndex, int newStatus, byte[] pdu) {
+ return mSmsMgrProxy.updateMessageOnIcc(messageIndex, newStatus, pdu);
+ }
+
+ /**
+ * Retrieves all messages currently stored on SIM.
+ * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+ * @deprecated Use android.telephony.SmsManager.
+ * {@hide}
+ */
+ @Deprecated
+ public final ArrayList<android.telephony.SmsMessage> getAllMessagesFromSim() {
+ return android.telephony.SmsManager.getDefault().getAllMessagesFromIcc();
+ }
+
+ /** Free space (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_FREE = 0;
+
+ /** Received and read (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_READ = 1;
+
+ /** Received and unread (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_UNREAD = 3;
+
+ /** Stored and sent (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_SENT = 5;
+
+ /** Stored and unsent (TS 51.011 10.5.3).
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int STATUS_ON_SIM_UNSENT = 7;
+
+ /** Generic failure cause
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
+
+ /** Failed because radio was explicitly turned off
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_RADIO_OFF = 2;
+
+ /** Failed because no pdu provided
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_NULL_PDU = 3;
+
+ /** Failed because service is currently unavailable
+ * @deprecated Use android.telephony.SmsManager. */
+ @Deprecated static public final int RESULT_ERROR_NO_SERVICE = 4;
+
+}
diff --git a/android/telephony/gsm/SmsMessage.java b/android/telephony/gsm/SmsMessage.java
new file mode 100644
index 00000000..8d5dac7e
--- /dev/null
+++ b/android/telephony/gsm/SmsMessage.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gsm;
+
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ * @deprecated Replaced by android.telephony.SmsMessage that supports both GSM and CDMA.
+ */
+@Deprecated
+public class SmsMessage {
+ /**
+ * SMS Class enumeration.
+ * See TS 23.038.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public enum MessageClass{
+ UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+ }
+
+ /** Unknown encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_UNKNOWN = 0;
+
+ /** 7-bit encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_7BIT = 1;
+
+ /** 8-bit encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_8BIT = 2;
+
+ /** 16-bit encoding scheme (see TS 23.038)
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int ENCODING_16BIT = 3;
+
+ /** The maximum number of payload bytes per message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int MAX_USER_DATA_BYTES = 140;
+
+ /**
+ * The maximum number of payload bytes per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ *
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide pending API Council approval to extend the public API
+ */
+ @Deprecated public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+ /** The maximum number of payload septets per message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int MAX_USER_DATA_SEPTETS = 160;
+
+ /**
+ * The maximum number of payload septets per message if a user data header
+ * is present. This assumes the header only contains the
+ * CONCATENATED_8_BIT_REFERENCE element.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+ /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+ * @deprecated Use android.telephony.SmsMessage.
+ * {@hide}
+ */
+ @Deprecated public SmsMessageBase mWrappedSmsMessage;
+
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public static class SubmitPdu {
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated public byte[] encodedScAddress; // Null if not applicable.
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated public byte[] encodedMessage;
+
+ //Constructor
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public SubmitPdu() {
+ }
+
+ /** @deprecated Use android.telephony.SmsMessage.
+ * {@hide}
+ */
+ @Deprecated
+ protected SubmitPdu(SubmitPduBase spb) {
+ this.encodedMessage = spb.encodedMessage;
+ this.encodedScAddress = spb.encodedScAddress;
+ }
+
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Override
+ @Deprecated
+ public String toString() {
+ return "SubmitPdu: encodedScAddress = "
+ + Arrays.toString(encodedScAddress)
+ + ", encodedMessage = "
+ + Arrays.toString(encodedMessage);
+ }
+ }
+
+ // Constructor
+ /** @deprecated Use android.telephony.SmsMessage. */
+ @Deprecated
+ public SmsMessage() {
+ this(getSmsFacility());
+ }
+
+ private SmsMessage(SmsMessageBase smb) {
+ mWrappedSmsMessage = smb;
+ }
+
+ /**
+ * Create an SmsMessage from a raw PDU.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static SmsMessage createFromPdu(byte[] pdu) {
+ SmsMessageBase wrappedMessage;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+ } else {
+ wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+ }
+
+ return new SmsMessage(wrappedMessage);
+ }
+
+ /**
+ * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+ * length in bytes (not hex chars) less the SMSC header
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static int getTPLayerLengthForPDU(String pdu) {
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+ } else {
+ return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+ }
+ }
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message, given the
+ * current encoding.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the GSM
+ * alphabet are counted as a single space char. If false, a
+ * messageBody containing non-GSM alphabet characters is calculated
+ * for 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's required, int[1]
+ * the number of code units used, and int[2] is the number of code
+ * units remaining until the next message. int[3] is the encoding
+ * type that should be used for the message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+ GsmAlphabet.TextEncodingDetails ted =
+ com.android.internal.telephony.gsm.SmsMessage
+ .calculateLength(messageBody, use7bitOnly);
+ int ret[] = new int[4];
+ ret[0] = ted.msgCount;
+ ret[1] = ted.codeUnitCount;
+ ret[2] = ted.codeUnitsRemaining;
+ ret[3] = ted.codeUnitSize;
+ return ret;
+ }
+
+ /**
+ * Calculates the number of SMS's required to encode the message body and
+ * the number of characters remaining until the next message, given the
+ * current encoding.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly if true, characters that are not part of the GSM
+ * alphabet are counted as a single space char. If false, a
+ * messageBody containing non-GSM alphabet characters is calculated
+ * for 16-bit encoding.
+ * @return an int[4] with int[0] being the number of SMS's required, int[1]
+ * the number of code units used, and int[2] is the number of code
+ * units remaining until the next message. int[3] is the encoding
+ * type that should be used for the message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+ return calculateLength((CharSequence)messageBody, use7bitOnly);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide
+ */
+ @Deprecated
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message,
+ boolean statusReportRequested, byte[] header) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested,
+ SmsHeader.fromByteArray(header));
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested, header);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a destination address and a message
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, String message, boolean statusReportRequested) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested, null);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, message, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+ *
+ * @param scAddress Service Centre address. null == use default
+ * @param destinationAddress the address of the destination for the message
+ * @param destinationPort the port to deliver the message to at the
+ * destination
+ * @param data the dat for the message
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public static SubmitPdu getSubmitPdu(String scAddress,
+ String destinationAddress, short destinationPort, byte[] data,
+ boolean statusReportRequested) {
+ SubmitPduBase spb;
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+ if (PHONE_TYPE_CDMA == activePhone) {
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ } else {
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ destinationAddress, destinationPort, data, statusReportRequested);
+ }
+
+ return new SubmitPdu(spb);
+ }
+
+ /**
+ * Returns the address of the SMS service center that relayed this message
+ * or null if there is none.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getServiceCenterAddress() {
+ return mWrappedSmsMessage.getServiceCenterAddress();
+ }
+
+ /**
+ * Returns the originating address (sender) of this SMS message in String
+ * form or null if unavailable
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getOriginatingAddress() {
+ return mWrappedSmsMessage.getOriginatingAddress();
+ }
+
+ /**
+ * Returns the originating address, or email from address if this message
+ * was from an email gateway. Returns null if originating address
+ * unavailable.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getDisplayOriginatingAddress() {
+ return mWrappedSmsMessage.getDisplayOriginatingAddress();
+ }
+
+ /**
+ * Returns the message body as a String, if it exists and is text based.
+ * @return message body is there is one, otherwise null
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getMessageBody() {
+ return mWrappedSmsMessage.getMessageBody();
+ }
+
+ /**
+ * Returns the class of this message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public MessageClass getMessageClass() {
+ int index = mWrappedSmsMessage.getMessageClass().ordinal();
+
+ return MessageClass.values()[index];
+ }
+
+ /**
+ * Returns the message body, or email message body if this message was from
+ * an email gateway. Returns null if message body unavailable.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getDisplayMessageBody() {
+ return mWrappedSmsMessage.getDisplayMessageBody();
+ }
+
+ /**
+ * Unofficial convention of a subject line enclosed in parens empty string
+ * if not present
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getPseudoSubject() {
+ return mWrappedSmsMessage.getPseudoSubject();
+ }
+
+ /**
+ * Returns the service centre timestamp in currentTimeMillis() format
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public long getTimestampMillis() {
+ return mWrappedSmsMessage.getTimestampMillis();
+ }
+
+ /**
+ * Returns true if message is an email.
+ *
+ * @return true if this message came through an email gateway and email
+ * sender / subject / parsed body are available
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isEmail() {
+ return mWrappedSmsMessage.isEmail();
+ }
+
+ /**
+ * @return if isEmail() is true, body of the email sent through the gateway.
+ * null otherwise
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getEmailBody() {
+ return mWrappedSmsMessage.getEmailBody();
+ }
+
+ /**
+ * @return if isEmail() is true, email from address of email sent through
+ * the gateway. null otherwise
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public String getEmailFrom() {
+ return mWrappedSmsMessage.getEmailFrom();
+ }
+
+ /**
+ * Get protocol identifier.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public int getProtocolIdentifier() {
+ return mWrappedSmsMessage.getProtocolIdentifier();
+ }
+
+ /**
+ * See TS 23.040 9.2.3.9 returns true if this is a "replace short message" SMS
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isReplace() {
+ return mWrappedSmsMessage.isReplace();
+ }
+
+ /**
+ * Returns true for CPHS MWI toggle message.
+ *
+ * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section B.4.2
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isCphsMwiMessage() {
+ return mWrappedSmsMessage.isCphsMwiMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) clear message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isMWIClearMessage() {
+ return mWrappedSmsMessage.isMWIClearMessage();
+ }
+
+ /**
+ * returns true if this message is a CPHS voicemail / message waiting
+ * indicator (MWI) set message
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isMWISetMessage() {
+ return mWrappedSmsMessage.isMWISetMessage();
+ }
+
+ /**
+ * returns true if this message is a "Message Waiting Indication Group:
+ * Discard Message" notification and should not be stored.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isMwiDontStore() {
+ return mWrappedSmsMessage.isMwiDontStore();
+ }
+
+ /**
+ * returns the user data section minus the user data header if one was present.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public byte[] getUserData() {
+ return mWrappedSmsMessage.getUserData();
+ }
+
+ /* Not part of the SDK interface and only needed by specific classes:
+ protected SmsHeader getUserDataHeader()
+ */
+
+ /**
+ * Returns the raw PDU for the message.
+ *
+ * @return the raw PDU for the message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public byte[] getPdu() {
+ return mWrappedSmsMessage.getPdu();
+ }
+
+ /**
+ * Returns the status of the message on the SIM (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the SIM. These are:
+ * SmsManager.STATUS_ON_SIM_FREE
+ * SmsManager.STATUS_ON_SIM_READ
+ * SmsManager.STATUS_ON_SIM_UNREAD
+ * SmsManager.STATUS_ON_SIM_SEND
+ * SmsManager.STATUS_ON_SIM_UNSENT
+ * @deprecated Use android.telephony.SmsMessage and getStatusOnIcc instead.
+ */
+ @Deprecated
+ public int getStatusOnSim() {
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the status of the message on the ICC (read, unread, sent, unsent).
+ *
+ * @return the status of the message on the ICC. These are:
+ * SmsManager.STATUS_ON_ICC_FREE
+ * SmsManager.STATUS_ON_ICC_READ
+ * SmsManager.STATUS_ON_ICC_UNREAD
+ * SmsManager.STATUS_ON_ICC_SEND
+ * SmsManager.STATUS_ON_ICC_UNSENT
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide
+ */
+ @Deprecated
+ public int getStatusOnIcc() {
+
+ return mWrappedSmsMessage.getStatusOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the SIM (1-based index).
+ * @return the record index of the message on the SIM, or -1 if this
+ * SmsMessage was not created from a SIM SMS EF record.
+ * @deprecated Use android.telephony.SmsMessage and getIndexOnIcc instead.
+ */
+ @Deprecated
+ public int getIndexOnSim() {
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * Returns the record index of the message on the ICC (1-based index).
+ * @return the record index of the message on the ICC, or -1 if this
+ * SmsMessage was not created from a ICC SMS EF record.
+ * @deprecated Use android.telephony.SmsMessage.
+ * @hide
+ */
+ @Deprecated
+ public int getIndexOnIcc() {
+
+ return mWrappedSmsMessage.getIndexOnIcc();
+ }
+
+ /**
+ * GSM:
+ * For an SMS-STATUS-REPORT message, this returns the status field from
+ * the status report. This field indicates the status of a previously
+ * submitted SMS, if requested. See TS 23.040, 9.2.3.15 TP-Status for a
+ * description of values.
+ * CDMA:
+ * For not interfering with status codes from GSM, the value is
+ * shifted to the bits 31-16.
+ * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+ * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+ *
+ * @return 0 indicates the previously sent message was received.
+ * See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+ * for a description of other possible values.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public int getStatus() {
+ return mWrappedSmsMessage.getStatus();
+ }
+
+ /**
+ * Return true iff the message is a SMS-STATUS-REPORT message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isStatusReportMessage() {
+ return mWrappedSmsMessage.isStatusReportMessage();
+ }
+
+ /**
+ * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+ * this message.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ public boolean isReplyPathPresent() {
+ return mWrappedSmsMessage.isReplyPathPresent();
+ }
+
+ /** This method returns the reference to a specific
+ * SmsMessage object, which is used for accessing its static methods.
+ * @return Specific SmsMessage.
+ * @deprecated Use android.telephony.SmsMessage.
+ */
+ @Deprecated
+ private static final SmsMessageBase getSmsFacility(){
+ int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+ if (PHONE_TYPE_CDMA == activePhone) {
+ return new com.android.internal.telephony.cdma.SmsMessage();
+ } else {
+ return new com.android.internal.telephony.gsm.SmsMessage();
+ }
+ }
+}
diff --git a/android/telephony/ims/ImsService.java b/android/telephony/ims/ImsService.java
new file mode 100644
index 00000000..9d91cc33
--- /dev/null
+++ b/android/telephony/ims/ImsService.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsUt;
+import com.android.internal.annotations.VisibleForTesting;
+
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ * android:permission="android.permission.BIND_IMS_SERVICE" >
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the RCS_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ * <intent-filter>
+ * <action android:name="android.telephony.ims.ImsService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ * "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
+ * available to place emergency calls at all times. This MUST be implemented by the default
+ * ImsService provided in the device overlay.
+ * @hide
+ */
+@SystemApi
+public class ImsService extends Service {
+
+ private static final String LOG_TAG = "ImsService";
+
+ /**
+ * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+
+ // A map of slot Id -> Set of features corresponding to that slot.
+ private final SparseArray<SparseArray<ImsFeature>> mFeatures = new SparseArray<>();
+
+ /**
+ * @hide
+ */
+ // Implements all supported features as a flat interface.
+ protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+ @Override
+ public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ synchronized (mFeatures) {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature");
+ onCreateImsFeatureInternal(slotId, feature, c);
+ }
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ synchronized (mFeatures) {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature");
+ onRemoveImsFeatureInternal(slotId, feature, c);
+ }
+ }
+
+ @Override
+ public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
+ IImsRegistrationListener listener) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.startSession(incomingCallIntent, listener);
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
+ synchronized (mFeatures) {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession");
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.endSession(sessionId);
+ }
+ }
+ }
+
+ @Override
+ public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
+ throws RemoteException {
+ enforceReadPhoneStatePermission("isConnected");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.isConnected(callSessionType, callType);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOpened(int slotId, int featureType) throws RemoteException {
+ enforceReadPhoneStatePermission("isOpened");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.isOpened();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
+ enforceReadPhoneStatePermission("getFeatureStatus");
+ int status = ImsFeature.STATE_NOT_AVAILABLE;
+ synchronized (mFeatures) {
+ SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
+ if (featureMap != null) {
+ ImsFeature feature = getImsFeatureFromType(featureMap, featureType);
+ if (feature != null) {
+ status = feature.getFeatureState();
+ }
+ }
+ }
+ return status;
+ }
+
+ @Override
+ public void addRegistrationListener(int slotId, int featureType,
+ IImsRegistrationListener listener) throws RemoteException {
+ enforceReadPhoneStatePermission("addRegistrationListener");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.addRegistrationListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public void removeRegistrationListener(int slotId, int featureType,
+ IImsRegistrationListener listener) throws RemoteException {
+ enforceReadPhoneStatePermission("removeRegistrationListener");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.removeRegistrationListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
+ int callSessionType, int callType) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.createCallProfile(sessionId, callSessionType, callType);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
+ ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.createCallSession(sessionId, profile, listener);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
+ String callId) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getPendingCallSession(sessionId, callId);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsUt getUtInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getUtInterface();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsConfig getConfigInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getConfigInterface();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void turnOnIms(int slotId, int featureType) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.turnOnIms();
+ }
+ }
+ }
+
+ @Override
+ public void turnOffIms(int slotId, int featureType) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.turnOffIms();
+ }
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getEcbmInterface();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.setUiTTYMode(uiTtyMode, onComplete);
+ }
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getMultiEndpointInterface();
+ }
+ }
+ return null;
+ }
+
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if(SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mImsServiceController;
+ }
+ return null;
+ }
+
+ /**
+ * Called from the ImsResolver to create the requested ImsFeature, as defined by the slot and
+ * featureType
+ * @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
+ * @param featureType An integer representing the type of ImsFeature being created. This is
+ * defined in {@link ImsFeature}.
+ */
+ // Be sure to lock on mFeatures before accessing this method
+ private void onCreateImsFeatureInternal(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
+ if (featureMap == null) {
+ featureMap = new SparseArray<>();
+ mFeatures.put(slotId, featureMap);
+ }
+ ImsFeature f = makeImsFeature(slotId, featureType);
+ if (f != null) {
+ f.setContext(this);
+ f.setSlotId(slotId);
+ f.addImsFeatureStatusCallback(c);
+ featureMap.put(featureType, f);
+ }
+
+ }
+ /**
+ * Called from the ImsResolver to remove an existing ImsFeature, as defined by the slot and
+ * featureType.
+ * @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
+ * @param featureType An integer representing the type of ImsFeature being removed. This is
+ * defined in {@link ImsFeature}.
+ */
+ // Be sure to lock on mFeatures before accessing this method
+ private void onRemoveImsFeatureInternal(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
+ if (featureMap == null) {
+ return;
+ }
+
+ ImsFeature featureToRemove = getImsFeatureFromType(featureMap, featureType);
+ if (featureToRemove != null) {
+ featureMap.remove(featureType);
+ featureToRemove.notifyFeatureRemoved(slotId);
+ // Remove reference to Binder
+ featureToRemove.removeImsFeatureStatusCallback(c);
+ }
+ }
+
+ // Be sure to lock on mFeatures before accessing this method
+ private MMTelFeature resolveMMTelFeature(int slotId, int featureType) {
+ SparseArray<ImsFeature> features = getImsFeatureMap(slotId);
+ MMTelFeature feature = null;
+ if (features != null) {
+ feature = resolveImsFeature(features, featureType, MMTelFeature.class);
+ }
+ return feature;
+ }
+
+ // Be sure to lock on mFeatures before accessing this method
+ private <T extends ImsFeature> T resolveImsFeature(SparseArray<ImsFeature> set, int featureType,
+ Class<T> className) {
+ ImsFeature feature = getImsFeatureFromType(set, featureType);
+ if (feature == null) {
+ return null;
+ }
+ try {
+ return className.cast(feature);
+ } catch (ClassCastException e)
+ {
+ Log.e(LOG_TAG, "Can not cast ImsFeature! Exception: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ // Be sure to lock on mFeatures before accessing this method
+ public SparseArray<ImsFeature> getImsFeatureMap(int slotId) {
+ return mFeatures.get(slotId);
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ // Be sure to lock on mFeatures before accessing this method
+ public ImsFeature getImsFeatureFromType(SparseArray<ImsFeature> set, int featureType) {
+ return set.get(featureType);
+ }
+
+ private ImsFeature makeImsFeature(int slotId, int feature) {
+ switch (feature) {
+ case ImsFeature.EMERGENCY_MMTEL: {
+ return onCreateEmergencyMMTelImsFeature(slotId);
+ }
+ case ImsFeature.MMTEL: {
+ return onCreateMMTelImsFeature(slotId);
+ }
+ case ImsFeature.RCS: {
+ return onCreateRcsFeature(slotId);
+ }
+ }
+ // Tried to create feature that is not defined.
+ return null;
+ }
+
+ /**
+ * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a
+ * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps.
+ */
+ private void enforceReadPhoneStatePermission(String fn) {
+ if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ enforceCallingOrSelfPermission(READ_PHONE_STATE, fn);
+ }
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality. Must be able to handle emergency calls at any time as well.
+ * @hide
+ */
+ public MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality.
+ * @hide
+ */
+ public MMTelFeature onCreateMMTelImsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @return An implementation of RcsFeature that will be used by the system for RCS.
+ * @hide
+ */
+ public RcsFeature onCreateRcsFeature(int slotId) {
+ return null;
+ }
+}
diff --git a/android/telephony/ims/ImsServiceProxy.java b/android/telephony/ims/ImsServiceProxy.java
new file mode 100644
index 00000000..038e295d
--- /dev/null
+++ b/android/telephony/ims/ImsServiceProxy.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.feature.IRcsFeature;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.Log;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
+ * the platform currently supports: MMTel and RCS.
+ * @hide
+ */
+
+public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
+
+ protected String LOG_TAG = "ImsServiceProxy";
+ private final int mSupportedFeature;
+
+ // Start by assuming the proxy is available for usage.
+ private boolean mIsAvailable = true;
+ // ImsFeature Status from the ImsService. Cached.
+ private Integer mFeatureStatusCached = null;
+ private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
+ private final Object mLock = new Object();
+
+ public interface INotifyStatusChanged {
+ void notifyStatusChanged();
+ }
+
+ private final IImsServiceFeatureListener mListenerBinder =
+ new IImsServiceFeatureListener.Stub() {
+
+ @Override
+ public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
+ // The feature has been re-enabled. This may happen when the service crashes.
+ synchronized (mLock) {
+ if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
+ Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
+ feature);
+ mIsAvailable = true;
+ }
+ }
+ }
+
+ @Override
+ public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
+ synchronized (mLock) {
+ if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
+ Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
+ feature);
+ mIsAvailable = false;
+ }
+ }
+ }
+
+ @Override
+ public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
+ synchronized (mLock) {
+ Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
+ " status: " + status);
+ if (mSlotId == slotId && feature == mSupportedFeature) {
+ mFeatureStatusCached = status;
+ if (mStatusCallback != null) {
+ mStatusCallback.notifyStatusChanged();
+ }
+ }
+ }
+ }
+ };
+
+ public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
+ super(slotId, binder);
+ mSupportedFeature = featureType;
+ }
+
+ public ImsServiceProxy(int slotId, int featureType) {
+ super(slotId, null /*IBinder*/);
+ mSupportedFeature = featureType;
+ }
+
+ public IImsServiceFeatureListener getListener() {
+ return mListenerBinder;
+ }
+
+ public void setBinder(IBinder binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
+ incomingCallIntent, listener);
+ }
+ }
+
+ @Override
+ public void endSession(int sessionId) throws RemoteException {
+ synchronized (mLock) {
+ // Only check to make sure the binder connection still exists. This method should
+ // still be able to be called when the state is STATE_NOT_AVAILABLE.
+ checkBinderConnection();
+ getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
+ }
+ }
+
+ @Override
+ public boolean isConnected(int callServiceType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
+ callServiceType, callType);
+ }
+ }
+
+ @Override
+ public boolean isOpened() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
+ listener);
+ }
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
+ listener);
+ }
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
+ sessionId, callServiceType, callType);
+ }
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
+ sessionId, profile, listener);
+ }
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
+ sessionId, callId);
+ }
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void turnOnIms() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void turnOffIms() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
+ onComplete);
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkServiceIsReady();
+ return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
+ mSupportedFeature);
+ }
+ }
+
+ @Override
+ public int getFeatureStatus() {
+ synchronized (mLock) {
+ if (isBinderAlive() && mFeatureStatusCached != null) {
+ Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
+ return mFeatureStatusCached;
+ }
+ }
+ // Don't synchronize on Binder call.
+ Integer status = retrieveFeatureStatus();
+ synchronized (mLock) {
+ if (status == null) {
+ return ImsFeature.STATE_NOT_AVAILABLE;
+ }
+ // Cache only non-null value for feature status.
+ mFeatureStatusCached = status;
+ }
+ Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
+ return status;
+ }
+
+ /**
+ * Internal method used to retrieve the feature status from the corresponding ImsService.
+ */
+ private Integer retrieveFeatureStatus() {
+ if (mBinder != null) {
+ try {
+ return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
+ } catch (RemoteException e) {
+ // Status check failed, don't update cache
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param c Callback that will fire when the feature status has changed.
+ */
+ public void setStatusCallback(INotifyStatusChanged c) {
+ mStatusCallback = c;
+ }
+
+ /**
+ * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
+ * method returns false, it doesn't mean that the Binder connection is not available (use
+ * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
+ * at this time.
+ *
+ * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
+ * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
+ */
+ public boolean isBinderReady() {
+ return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
+ }
+
+ @Override
+ public boolean isBinderAlive() {
+ return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
+ }
+
+ protected void checkServiceIsReady() throws RemoteException {
+ if (!isBinderReady()) {
+ throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
+ }
+ }
+
+ private IImsServiceController getServiceInterface(IBinder b) {
+ return IImsServiceController.Stub.asInterface(b);
+ }
+}
diff --git a/android/telephony/ims/ImsServiceProxyCompat.java b/android/telephony/ims/ImsServiceProxyCompat.java
new file mode 100644
index 00000000..bbd5f027
--- /dev/null
+++ b/android/telephony/ims/ImsServiceProxyCompat.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.feature.IMMTelFeature;
+import android.telephony.ims.feature.ImsFeature;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsService;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Compatibility class that implements the new ImsService IMMTelFeature interface, but
+ * uses the old IImsService interface to support older devices that implement the deprecated
+ * opt/net/ims interface.
+ * @hide
+ */
+
+public class ImsServiceProxyCompat implements IMMTelFeature {
+
+ private static final int SERVICE_ID = ImsFeature.MMTEL;
+
+ protected final int mSlotId;
+ protected IBinder mBinder;
+
+ public ImsServiceProxyCompat(int slotId, IBinder binder) {
+ mSlotId = slotId;
+ mBinder = binder;
+ }
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
+ listener);
+ }
+
+ @Override
+ public void endSession(int sessionId) throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).close(sessionId);
+ }
+
+ @Override
+ public boolean isConnected(int callServiceType, int callType)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).isConnected(SERVICE_ID, callServiceType, callType);
+ }
+
+ @Override
+ public boolean isOpened() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).isOpened(SERVICE_ID);
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ // Not Implemented in old ImsService. If the registration listener becomes invalid, the
+ // ImsService will remove.
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getConfigInterface(mSlotId);
+ }
+
+ @Override
+ public void turnOnIms() throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).turnOnIms(mSlotId);
+ }
+
+ @Override
+ public void turnOffIms() throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).turnOffIms(mSlotId);
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete)
+ throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
+ }
+
+ /**
+ * Base implementation, always returns READY for compatibility with old ImsService.
+ */
+ public int getFeatureStatus() {
+ return ImsFeature.STATE_READY;
+ }
+
+ /**
+ * @return false if the binder connection is no longer alive.
+ */
+ public boolean isBinderAlive() {
+ return mBinder != null && mBinder.isBinderAlive();
+ }
+
+ private IImsService getServiceInterface(IBinder b) {
+ return IImsService.Stub.asInterface(b);
+ }
+
+ protected void checkBinderConnection() throws RemoteException {
+ if (!isBinderAlive()) {
+ throw new RemoteException("ImsServiceProxy is not available for that feature.");
+ }
+ }
+}
diff --git a/android/telephony/ims/feature/IMMTelFeature.java b/android/telephony/ims/feature/IMMTelFeature.java
new file mode 100644
index 00000000..d65e27eb
--- /dev/null
+++ b/android/telephony/ims/feature/IMMTelFeature.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+import android.app.PendingIntent;
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * MMTel interface for an ImsService. When updating this interface, ensure that base implementations
+ * of your changes are also present in MMTelFeature for compatibility with older versions of the
+ * MMTel feature.
+ * @hide
+ */
+
+public interface IMMTelFeature {
+
+ /**
+ * Notifies the MMTel feature that you would like to start a session. This should always be
+ * done before making/receiving IMS calls. The IMS service will register the device to the
+ * operator's network with the credentials (from ISIM) periodically in order to receive calls
+ * from the operator's network. When the IMS service receives a new call, it will send out an
+ * intent with the provided action string. The intent contains a call ID extra
+ * {@link IImsCallSession#getCallId} and it can be used to take a call.
+ *
+ * @param incomingCallIntent When an incoming call is received, the IMS service will call
+ * {@link PendingIntent#send} to send back the intent to the caller with
+ * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID;
+ * It cannot be null.
+ * @param listener To listen to IMS registration events; It cannot be null
+ * @return an integer (greater than 0) representing the session id associated with the session
+ * that has been started.
+ */
+ int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException;
+
+ /**
+ * End a previously started session using the associated sessionId.
+ * @param sessionId an integer (greater than 0) representing the ongoing session. See
+ * {@link #startSession}.
+ */
+ void endSession(int sessionId) throws RemoteException;
+
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network with the specified
+ * service & call type.
+ *
+ * @param callServiceType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * @return true if the specified service id is connected to the IMS network; false otherwise
+ * @throws RemoteException
+ */
+ boolean isConnected(int callServiceType, int callType) throws RemoteException;
+
+ /**
+ * Checks if the specified IMS service is opened.
+ *
+ * @return true if the specified service id is opened; false otherwise
+ */
+ boolean isOpened() throws RemoteException;
+
+ /**
+ * Add a new registration listener for the client associated with the session Id.
+ * @param listener An implementation of IImsRegistrationListener.
+ */
+ void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException;
+
+ /**
+ * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+ * associated with the session Id.
+ * @param listener A previously registered IImsRegistrationListener
+ */
+ void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException;
+
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callServiceType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
+ ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
+ throws RemoteException;
+
+ /**
+ * Creates a {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param profile a call profile to make the call
+ * @param listener An implementation of IImsCallSessionListener.
+ */
+ IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException;
+
+ /**
+ * Retrieves the call session associated with a pending call.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callId a call id to make the call
+ */
+ IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
+
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
+ IImsUt getUtInterface() throws RemoteException;
+
+ /**
+ * @return The config interface for IMS Configuration
+ */
+ IImsConfig getConfigInterface() throws RemoteException;
+
+ /**
+ * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ */
+ void turnOnIms() throws RemoteException;
+
+ /**
+ * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ */
+ void turnOffIms() throws RemoteException;
+
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
+ IImsEcbm getEcbmInterface() throws RemoteException;
+
+ /**
+ * Sets the current UI TTY mode for the MMTelFeature.
+ * @param uiTtyMode An integer containing the new UI TTY Mode.
+ * @param onComplete A {@link Message} to be used when the mode has been set.
+ * @throws RemoteException
+ */
+ void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
+
+ /**
+ * @return MultiEndpoint interface for DEP notifications
+ */
+ IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
+}
diff --git a/android/telephony/ims/feature/IRcsFeature.java b/android/telephony/ims/feature/IRcsFeature.java
new file mode 100644
index 00000000..e28e1b38
--- /dev/null
+++ b/android/telephony/ims/feature/IRcsFeature.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+/**
+ * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
+ * in the framework.
+ * @hide
+ */
+
+public interface IRcsFeature {
+}
diff --git a/android/telephony/ims/feature/ImsFeature.java b/android/telephony/ims/feature/ImsFeature.java
new file mode 100644
index 00000000..9d880b73
--- /dev/null
+++ b/android/telephony/ims/feature/ImsFeature.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public abstract class ImsFeature {
+
+ private static final String LOG_TAG = "ImsFeature";
+
+ /**
+ * Action to broadcast when ImsService is up.
+ * Internal use only.
+ * Only defined here separately compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_UP =
+ "com.android.ims.IMS_SERVICE_UP";
+
+ /**
+ * Action to broadcast when ImsService is down.
+ * Internal use only.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_DOWN =
+ "com.android.ims.IMS_SERVICE_DOWN";
+
+ /**
+ * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+ * A long value; the phone ID corresponding to the IMS service coming up or down.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+ // Invalid feature value
+ public static final int INVALID = -1;
+ // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+ // defined values in ImsServiceClass for compatibility purposes.
+ public static final int EMERGENCY_MMTEL = 0;
+ public static final int MMTEL = 1;
+ public static final int RCS = 2;
+ // Total number of features defined
+ public static final int MAX = 3;
+
+ // Integer values defining the state of the ImsFeature at any time.
+ @IntDef(flag = true,
+ value = {
+ STATE_NOT_AVAILABLE,
+ STATE_INITIALIZING,
+ STATE_READY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsState {}
+ public static final int STATE_NOT_AVAILABLE = 0;
+ public static final int STATE_INITIALIZING = 1;
+ public static final int STATE_READY = 2;
+
+ private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>();
+ private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+ new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+ private @ImsState int mState = STATE_NOT_AVAILABLE;
+ private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ private Context mContext;
+
+ public interface INotifyFeatureRemoved {
+ void onFeatureRemoved(int slotId);
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public void setSlotId(int slotId) {
+ mSlotId = slotId;
+ }
+
+ public void addFeatureRemovedListener(INotifyFeatureRemoved listener) {
+ synchronized (mRemovedListeners) {
+ mRemovedListeners.add(listener);
+ }
+ }
+
+ public void removeFeatureRemovedListener(INotifyFeatureRemoved listener) {
+ synchronized (mRemovedListeners) {
+ mRemovedListeners.remove(listener);
+ }
+ }
+
+ // Not final for testing.
+ public void notifyFeatureRemoved(int slotId) {
+ synchronized (mRemovedListeners) {
+ mRemovedListeners.forEach(l -> l.onFeatureRemoved(slotId));
+ onFeatureRemoved();
+ }
+ }
+
+ public int getFeatureState() {
+ return mState;
+ }
+
+ protected final void setFeatureState(@ImsState int state) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
+ }
+
+ public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ if (c == null) {
+ return;
+ }
+ try {
+ // If we have just connected, send queued status.
+ c.notifyImsFeatureStatus(mState);
+ // Add the callback if the callback completes successfully without a RemoteException.
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.add(c);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+
+ public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ if (c == null) {
+ return;
+ }
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.remove(c);
+ }
+ }
+
+ /**
+ * Internal method called by ImsFeature when setFeatureState has changed.
+ * @param state
+ */
+ private void notifyFeatureState(@ImsState int state) {
+ synchronized (mStatusCallbacks) {
+ for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+ iter.hasNext(); ) {
+ IImsFeatureStatusCallback callback = iter.next();
+ try {
+ Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+ callback.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ // remove if the callback is no longer alive.
+ iter.remove();
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+ }
+ sendImsServiceIntent(state);
+ }
+
+ /**
+ * Provide backwards compatibility using deprecated service UP/DOWN intents.
+ */
+ private void sendImsServiceIntent(@ImsState int state) {
+ if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
+ Intent intent;
+ switch (state) {
+ case ImsFeature.STATE_NOT_AVAILABLE:
+ case ImsFeature.STATE_INITIALIZING:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ break;
+ case ImsFeature.STATE_READY:
+ intent = new Intent(ACTION_IMS_SERVICE_UP);
+ break;
+ default:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ }
+ intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Called when the feature is being removed and must be cleaned up.
+ */
+ public abstract void onFeatureRemoved();
+}
diff --git a/android/telephony/ims/feature/MMTelFeature.java b/android/telephony/ims/feature/MMTelFeature.java
new file mode 100644
index 00000000..a71f0bf0
--- /dev/null
+++ b/android/telephony/ims/feature/MMTelFeature.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+import android.app.PendingIntent;
+import android.os.Message;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
+ * MMTelFeature should extend this class and implement all methods that the service supports.
+ *
+ * @hide
+ */
+
+public class MMTelFeature extends ImsFeature implements IMMTelFeature {
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
+ return 0;
+ }
+
+ @Override
+ public void endSession(int sessionId) {
+ }
+
+ @Override
+ public boolean isConnected(int callSessionType, int callType) {
+ return false;
+ }
+
+ @Override
+ public boolean isOpened() {
+ return false;
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener) {
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener) {
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
+ return null;
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) {
+ return null;
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId) {
+ return null;
+ }
+
+ @Override
+ public IImsUt getUtInterface() {
+ return null;
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() {
+ return null;
+ }
+
+ @Override
+ public void turnOnIms() {
+ }
+
+ @Override
+ public void turnOffIms() {
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() {
+ return null;
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete) {
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() {
+ return null;
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+}
diff --git a/android/telephony/ims/feature/RcsFeature.java b/android/telephony/ims/feature/RcsFeature.java
new file mode 100644
index 00000000..9cddc1b9
--- /dev/null
+++ b/android/telephony/ims/feature/RcsFeature.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the IRcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature implements IRcsFeature {
+
+ public RcsFeature() {
+ super();
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+}
diff --git a/android/telephony/ims/stub/ImsCallSessionImplBase.java b/android/telephony/ims/stub/ImsCallSessionImplBase.java
new file mode 100644
index 00000000..80b2f789
--- /dev/null
+++ b/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Base implementation of IImsCallSession, which implements stub versions of the methods in the
+ * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() throws RemoteException {
+
+ }
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ @Override
+ public String getCallId() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getLocalCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the remote call profile that this session is associated with
+ *
+ * @return the remote {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getRemoteCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ @Override
+ public String getProperty(String name) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the session state.
+ * The value returned must be one of the states in {@link ImsCallSession.State}.
+ *
+ * @return the session state
+ */
+ @Override
+ public int getState() throws RemoteException {
+ return ImsCallSession.State.INVALID;
+ }
+
+ /**
+ * Checks if the session is in call.
+ *
+ * @return true if the session is in call, false otherwise
+ */
+ @Override
+ public boolean isInCall() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Sets the listener to listen to the session events. An {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ @Override
+ public void setListener(IImsCallSessionListener listener) throws RemoteException {
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ @Override
+ public void setMute(boolean muted) throws RemoteException {
+ }
+
+ /**
+ * Initiates an IMS call with the specified target and call profile.
+ * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void start(String callee, ImsCallProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Initiates an IMS call with the specified participants and call profile.
+ * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile)
+ throws RemoteException {
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see {@link ImsCallSession.Listener#callSessionStarted}
+ */
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call, defined in
+ * com.android.ims.ImsReasonInfo
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void reject(int reason) throws RemoteException {
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @param reason reason code to terminate a call, defined in
+ * com.android.ims.ImsReasonInfo
+ *
+ * @see {@link ImsCallSession.Listener#callSessionTerminated}
+ */
+ @Override
+ public void terminate(int reason) throws RemoteException {
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+ * called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see {@link ImsCallSession.Listener#callSessionHeld},
+ * {@link ImsCallSession.Listener#callSessionHoldFailed}
+ */
+ @Override
+ public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link ImsCallSession.Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+ * @see {@link ImsCallSession.Listener#callSessionResumed},
+ * {@link ImsCallSession.Listener#callSessionResumeFailed}
+ */
+ @Override
+ public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Merges the active & hold call. When the merge starts,
+ * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+ * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+ * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+ * fails.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+ * {@link ImsCallSession.Listener#callSessionMergeComplete},
+ * {@link ImsCallSession.Listener#callSessionMergeFailed}
+ */
+ @Override
+ public void merge() throws RemoteException {
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see {@link ImsCallSession.Listener#callSessionUpdated},
+ * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+ */
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants participant list to be invited to the conference call after extending the
+ * call
+ * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+ * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+ */
+ @Override
+ public void extendToConference(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants participant list to be invited to the conference call
+ * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+ */
+ @Override
+ public void inviteParticipants(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+ */
+ @Override
+ public void removeParticipants(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void sendDtmf(char c, Message result) throws RemoteException {
+ }
+
+ /**
+ * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void startDtmf(char c) throws RemoteException {
+ }
+
+ /**
+ * Stop a DTMF code.
+ */
+ @Override
+ public void stopDtmf() throws RemoteException {
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ @Override
+ public void sendUssd(String ussdMessage) throws RemoteException {
+ }
+
+ /**
+ * Returns a binder for the video call provider implementation contained within the IMS service
+ * process. This binder is used by the VideoCallProvider subclass in Telephony which
+ * intermediates between the propriety implementation and Telecomm/InCall.
+ */
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Determines if the current session is multiparty.
+ * @return {@code True} if the session is multiparty.
+ */
+ @Override
+ public boolean isMultiparty() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Device issues RTT modify request
+ * @param toProfile The profile with requested changes made
+ */
+ @Override
+ public void sendRttModifyRequest(ImsCallProfile toProfile) {
+ }
+
+ /**
+ * Device responds to Remote RTT modify request
+ * @param status true Accepted the request
+ * false Declined the request
+ */
+ @Override
+ public void sendRttModifyResponse(boolean status) {
+ }
+
+ /**
+ * Device sends RTT message
+ * @param rttMessage RTT message to be sent
+ */
+ @Override
+ public void sendRttMessage(String rttMessage) {
+ }
+}
diff --git a/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
new file mode 100644
index 00000000..6c18935d
--- /dev/null
+++ b/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+
+/**
+ * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
+ * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
+ * ImsCallSessionListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ @Override
+ public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of call merge operation.
+ */
+ @Override
+ public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionMergeComplete(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call
+ * updates.
+ */
+ @Override
+ public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession session,
+ IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from the
+ * conference session.
+ */
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the changes of the conference info. the conference session.
+ */
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession session,
+ ImsConferenceState state) {
+ // no-op
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
+ String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
+ * handover from one radio technology to another.
+ * @param session
+ * @param srcAccessTech The source radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For
+ * example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @param targetAccessTech The target radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For
+ * example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ */
+ @Override
+ public void callSessionMayHandover(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech) {
+ // no-op
+ }
+
+ /**
+ * Notifies of handover information for this call
+ */
+ @Override
+ public void callSessionHandover(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the TTY mode change by remote party.
+ *
+ * @param mode one of the following: -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this
+ * {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty,
+ * {@code false} otherwise.
+ */
+ @Override
+ public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
+ // no-op
+ }
+
+ /**
+ * Notifies the supplementary service information for the current session.
+ */
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession session,
+ ImsSuppServiceNotification suppSrvNotification) {
+ // no-op
+ }
+
+ /**
+ * Received RTT modify request from Remote Party
+ * @param session The call session.
+ * @param callProfile ImsCallProfile with updated attribute
+ */
+ @Override
+ public void callSessionRttModifyRequestReceived(IImsCallSession session,
+ ImsCallProfile callProfile) {
+ // no-op
+ }
+
+ /**
+ * Received response for RTT modify request
+ * @param status true : Accepted the request
+ * false : Declined the request
+ */
+ @Override
+ public void callSessionRttModifyResponseReceived(int status) {
+ // no -op
+ }
+
+ /**
+ * Device received RTT message from Remote UE
+ * @param rttMessage RTT message received
+ */
+ @Override
+ public void callSessionRttMessageReceived(String rttMessage) {
+ // no-op
+ }
+}
+
diff --git a/android/telephony/ims/stub/ImsConfigImplBase.java b/android/telephony/ims/stub/ImsConfigImplBase.java
new file mode 100644
index 00000000..5a4db99e
--- /dev/null
+++ b/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Base implementation of ImsConfig, which implements stub versions of the methods
+ * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+
+public class ImsConfigImplBase extends IImsConfig.Stub {
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ @Override
+ public int getProvisionedValue(int item) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ @Override
+ public String getProvisionedStringValue(int item) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public int setProvisionedValue(int item, int value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public int setProvisionedStringValue(int item, String value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Gets the value of the specified IMS feature item for specified network type.
+ * This operation gets the feature config value from the master storage (i.e. final
+ * value). Asynchronous non-blocking call.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener feature value returned asynchronously through listener.
+ */
+ @Override
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ * This operation stores the user setting in setting db from which master db
+ * is derived.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ @Override
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Gets the value for IMS VoLTE provisioned.
+ * This should be the same as the operator provisioned value if applies.
+ */
+ @Override
+ public boolean getVolteProvisioned() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Gets the value for IMS feature item video quality.
+ *
+ * @param listener Video quality value returned asynchronously through listener.
+ */
+ @Override
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item video quality.
+ *
+ * @param quality, defines the value of video quality.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ @Override
+ public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+ }
+}
diff --git a/android/telephony/ims/stub/ImsEcbmImplBase.java b/android/telephony/ims/stub/ImsEcbmImplBase.java
new file mode 100644
index 00000000..89f95ff0
--- /dev/null
+++ b/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsEcbmListener;
+
+/**
+ * Base implementation of ImsEcbm, which implements stub versions of the methods
+ * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsEcbm maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsEcbmImplBase extends IImsEcbm.Stub {
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsEcbmListener listener) throws RemoteException {
+
+ }
+
+ /**
+ * Requests Modem to come out of ECBM mode
+ */
+ @Override
+ public void exitEmergencyCallbackMode() throws RemoteException {
+
+ }
+}
diff --git a/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
new file mode 100644
index 00000000..05da9da4
--- /dev/null
+++ b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+import com.android.ims.internal.IImsMultiEndpoint;
+
+/**
+ * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
+ * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
+ * ImsMultiEndpoint supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsMultiEndpoint maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+
+ }
+
+ /**
+ * Query API to get the latest Dialog Event Package information
+ * Should be invoked only after setListener is done
+ */
+ @Override
+ public void requestImsExternalCallStateInfo() throws RemoteException {
+
+ }
+}
diff --git a/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
new file mode 100644
index 00000000..92f1a018
--- /dev/null
+++ b/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsStreamMediaSession;
+
+/**
+ * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods
+ * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of
+ * ImsStreamMediaSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsStreamMediaSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub {
+
+ @Override
+ public void close() throws RemoteException {
+
+ }
+}
diff --git a/android/telephony/ims/stub/ImsUtImplBase.java b/android/telephony/ims/stub/ImsUtImplBase.java
new file mode 100644
index 00000000..dc74094d
--- /dev/null
+++ b/android/telephony/ims/stub/ImsUtImplBase.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUt, which implements stub versions of the methods
+ * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUt maintained by other ImsServices.
+ *
+ * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ *
+ * @hide
+ */
+
+public class ImsUtImplBase extends IImsUt.Stub {
+
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() throws RemoteException {
+
+ }
+
+ /**
+ * Retrieves the configuration of the call barring.
+ */
+ @Override
+ public int queryCallBarring(int cbType) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call forward.
+ */
+ @Override
+ public int queryCallForward(int condition, String number) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ */
+ @Override
+ public int queryCallWaiting() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ @Override
+ public int queryCLIR() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ @Override
+ public int queryCLIP() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ @Override
+ public int queryCOLR() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ @Override
+ public int queryCOLP() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates or retrieves the supplementary service configuration.
+ */
+ @Override
+ public int transact(Bundle ssInfo) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call barring.
+ */
+ @Override
+ public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call forward.
+ */
+ @Override
+ public int updateCallForward(int action, int condition, String number, int serviceClass,
+ int timeSeconds) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Updates the configuration of the call waiting.
+ */
+ @Override
+ public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ @Override
+ public int updateCLIR(int clirMode) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ @Override
+ public int updateCLIP(boolean enable) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ @Override
+ public int updateCOLR(int presentation) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ @Override
+ public int updateCOLP(boolean enable) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsUtListener listener) throws RemoteException {
+ }
+}
diff --git a/android/telephony/ims/stub/ImsUtListenerImplBase.java b/android/telephony/ims/stub/ImsUtListenerImplBase.java
new file mode 100644
index 00000000..b371efb6
--- /dev/null
+++ b/android/telephony/ims/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsSsInfo;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+ /**
+ * Notifies the result of the supplementary service configuration udpate.
+ */
+ @Override
+ public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the result of the supplementary service configuration query.
+ */
+ @Override
+ public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call barring supplementary service.
+ */
+ @Override
+ public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call forwarding supplementary service.
+ */
+ @Override
+ public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call waiting supplementary service.
+ */
+ @Override
+ public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+ throws RemoteException {
+ }
+}
diff --git a/android/telephony/mbms/DownloadProgressListener.java b/android/telephony/mbms/DownloadProgressListener.java
new file mode 100644
index 00000000..d91e9ad2
--- /dev/null
+++ b/android/telephony/mbms/DownloadProgressListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.RemoteException;
+
+/**
+ * A optional listener class used by download clients to track progress.
+ * @hide
+ */
+public class DownloadProgressListener extends IDownloadProgressListener.Stub {
+ /**
+ * Gives process callbacks for a given DownloadRequest.
+ * This is optionally specified when requesting a download and
+ * only lives while the app is running - it's unlikely to be useful for
+ * downloads far in the future.
+ *
+ * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+ * @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
+ * the request may result in many files being downloaded and the client
+ * may not have been able to get a list of them in advance.
+ * @param currentDownloadSize is the current amount downloaded.
+ * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
+ * This may be different from the decoded final size, but is useful in gauging download
+ * progress.
+ * @param currentDecodedSize is the number of bytes that have been decoded.
+ * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
+ */
+ @Override
+ public void progress(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize,
+ int currentDecodedSize, int fullDecodedSize) throws RemoteException {
+ }
+}
diff --git a/android/telephony/mbms/DownloadRequest.java b/android/telephony/mbms/DownloadRequest.java
new file mode 100644
index 00000000..eae9011e
--- /dev/null
+++ b/android/telephony/mbms/DownloadRequest.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Base64;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+
+/**
+ * A Parcelable class describing a pending Cell-Broadcast download request
+ * @hide
+ */
+public class DownloadRequest implements Parcelable {
+ // Version code used to keep token calculation consistent.
+ private static final int CURRENT_VERSION = 1;
+ private static final String LOG_TAG = "MbmsDownloadRequest";
+
+ /**
+ * Maximum permissible length for the app's download-completion intent, when serialized via
+ * {@link Intent#toUri(int)}.
+ */
+ public static final int MAX_APP_INTENT_SIZE = 50000;
+
+ /**
+ * Maximum permissible length for the app's destination path, when serialized via
+ * {@link Uri#toString()}.
+ */
+ public static final int MAX_DESTINATION_URI_SIZE = 50000;
+
+ /** @hide */
+ private static class OpaqueDataContainer implements Serializable {
+ private final String destinationUri;
+ private final String appIntent;
+ private final int version;
+
+ public OpaqueDataContainer(String destinationUri, String appIntent, int version) {
+ this.destinationUri = destinationUri;
+ this.appIntent = appIntent;
+ this.version = version;
+ }
+ }
+
+ public static class Builder {
+ private String fileServiceId;
+ private Uri source;
+ private Uri dest;
+ private int subscriptionId;
+ private String appIntent;
+ private int version = CURRENT_VERSION;
+
+ /**
+ * Sets the service from which the download request to be built will download from.
+ * @param serviceInfo
+ * @return
+ */
+ public Builder setServiceInfo(FileServiceInfo serviceInfo) {
+ fileServiceId = serviceInfo.getServiceId();
+ return this;
+ }
+
+ /**
+ * Set the service ID for the download request. For use by the middleware only.
+ * @hide
+ * TODO: systemapi
+ */
+ public Builder setServiceId(String serviceId) {
+ fileServiceId = serviceId;
+ return this;
+ }
+
+ /**
+ * Sets the source URI for the download request to be built.
+ * @param source
+ * @return
+ */
+ public Builder setSource(Uri source) {
+ this.source = source;
+ return this;
+ }
+
+ /**
+ * Sets the destination URI for the download request to be built. The middleware should
+ * not set this directly.
+ * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
+ * final destination of the download.
+ * @return
+ */
+ public Builder setDest(Uri dest) {
+ if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
+ throw new IllegalArgumentException("Destination uri must not exceed length " +
+ MAX_DESTINATION_URI_SIZE);
+ }
+ this.dest = dest;
+ return this;
+ }
+
+ /**
+ * Set the subscription ID on which the file(s) should be downloaded.
+ * @param subscriptionId
+ * @return
+ */
+ public Builder setSubscriptionId(int subscriptionId) {
+ this.subscriptionId = subscriptionId;
+ return this;
+ }
+
+ /**
+ * Set the {@link Intent} that should be sent when the download completes or fails. This
+ * should be an intent with a explicit {@link android.content.ComponentName} targeted to a
+ * {@link android.content.BroadcastReceiver} in the app's package.
+ *
+ * The middleware should not use this method.
+ * @param intent
+ * @return
+ */
+ public Builder setAppIntent(Intent intent) {
+ this.appIntent = intent.toUri(0);
+ if (this.appIntent.length() > MAX_APP_INTENT_SIZE) {
+ throw new IllegalArgumentException("App intent must not exceed length " +
+ MAX_APP_INTENT_SIZE);
+ }
+ return this;
+ }
+
+ /**
+ * For use by the middleware to set the byte array of opaque data. The opaque data
+ * includes information about the download request that is used by the client app and the
+ * manager code, but is irrelevant to the middleware.
+ * @param data A byte array, the contents of which should have been originally obtained
+ * from {@link DownloadRequest#getOpaqueData()}.
+ * @return
+ * TODO: systemapi
+ * @hide
+ */
+ public Builder setOpaqueData(byte[] data) {
+ try {
+ ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+ OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
+ version = dataContainer.version;
+ appIntent = dataContainer.appIntent;
+ dest = Uri.parse(dataContainer.destinationUri);
+ } catch (IOException e) {
+ // Really should never happen
+ Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+ throw new IllegalArgumentException(e);
+ } catch (ClassNotFoundException e) {
+ Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+ throw new IllegalArgumentException(e);
+ }
+ return this;
+ }
+
+ public DownloadRequest build() {
+ return new DownloadRequest(fileServiceId, source, dest,
+ subscriptionId, appIntent, version);
+ }
+ }
+
+ private final String fileServiceId;
+ private final Uri sourceUri;
+ private final Uri destinationUri;
+ private final int subscriptionId;
+ private final String serializedResultIntentForApp;
+ private final int version;
+
+ private DownloadRequest(String fileServiceId,
+ Uri source, Uri dest,
+ int sub, String appIntent, int version) {
+ this.fileServiceId = fileServiceId;
+ sourceUri = source;
+ destinationUri = dest;
+ subscriptionId = sub;
+ serializedResultIntentForApp = appIntent;
+ this.version = version;
+ }
+
+ public static DownloadRequest copy(DownloadRequest other) {
+ return new DownloadRequest(other);
+ }
+
+ private DownloadRequest(DownloadRequest dr) {
+ fileServiceId = dr.fileServiceId;
+ sourceUri = dr.sourceUri;
+ destinationUri = dr.destinationUri;
+ subscriptionId = dr.subscriptionId;
+ serializedResultIntentForApp = dr.serializedResultIntentForApp;
+ version = dr.version;
+ }
+
+ private DownloadRequest(Parcel in) {
+ fileServiceId = in.readString();
+ sourceUri = in.readParcelable(getClass().getClassLoader());
+ destinationUri = in.readParcelable(getClass().getClassLoader());
+ subscriptionId = in.readInt();
+ serializedResultIntentForApp = in.readString();
+ version = in.readInt();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(fileServiceId);
+ out.writeParcelable(sourceUri, flags);
+ out.writeParcelable(destinationUri, flags);
+ out.writeInt(subscriptionId);
+ out.writeString(serializedResultIntentForApp);
+ out.writeInt(version);
+ }
+
+ /**
+ * @return The ID of the file service to download from.
+ */
+ public String getFileServiceId() {
+ return fileServiceId;
+ }
+
+ /**
+ * @return The source URI to download from
+ */
+ public Uri getSourceUri() {
+ return sourceUri;
+ }
+
+ /**
+ * For use by the client app only.
+ * @return The URI of the final destination of the download.
+ */
+ public Uri getDestinationUri() {
+ return destinationUri;
+ }
+
+ /**
+ * @return The subscription ID on which to perform MBMS operations.
+ */
+ public int getSubscriptionId() {
+ return subscriptionId;
+ }
+
+ /**
+ * For internal use -- returns the intent to send to the app after download completion or
+ * failure.
+ * @hide
+ */
+ public Intent getIntentForApp() {
+ try {
+ return Intent.parseUri(serializedResultIntentForApp, 0);
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+ /**
+ * For use by the middleware only. The byte array returned from this method should be
+ * persisted and sent back to the app upon download completion or failure by passing it into
+ * {@link Builder#setOpaqueData(byte[])}.
+ * @return A byte array of opaque data to persist.
+ * @hide
+ * TODO: systemapi
+ */
+ public byte[] getOpaqueData() {
+ try {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
+ OpaqueDataContainer container = new OpaqueDataContainer(
+ destinationUri.toString(), serializedResultIntentForApp, version);
+ stream.writeObject(container);
+ stream.flush();
+ return byteArrayOutputStream.toByteArray();
+ } catch (IOException e) {
+ // Really should never happen
+ Log.e(LOG_TAG, "Got IOException trying to serialize opaque data");
+ return null;
+ }
+ }
+
+ /** @hide */
+ public int getVersion() {
+ return version;
+ }
+
+ public static final Parcelable.Creator<DownloadRequest> CREATOR =
+ new Parcelable.Creator<DownloadRequest>() {
+ public DownloadRequest createFromParcel(Parcel in) {
+ return new DownloadRequest(in);
+ }
+ public DownloadRequest[] newArray(int size) {
+ return new DownloadRequest[size];
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public boolean isMultipartDownload() {
+ // TODO: figure out what qualifies a request as a multipart download request.
+ return getSourceUri().getLastPathSegment() != null &&
+ getSourceUri().getLastPathSegment().contains("*");
+ }
+
+ /**
+ * Retrieves the hash string that should be used as the filename when storing a token for
+ * this DownloadRequest.
+ * @hide
+ */
+ public String getHash() {
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Could not get sha256 hash object");
+ }
+ if (version >= 1) {
+ // Hash the source URI, destination URI, and the app intent
+ digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
+ digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
+ digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+ }
+ // Add updates for future versions here
+ return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof DownloadRequest)) {
+ return false;
+ }
+ DownloadRequest request = (DownloadRequest) o;
+ return subscriptionId == request.subscriptionId &&
+ version == request.version &&
+ Objects.equals(fileServiceId, request.fileServiceId) &&
+ Objects.equals(sourceUri, request.sourceUri) &&
+ Objects.equals(destinationUri, request.destinationUri) &&
+ Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fileServiceId, sourceUri, destinationUri,
+ subscriptionId, serializedResultIntentForApp, version);
+ }
+}
diff --git a/android/telephony/mbms/FileInfo.java b/android/telephony/mbms/FileInfo.java
new file mode 100644
index 00000000..f97131dd
--- /dev/null
+++ b/android/telephony/mbms/FileInfo.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A Parcelable class Cell-Broadcast downloadable file information.
+ * @hide
+ */
+public class FileInfo implements Parcelable {
+
+ /**
+ * The URI into the carriers infrastructure which points to this file.
+ * This is used internally but is also one of the few pieces of data about the content that is
+ * exposed and may be needed for disambiguation by the application.
+ */
+ private final Uri uri;
+
+ /**
+ * The mime type of the content.
+ */
+ private final String mimeType;
+
+ public static final Parcelable.Creator<FileInfo> CREATOR =
+ new Parcelable.Creator<FileInfo>() {
+ @Override
+ public FileInfo createFromParcel(Parcel source) {
+ return new FileInfo(source);
+ }
+
+ @Override
+ public FileInfo[] newArray(int size) {
+ return new FileInfo[size];
+ }
+ };
+
+ /**
+ * @hide
+ * TODO: systemapi
+ */
+ public FileInfo(Uri uri, String mimeType) {
+ this.uri = uri;
+ this.mimeType = mimeType;
+ }
+
+ private FileInfo(Parcel in) {
+ uri = in.readParcelable(null);
+ mimeType = in.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(uri, flags);
+ dest.writeString(mimeType);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public Uri getUri() {
+ return uri;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+}
diff --git a/android/telephony/mbms/FileServiceInfo.java b/android/telephony/mbms/FileServiceInfo.java
new file mode 100644
index 00000000..8afe4d3c
--- /dev/null
+++ b/android/telephony/mbms/FileServiceInfo.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * A Parcelable class Cell-Broadcast downloadable file information.
+ * @hide
+ */
+public class FileServiceInfo extends ServiceInfo implements Parcelable {
+ private final List<FileInfo> files;
+
+ /** @hide TODO: systemapi */
+ public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
+ List<Locale> newLocales, String newServiceId, Date start, Date end,
+ List<FileInfo> newFiles) {
+ super(newNames, newClassName, newLocales, newServiceId, start, end);
+ files = new ArrayList<>(newFiles);
+ }
+
+ public static final Parcelable.Creator<FileServiceInfo> CREATOR =
+ new Parcelable.Creator<FileServiceInfo>() {
+ @Override
+ public FileServiceInfo createFromParcel(Parcel source) {
+ return new FileServiceInfo(source);
+ }
+
+ @Override
+ public FileServiceInfo[] newArray(int size) {
+ return new FileServiceInfo[size];
+ }
+ };
+
+ FileServiceInfo(Parcel in) {
+ super(in);
+ files = new ArrayList<FileInfo>();
+ in.readList(files, null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeList(files);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public List<FileInfo> getFiles() {
+ return files;
+ }
+
+}
diff --git a/android/telephony/mbms/InternalStreamingManagerCallback.java b/android/telephony/mbms/InternalStreamingManagerCallback.java
new file mode 100644
index 00000000..b52df8c0
--- /dev/null
+++ b/android/telephony/mbms/InternalStreamingManagerCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+
+import java.util.List;
+
+/** @hide */
+public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
+ private final Handler mHandler;
+ private final MbmsStreamingManagerCallback mAppCallback;
+
+ public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback,
+ Handler handler) {
+ mAppCallback = appCallback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onError(errorCode, message);
+ }
+ });
+ }
+
+ @Override
+ public void streamingServicesUpdated(List<StreamingServiceInfo> services)
+ throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStreamingServicesUpdated(services);
+ }
+ });
+ }
+
+ @Override
+ public void middlewareReady() throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onMiddlewareReady();
+ }
+ });
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+}
diff --git a/android/telephony/mbms/InternalStreamingServiceCallback.java b/android/telephony/mbms/InternalStreamingServiceCallback.java
new file mode 100644
index 00000000..bb337b27
--- /dev/null
+++ b/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+/** @hide */
+public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
+ private final StreamingServiceCallback mAppCallback;
+ private final Handler mHandler;
+
+ public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+ mAppCallback = appCallback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onError(errorCode, message);
+ }
+ });
+ }
+
+ @Override
+ public void streamStateUpdated(int state, int reason) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStreamStateUpdated(state, reason);
+ }
+ });
+ }
+
+ @Override
+ public void mediaDescriptionUpdated() throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onMediaDescriptionUpdated();
+ }
+ });
+ }
+
+ @Override
+ public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+ }
+ });
+ }
+
+ @Override
+ public void streamMethodUpdated(int methodType) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStreamMethodUpdated(methodType);
+ }
+ });
+ }
+}
diff --git a/android/telephony/mbms/MbmsDownloadManagerCallback.java b/android/telephony/mbms/MbmsDownloadManagerCallback.java
new file mode 100644
index 00000000..17291d09
--- /dev/null
+++ b/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadManager;
+
+import java.util.List;
+
+/**
+ * A Parcelable class with Cell-Broadcast service information.
+ * @hide
+ */
+public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
+
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate published File Services have changed.
+ *
+ * This will only be called after the application has requested
+ * a list of file services and specified a service class list
+ * of interest AND the results of a subsequent getFileServices
+ * call with the same service class list would return different
+ * results.
+ *
+ * @param services a List of FileServiceInfos
+ *
+ */
+ @Override
+ public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
+ * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+ * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
+ @Override
+ public void middlewareReady() throws RemoteException {
+ // default implementation empty
+ }
+}
diff --git a/android/telephony/mbms/MbmsDownloadReceiver.java b/android/telephony/mbms/MbmsDownloadReceiver.java
new file mode 100644
index 00000000..ba7d120a
--- /dev/null
+++ b/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.MbmsDownloadManager;
+import android.telephony.mbms.vendor.VendorIntents;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+public class MbmsDownloadReceiver extends BroadcastReceiver {
+ public static final String DOWNLOAD_TOKEN_SUFFIX = ".download_token";
+ public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
+
+ /**
+ * TODO: @SystemApi all these result codes
+ * Indicates that the requested operation completed without error.
+ */
+ public static final int RESULT_OK = 0;
+
+ /**
+ * Indicates that the intent sent had an invalid action. This will be the result if
+ * {@link Intent#getAction()} returns anything other than
+ * {@link VendorIntents#ACTION_DOWNLOAD_RESULT_INTERNAL},
+ * {@link VendorIntents#ACTION_FILE_DESCRIPTOR_REQUEST}, or
+ * {@link VendorIntents#ACTION_CLEANUP}.
+ * This is a fatal result code and no result extras should be expected.
+ */
+ public static final int RESULT_INVALID_ACTION = 1;
+
+ /**
+ * Indicates that the intent was missing some required extras.
+ * This is a fatal result code and no result extras should be expected.
+ */
+ public static final int RESULT_MALFORMED_INTENT = 2;
+
+ /**
+ * Indicates that the supplied value for {@link VendorIntents#EXTRA_TEMP_FILE_ROOT}
+ * does not match what the app has stored.
+ * This is a fatal result code and no result extras should be expected.
+ */
+ public static final int RESULT_BAD_TEMP_FILE_ROOT = 3;
+
+ /**
+ * Indicates that the manager was unable to move the completed download to its final location.
+ * This is a fatal result code and no result extras should be expected.
+ */
+ public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4;
+
+ /**
+ * Indicates that the manager was unable to generate one or more of the requested file
+ * descriptors.
+ * This is a non-fatal result code -- some file descriptors may still be generated, but there
+ * is no guarantee that they will be the same number as requested.
+ */
+ public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5;
+
+ private static final String LOG_TAG = "MbmsDownloadReceiver";
+ private static final String TEMP_FILE_SUFFIX = ".embms.temp";
+ private static final int MAX_TEMP_FILE_RETRIES = 5;
+
+
+ private String mFileProviderAuthorityCache = null;
+ private String mMiddlewarePackageNameCache = null;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!verifyIntentContents(context, intent)) {
+ setResultCode(RESULT_MALFORMED_INTENT);
+ return;
+ }
+ if (!Objects.equals(intent.getStringExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT),
+ MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
+ setResultCode(RESULT_BAD_TEMP_FILE_ROOT);
+ return;
+ }
+
+ if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+ moveDownloadedFile(context, intent);
+ cleanupPostMove(context, intent);
+ } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+ generateTempFiles(context, intent);
+ } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
+ cleanupTempFiles(context, intent);
+ } else {
+ setResultCode(RESULT_INVALID_ACTION);
+ }
+ }
+
+ private boolean verifyIntentContents(Context context, Intent intent) {
+ if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+ if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) {
+ Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(VendorIntents.EXTRA_REQUEST)) {
+ Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+ Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) {
+ Log.w(LOG_TAG, "Download result did not include the associated file info. " +
+ "Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(VendorIntents.EXTRA_FINAL_URI)) {
+ Log.w(LOG_TAG, "Download result did not include the path to the final " +
+ "temp file. Ignoring.");
+ return false;
+ }
+ DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+ String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
+ File expectedTokenFile = new File(
+ MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
+ expectedTokenFileName);
+ if (!expectedTokenFile.exists()) {
+ Log.w(LOG_TAG, "Supplied download request does not match a token that we have. " +
+ "Expected " + expectedTokenFile);
+ return false;
+ }
+ } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
+ Log.w(LOG_TAG, "Temp file request did not include the associated service info." +
+ " Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+ Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+ return false;
+ }
+ } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
+ Log.w(LOG_TAG, "Cleanup request did not include the associated service info." +
+ " Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+ Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE)) {
+ Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " +
+ "Ignoring.");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void moveDownloadedFile(Context context, Intent intent) {
+ DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+ Intent intentForApp = request.getIntentForApp();
+
+ int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT,
+ MbmsDownloadManager.RESULT_CANCELLED);
+ intentForApp.putExtra(MbmsDownloadManager.EXTRA_RESULT, result);
+
+ if (result != MbmsDownloadManager.RESULT_SUCCESSFUL) {
+ Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
+ context.sendBroadcast(intentForApp);
+ return;
+ }
+
+ Uri destinationUri = request.getDestinationUri();
+ Uri finalTempFile = intent.getParcelableExtra(VendorIntents.EXTRA_FINAL_URI);
+ if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
+ Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
+ setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
+ return;
+ }
+
+ FileInfo completedFileInfo =
+ (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO);
+ String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);
+
+ Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
+ if (finalFileLocation == null) {
+ Log.w(LOG_TAG, "Failed to move temp file to final destination");
+ setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
+ return;
+ }
+ intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
+ intentForApp.putExtra(MbmsDownloadManager.EXTRA_FILE_INFO, completedFileInfo);
+
+ context.sendBroadcast(intentForApp);
+ setResultCode(RESULT_OK);
+ }
+
+ private void cleanupPostMove(Context context, Intent intent) {
+ DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+ if (request == null) {
+ Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
+ return;
+ }
+
+ List<Uri> tempFiles = intent.getParcelableExtra(VendorIntents.EXTRA_TEMP_LIST);
+ if (tempFiles == null) {
+ return;
+ }
+
+ for (Uri tempFileUri : tempFiles) {
+ if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
+ File tempFile = new File(tempFileUri.getSchemeSpecificPart());
+ tempFile.delete();
+ }
+ }
+ }
+
+ private void generateTempFiles(Context context, Intent intent) {
+ FileServiceInfo serviceInfo =
+ intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
+ if (serviceInfo == null) {
+ Log.w(LOG_TAG, "Temp file request did not include the associated service info. " +
+ "Ignoring.");
+ setResultCode(RESULT_MALFORMED_INTENT);
+ return;
+ }
+ int fdCount = intent.getIntExtra(VendorIntents.EXTRA_FD_COUNT, 0);
+ List<Uri> pausedList = intent.getParcelableExtra(VendorIntents.EXTRA_PAUSED_LIST);
+
+ if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
+ Log.i(LOG_TAG, "No temp files actually requested. Ending.");
+ setResultCode(RESULT_OK);
+ setResultExtras(Bundle.EMPTY);
+ return;
+ }
+
+ ArrayList<UriPathPair> freshTempFiles =
+ generateFreshTempFiles(context, serviceInfo, fdCount);
+ ArrayList<UriPathPair> pausedFiles =
+ generateUrisForPausedFiles(context, serviceInfo, pausedList);
+
+ Bundle result = new Bundle();
+ result.putParcelableArrayList(VendorIntents.EXTRA_FREE_URI_LIST, freshTempFiles);
+ result.putParcelableArrayList(VendorIntents.EXTRA_PAUSED_URI_LIST, pausedFiles);
+ setResultCode(RESULT_OK);
+ setResultExtras(result);
+ }
+
+ private ArrayList<UriPathPair> generateFreshTempFiles(Context context,
+ FileServiceInfo serviceInfo,
+ int freshFdCount) {
+ File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
+ serviceInfo.getServiceId());
+ if (!tempFileDir.exists()) {
+ tempFileDir.mkdirs();
+ }
+
+ // Name the files with the template "N-UUID", where N is the request ID and UUID is a
+ // random uuid.
+ ArrayList<UriPathPair> result = new ArrayList<>(freshFdCount);
+ for (int i = 0; i < freshFdCount; i++) {
+ File tempFile = generateSingleTempFile(tempFileDir);
+ if (tempFile == null) {
+ setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
+ Log.w(LOG_TAG, "Failed to generate a temp file. Moving on.");
+ continue;
+ }
+ Uri fileUri = Uri.fromFile(tempFile);
+ Uri contentUri = MbmsTempFileProvider.getUriForFile(
+ context, getFileProviderAuthorityCached(context), tempFile);
+ context.grantUriPermission(getMiddlewarePackageCached(context), contentUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ result.add(new UriPathPair(fileUri, contentUri));
+ }
+
+ return result;
+ }
+
+ private static File generateSingleTempFile(File tempFileDir) {
+ int numTries = 0;
+ while (numTries < MAX_TEMP_FILE_RETRIES) {
+ numTries++;
+ String fileName = UUID.randomUUID() + TEMP_FILE_SUFFIX;
+ File tempFile = new File(tempFileDir, fileName);
+ try {
+ if (tempFile.createNewFile()) {
+ return tempFile.getCanonicalFile();
+ }
+ } catch (IOException e) {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context,
+ FileServiceInfo serviceInfo, List<Uri> pausedFiles) {
+ if (pausedFiles == null) {
+ return new ArrayList<>(0);
+ }
+ ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size());
+
+ for (Uri fileUri : pausedFiles) {
+ if (!verifyTempFilePath(context, serviceInfo.getServiceId(), fileUri)) {
+ Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume");
+ setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
+ continue;
+ }
+ File tempFile = new File(fileUri.getSchemeSpecificPart());
+ if (!tempFile.exists()) {
+ Log.w(LOG_TAG, "Supplied file " + fileUri + " does not exist.");
+ setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
+ continue;
+ }
+ Uri contentUri = MbmsTempFileProvider.getUriForFile(
+ context, getFileProviderAuthorityCached(context), tempFile);
+ context.grantUriPermission(getMiddlewarePackageCached(context), contentUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ result.add(new UriPathPair(fileUri, contentUri));
+ }
+ return result;
+ }
+
+ private void cleanupTempFiles(Context context, Intent intent) {
+ FileServiceInfo serviceInfo =
+ intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
+ File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
+ serviceInfo.getServiceId());
+ final List<Uri> filesInUse =
+ intent.getParcelableArrayListExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE);
+ File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ File canonicalFile;
+ try {
+ canonicalFile = file.getCanonicalFile();
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Got IOException canonicalizing " + file + ", not deleting.");
+ return false;
+ }
+ // Reject all files that don't match what we think a temp file should look like
+ // e.g. download tokens
+ if (!canonicalFile.getName().endsWith(TEMP_FILE_SUFFIX)) {
+ return false;
+ }
+ // If any of the files in use match the uri, return false to reject it from the
+ // list to delete.
+ Uri fileInUseUri = Uri.fromFile(canonicalFile);
+ return !filesInUse.contains(fileInUseUri);
+ }
+ });
+ for (File fileToDelete : filesToDelete) {
+ fileToDelete.delete();
+ }
+ }
+
+ private static String calculateDestinationFileRelativePath(DownloadRequest request,
+ FileInfo info) {
+ List<String> filePathComponents = info.getUri().getPathSegments();
+ List<String> requestPathComponents = request.getSourceUri().getPathSegments();
+ Iterator<String> filePathIter = filePathComponents.iterator();
+ Iterator<String> requestPathIter = requestPathComponents.iterator();
+
+ StringBuilder pathBuilder = new StringBuilder();
+ // Iterate through the segments of the carrier's URI to the file, along with the segments
+ // of the source URI specified in the download request. The relative path is calculated
+ // as the tail of the file's URI that does not match the path segments in the source URI.
+ while (filePathIter.hasNext()) {
+ String currFilePathComponent = filePathIter.next();
+ if (requestPathIter.hasNext()) {
+ String requestFilePathComponent = requestPathIter.next();
+ if (requestFilePathComponent.equals(currFilePathComponent)) {
+ continue;
+ }
+ }
+ pathBuilder.append(currFilePathComponent);
+ pathBuilder.append('/');
+ }
+ // remove the trailing slash
+ if (pathBuilder.length() > 0) {
+ pathBuilder.deleteCharAt(pathBuilder.length() - 1);
+ }
+ return pathBuilder.toString();
+ }
+
+ /*
+ * Moves a tempfile located at fromPath to a new location at toPath. If
+ * toPath is a directory, the destination file will be located at relativePath
+ * underneath toPath.
+ */
+ private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
+ if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
+ Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
+ return null;
+ }
+ if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) {
+ Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme");
+ return null;
+ }
+
+ File fromFile = new File(fromPath.getSchemeSpecificPart());
+ File toFile = new File(toPath.getSchemeSpecificPart());
+ if (toFile.isDirectory()) {
+ toFile = new File(toFile, relativePath);
+ }
+ toFile.getParentFile().mkdirs();
+
+ if (fromFile.renameTo(toFile)) {
+ return Uri.fromFile(toFile);
+ } else if (manualMove(fromFile, toFile)) {
+ return Uri.fromFile(toFile);
+ }
+ return null;
+ }
+
+ private static boolean verifyTempFilePath(Context context, String serviceId,
+ Uri filePath) {
+ if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
+ Log.w(LOG_TAG, "Uri " + filePath + " does not have a file scheme");
+ return false;
+ }
+
+ String path = filePath.getSchemeSpecificPart();
+ File tempFile = new File(path);
+ if (!tempFile.exists()) {
+ Log.w(LOG_TAG, "File at " + path + " does not exist.");
+ return false;
+ }
+
+ if (!MbmsUtils.isContainedIn(
+ MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private String getFileProviderAuthorityCached(Context context) {
+ if (mFileProviderAuthorityCache != null) {
+ return mFileProviderAuthorityCache;
+ }
+
+ mFileProviderAuthorityCache = getFileProviderAuthority(context);
+ return mFileProviderAuthorityCache;
+ }
+
+ private static String getFileProviderAuthority(Context context) {
+ ApplicationInfo appInfo;
+ try {
+ appInfo = context.getPackageManager()
+ .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Package manager couldn't find " + context.getPackageName());
+ }
+ String authority = appInfo.metaData.getString(MBMS_FILE_PROVIDER_META_DATA_KEY);
+ if (authority == null) {
+ throw new RuntimeException("Must declare the file provider authority as meta data");
+ }
+ return authority;
+ }
+
+ private String getMiddlewarePackageCached(Context context) {
+ if (mMiddlewarePackageNameCache == null) {
+ mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context,
+ MbmsDownloadManager.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
+ }
+ return mMiddlewarePackageNameCache;
+ }
+
+ private static boolean manualMove(File src, File dst) {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ if (!dst.exists()) {
+ dst.createNewFile();
+ }
+ in = new FileInputStream(src);
+ out = new FileOutputStream(dst);
+ byte[] buffer = new byte[2048];
+ int len;
+ do {
+ len = in.read(buffer);
+ out.write(buffer, 0, len);
+ } while (len > 0);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Manual file move failed due to exception " + e);
+ if (dst.exists()) {
+ dst.delete();
+ }
+ return false;
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Error closing streams: " + e);
+ }
+ }
+ return true;
+ }
+}
diff --git a/android/telephony/mbms/MbmsException.java b/android/telephony/mbms/MbmsException.java
new file mode 100644
index 00000000..6de5a18e
--- /dev/null
+++ b/android/telephony/mbms/MbmsException.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+public class MbmsException extends Exception {
+ /** Indicates that the operation was successful. */
+ public static final int SUCCESS = 0;
+
+ // Following errors are generated in the manager and should not be returned from the
+ // middleware
+ /**
+ * Indicates that either no MBMS middleware app is installed on the device or multiple
+ * middleware apps are installed on the device.
+ */
+ public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1;
+
+ /**
+ * Indicates that the app attempted to perform an operation on an instance of
+ * TODO: link android.telephony.MbmsDownloadManager or
+ * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware.
+ */
+ public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
+
+ /** Indicates that the middleware has died and the requested operation was not completed.*/
+ public static final int ERROR_MIDDLEWARE_LOST = 3;
+
+ /**
+ * Indicates errors that may be generated during initialization by the
+ * middleware. They are applicable to both streaming and file-download use-cases.
+ */
+ public static class InitializationErrors {
+ private InitializationErrors() {}
+ /**
+ * Indicates that the app tried to create more than one instance each of
+ * {@link android.telephony.MbmsStreamingManager} or
+ * TODO: link android.telephony.MbmsDownloadManager
+ */
+ public static final int ERROR_DUPLICATE_INITIALIZE = 101;
+ /** Indicates that the app is not authorized to access media via MBMS.*/
+ public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102;
+ /** Indicates that the middleware was unable to initialize for this app. */
+ public static final int ERROR_UNABLE_TO_INITIALIZE = 103;
+ }
+
+ /**
+ * Indicates the errors that may occur at any point and are applicable to both
+ * streaming and file-download.
+ */
+ public static class GeneralErrors {
+ private GeneralErrors() {}
+ /**
+ * Indicates that the app attempted to perform an operation before receiving notification
+ * that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()}
+ * or TODO: link MbmsDownloadManagerCallback#middlewareReady
+ */
+ public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
+ /**
+ * Indicates that the middleware ran out of memory and was unable to complete the requested
+ * operation.
+ */
+ public static final int ERROR_OUT_OF_MEMORY = 202;
+ /**
+ * Indicates that the requested operation failed due to the middleware being unavailable due
+ * to a transient condition. The app may retry the operation at a later time.
+ */
+ public static final int ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 203;
+ /**
+ * Indicates that the requested operation was not performed due to being in emergency
+ * callback mode.
+ */
+ public static final int ERROR_IN_E911 = 204;
+ /** Indicates that MBMS is not available due to the device being in roaming. */
+ public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 205;
+ /** Indicates that MBMS is not available due to a SIM read error. */
+ public static final int ERROR_UNABLE_TO_READ_SIM = 206;
+ /**
+ * Indicates that MBMS is not available due to the inserted SIM being from an unsupported
+ * carrier.
+ */
+ public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207;
+ }
+
+ /**
+ * Indicates the errors that are applicable only to the streaming use-case
+ */
+ public static class StreamingErrors {
+ private StreamingErrors() {}
+ /** Indicates that the middleware cannot start a stream due to too many ongoing streams */
+ public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301;
+
+ /** Indicates that the middleware was unable to start the streaming service */
+ public static final int ERROR_UNABLE_TO_START_SERVICE = 302;
+
+ /**
+ * Indicates that the app called
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(
+ * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
+ * more than once for the same {@link StreamingServiceInfo}.
+ */
+ public static final int ERROR_DUPLICATE_START_STREAM = 303;
+ }
+
+ /**
+ * Indicates the errors that are applicable only to the file-download use-case
+ * TODO: unhide
+ * @hide
+ */
+ public static class DownloadErrors {
+ /**
+ * Indicates that the app is not allowed to change the temp file root at this time due to
+ * outstanding download requests.
+ */
+ public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401;
+
+ /** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */
+ public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
+ }
+
+ private final int mErrorCode;
+
+ /** @hide */
+ public MbmsException(int errorCode) {
+ super();
+ mErrorCode = errorCode;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+}
diff --git a/android/telephony/mbms/MbmsStreamingManagerCallback.java b/android/telephony/mbms/MbmsStreamingManagerCallback.java
new file mode 100644
index 00000000..b31ffa72
--- /dev/null
+++ b/android/telephony/mbms/MbmsStreamingManagerCallback.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.MbmsStreamingManager;
+
+import java.util.List;
+
+/**
+ * A callback class that is used to receive information from the middleware on MBMS streaming
+ * services. An instance of this object should be passed into
+ * {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}.
+ */
+public class MbmsStreamingManagerCallback {
+ /**
+ * Called by the middleware when it has detected an error condition. The possible error codes
+ * are listed in {@link MbmsException}.
+ * @param errorCode The error code.
+ * @param message A human-readable message generated by the middleware for debugging purposes.
+ */
+ public void onError(int errorCode, String message) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate published Streaming Services have changed.
+ *
+ * This will only be called after the application has requested
+ * a list of streaming services and specified a service class list
+ * of interest AND the results of a subsequent getStreamServices
+ * call with the same service class list would return different
+ * results.
+ *
+ * @param services a List of StreamingServiceInfos
+ *
+ */
+ public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException}
+ * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+ * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
+ public void onMiddlewareReady() {
+ // default implementation empty
+ }
+}
diff --git a/android/telephony/mbms/MbmsTempFileProvider.java b/android/telephony/mbms/MbmsTempFileProvider.java
new file mode 100644
index 00000000..c4d033bf
--- /dev/null
+++ b/android/telephony/mbms/MbmsTempFileProvider.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public class MbmsTempFileProvider extends ContentProvider {
+ public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+ public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
+ public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
+
+ private String mAuthority;
+ private Context mContext;
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs,
+ @Nullable String sortOrder) {
+ throw new UnsupportedOperationException("No querying supported");
+ }
+
+ @Override
+ public String getType(@NonNull Uri uri) {
+ // EMBMS temp files can contain arbitrary content.
+ return "application/octet-stream";
+ }
+
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ throw new UnsupportedOperationException("No inserting supported");
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException("No deleting supported");
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String
+ selection, @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException("No updating supported");
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ // ContentProvider has already checked granted permissions
+ final File file = getFileForUri(mContext, mAuthority, uri);
+ final int fileMode = ParcelFileDescriptor.parseMode(mode);
+ return ParcelFileDescriptor.open(file, fileMode);
+ }
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ super.attachInfo(context, info);
+
+ // Sanity check our security
+ if (info.exported) {
+ throw new SecurityException("Provider must not be exported");
+ }
+ if (!info.grantUriPermissions) {
+ throw new SecurityException("Provider must grant uri permissions");
+ }
+
+ mAuthority = info.authority;
+ mContext = context;
+ }
+
+ public static Uri getUriForFile(Context context, String authority, File file) {
+ // Get the canonical path of the temp file
+ String filePath;
+ try {
+ filePath = file.getCanonicalPath();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Could not get canonical path for file " + file);
+ }
+
+ // Make sure the temp file is contained in the temp file directory as configured in the
+ // manifest
+ File tempFileDir = getEmbmsTempFileDir(context);
+ if (!MbmsUtils.isContainedIn(tempFileDir, file)) {
+ throw new IllegalArgumentException("File " + file + " is not contained in the temp " +
+ "file directory, which is " + tempFileDir);
+ }
+
+ // Get the canonical path of the temp file directory
+ String tempFileDirPath;
+ try {
+ tempFileDirPath = tempFileDir.getCanonicalPath();
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "Could not get canonical path for temp file root dir " + tempFileDir);
+ }
+
+ // Start at first char of path under temp file directory
+ String pathFragment;
+ if (tempFileDirPath.endsWith("/")) {
+ pathFragment = filePath.substring(tempFileDirPath.length());
+ } else {
+ pathFragment = filePath.substring(tempFileDirPath.length() + 1);
+ }
+
+ String encodedPath = Uri.encode(pathFragment);
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).encodedPath(encodedPath).build();
+ }
+
+ public static File getFileForUri(Context context, String authority, Uri uri)
+ throws FileNotFoundException {
+ if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ throw new IllegalArgumentException("Uri must have scheme content");
+ }
+ if (!Objects.equals(authority, uri.getAuthority())) {
+ throw new IllegalArgumentException("Uri does not have a matching authority: " +
+ authority + ", " + uri.getAuthority());
+ }
+
+ String relPath = Uri.decode(uri.getEncodedPath());
+ File file;
+ File tempFileDir;
+
+ try {
+ tempFileDir = getEmbmsTempFileDir(context).getCanonicalFile();
+ file = new File(tempFileDir, relPath).getCanonicalFile();
+ } catch (IOException e) {
+ throw new FileNotFoundException("Could not resolve paths");
+ }
+
+ if (!file.getPath().startsWith(tempFileDir.getPath())) {
+ throw new SecurityException("Resolved path jumped beyond configured root");
+ }
+
+ return file;
+ }
+
+ /**
+ * Returns a File for the directory used to store temp files for this app
+ */
+ public static File getEmbmsTempFileDir(Context context) {
+ SharedPreferences prefs = context.getSharedPreferences(TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ String storedTempFileRoot = prefs.getString(TEMP_FILE_ROOT_PREF_NAME, null);
+ try {
+ if (storedTempFileRoot != null) {
+ return new File(storedTempFileRoot).getCanonicalFile();
+ } else {
+ return new File(context.getFilesDir(), DEFAULT_TOP_LEVEL_TEMP_DIRECTORY)
+ .getCanonicalFile();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to canonicalize temp file root path " + e);
+ }
+ }
+}
diff --git a/android/telephony/mbms/MbmsUtils.java b/android/telephony/mbms/MbmsUtils.java
new file mode 100644
index 00000000..4b913f82
--- /dev/null
+++ b/android/telephony/mbms/MbmsUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.*;
+import android.content.pm.ServiceInfo;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class MbmsUtils {
+ private static final String LOG_TAG = "MbmsUtils";
+
+ public static boolean isContainedIn(File parent, File child) {
+ try {
+ String parentPath = parent.getCanonicalPath();
+ String childPath = child.getCanonicalPath();
+ return childPath.startsWith(parentPath);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to resolve canonical paths: " + e);
+ }
+ }
+
+ public static ComponentName toComponentName(ComponentInfo ci) {
+ return new ComponentName(ci.packageName, ci.name);
+ }
+
+ public static ServiceInfo getMiddlewareServiceInfo(Context context, String serviceAction) {
+ // Query for the proper service
+ PackageManager packageManager = context.getPackageManager();
+ Intent queryIntent = new Intent();
+ queryIntent.setAction(serviceAction);
+ List<ResolveInfo> downloadServices = packageManager.queryIntentServices(queryIntent,
+ PackageManager.MATCH_SYSTEM_ONLY);
+
+ if (downloadServices == null || downloadServices.size() == 0) {
+ Log.w(LOG_TAG, "No download services found, cannot get service info");
+ return null;
+ }
+
+ if (downloadServices.size() > 1) {
+ Log.w(LOG_TAG, "More than one download service found, cannot get unique service");
+ return null;
+ }
+ return downloadServices.get(0).serviceInfo;
+ }
+
+ public static void startBinding(Context context, String serviceAction,
+ ServiceConnection serviceConnection) throws MbmsException {
+ Intent bindIntent = new Intent();
+ ServiceInfo mbmsServiceInfo =
+ MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
+
+ if (mbmsServiceInfo == null) {
+ throw new MbmsException(MbmsException.ERROR_NO_UNIQUE_MIDDLEWARE);
+ }
+
+ bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
+
+ context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Returns a File linked to the directory used to store temp files for this file service
+ */
+ public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
+ File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
+
+ return new File(embmsTempFileDir, serviceId);
+ }
+}
diff --git a/android/telephony/mbms/ServiceInfo.java b/android/telephony/mbms/ServiceInfo.java
new file mode 100644
index 00000000..c01604b0
--- /dev/null
+++ b/android/telephony/mbms/ServiceInfo.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Describes a cell-broadcast service. This class should not be instantiated directly -- use
+ * {@link StreamingServiceInfo} or FileServiceInfo TODO: add link once that's unhidden
+ */
+public class ServiceInfo {
+ // arbitrary limit on the number of locale -> name pairs we support
+ final static int MAP_LIMIT = 1000;
+
+ private final Map<Locale, String> names;
+ private final String className;
+ private final List<Locale> locales;
+ private final String serviceId;
+ private final Date sessionStartTime;
+ private final Date sessionEndTime;
+
+ /** @hide */
+ public ServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales,
+ String newServiceId, Date start, Date end) {
+ if (newNames == null || newNames.isEmpty() || TextUtils.isEmpty(newClassName)
+ || newLocales == null || newLocales.isEmpty() || TextUtils.isEmpty(newServiceId)
+ || start == null || end == null) {
+ throw new IllegalArgumentException("Bad ServiceInfo construction");
+ }
+ if (newNames.size() > MAP_LIMIT) {
+ throw new RuntimeException("bad map length " + newNames.size());
+ }
+ if (newLocales.size() > MAP_LIMIT) {
+ throw new RuntimeException("bad locales length " + newLocales.size());
+ }
+ names = new HashMap(newNames.size());
+ names.putAll(newNames);
+ className = newClassName;
+ locales = new ArrayList(newLocales);
+ serviceId = newServiceId;
+ sessionStartTime = (Date)start.clone();
+ sessionEndTime = (Date)end.clone();
+ }
+
+ /** @hide */
+ protected ServiceInfo(Parcel in) {
+ int mapCount = in.readInt();
+ if (mapCount > MAP_LIMIT || mapCount < 0) {
+ throw new RuntimeException("bad map length" + mapCount);
+ }
+ names = new HashMap(mapCount);
+ while (mapCount-- > 0) {
+ Locale locale = (java.util.Locale) in.readSerializable();
+ String name = in.readString();
+ names.put(locale, name);
+ }
+ className = in.readString();
+ int localesCount = in.readInt();
+ if (localesCount > MAP_LIMIT || localesCount < 0) {
+ throw new RuntimeException("bad locale length " + localesCount);
+ }
+ locales = new ArrayList<Locale>(localesCount);
+ while (localesCount-- > 0) {
+ Locale l = (java.util.Locale) in.readSerializable();
+ locales.add(l);
+ }
+ serviceId = in.readString();
+ sessionStartTime = (java.util.Date) in.readSerializable();
+ sessionEndTime = (java.util.Date) in.readSerializable();
+ }
+
+ /** @hide */
+ public void writeToParcel(Parcel dest, int flags) {
+ Set<Locale> keySet = names.keySet();
+ dest.writeInt(keySet.size());
+ for (Locale l : keySet) {
+ dest.writeSerializable(l);
+ dest.writeString(names.get(l));
+ }
+ dest.writeString(className);
+ int localesCount = locales.size();
+ dest.writeInt(localesCount);
+ for (Locale l : locales) {
+ dest.writeSerializable(l);
+ }
+ dest.writeString(serviceId);
+ dest.writeSerializable(sessionStartTime);
+ dest.writeSerializable(sessionEndTime);
+ }
+
+ /**
+ * User displayable names listed by language. Do not modify the map returned from this method.
+ */
+ public Map<Locale, String> getNames() {
+ return names;
+ }
+
+ /**
+ * The class name for this service - used to categorize and filter
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * The languages available for this service content
+ */
+ public List<Locale> getLocales() {
+ return locales;
+ }
+
+ /**
+ * The carrier's identifier for the service.
+ */
+ public String getServiceId() {
+ return serviceId;
+ }
+
+ /**
+ * The start time indicating when this service will be available.
+ */
+ public Date getSessionStartTime() {
+ return sessionStartTime;
+ }
+
+ /**
+ * The end time indicating when this session stops being available.
+ */
+ public Date getSessionEndTime() {
+ return sessionEndTime;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof ServiceInfo)) {
+ return false;
+ }
+ ServiceInfo that = (ServiceInfo) o;
+ return Objects.equals(names, that.names) &&
+ Objects.equals(className, that.className) &&
+ Objects.equals(locales, that.locales) &&
+ Objects.equals(serviceId, that.serviceId) &&
+ Objects.equals(sessionStartTime, that.sessionStartTime) &&
+ Objects.equals(sessionEndTime, that.sessionEndTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(names, className, locales, serviceId, sessionStartTime, sessionEndTime);
+ }
+}
diff --git a/android/telephony/mbms/StreamingService.java b/android/telephony/mbms/StreamingService.java
new file mode 100644
index 00000000..1d66bac0
--- /dev/null
+++ b/android/telephony/mbms/StreamingService.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class used to represent a single MBMS stream. After a stream has been started with
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * StreamingServiceCallback, android.os.Handler)},
+ * this class is used to hold information about the stream and control it.
+ */
+public class StreamingService {
+ private static final String LOG_TAG = "MbmsStreamingService";
+
+ /**
+ * The state of a stream, reported via {@link StreamingServiceCallback#onStreamStateUpdated}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+ public @interface StreamingState {}
+ public final static int STATE_STOPPED = 1;
+ public final static int STATE_STARTED = 2;
+ public final static int STATE_STALLED = 3;
+
+ /**
+ * The reason for a stream state change, reported via
+ * {@link StreamingServiceCallback#onStreamStateUpdated}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+ REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+ REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
+ public @interface StreamingStateChangeReason {}
+
+ /**
+ * Indicates that the middleware does not have a reason to provide for the state change.
+ */
+ public static final int REASON_NONE = 0;
+
+ /**
+ * State changed due to a call to {@link #stopStreaming()} or
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * StreamingServiceCallback, android.os.Handler)}
+ */
+ public static final int REASON_BY_USER_REQUEST = 1;
+
+ /**
+ * State changed due to the streaming session ending at the carrier.
+ */
+ public static final int REASON_END_OF_SESSION = 2;
+
+ /**
+ * State changed due to a frequency conflict with another requested stream.
+ */
+ public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+ /**
+ * State changed due to the middleware running out of memory
+ */
+ public static final int REASON_OUT_OF_MEMORY = 4;
+
+ /**
+ * State changed due to the device leaving the home carrier's LTE network.
+ */
+ public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+ /**
+ * State changed due to the device leaving the where this stream is being broadcast.
+ */
+ public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
+
+ /**
+ * The method of transmission currently used for a stream,
+ * reported via {@link StreamingServiceCallback#onStreamMethodUpdated}
+ */
+ public final static int BROADCAST_METHOD = 1;
+ public final static int UNICAST_METHOD = 2;
+
+ private final int mSubscriptionId;
+ private final StreamingServiceInfo mServiceInfo;
+ private final InternalStreamingServiceCallback mCallback;
+
+ private IMbmsStreamingService mService;
+
+ /**
+ * @hide
+ */
+ public StreamingService(int subscriptionId,
+ IMbmsStreamingService service,
+ StreamingServiceInfo streamingServiceInfo,
+ InternalStreamingServiceCallback callback) {
+ mSubscriptionId = subscriptionId;
+ mService = service;
+ mServiceInfo = streamingServiceInfo;
+ mCallback = callback;
+ }
+
+ /**
+ * Retreive the Uri used to play this stream.
+ *
+ * This may throw a {@link MbmsException} with the error code
+ * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+ *
+ * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @return The {@link Uri} to pass to the streaming client.
+ */
+ public Uri getPlaybackUri() throws MbmsException {
+ if (mService == null) {
+ throw new IllegalStateException("No streaming service attached");
+ }
+
+ try {
+ return mService.getPlaybackUri(mSubscriptionId, mServiceInfo.getServiceId());
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService = null;
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Retreive the info for this StreamingService.
+ */
+ public StreamingServiceInfo getInfo() {
+ return mServiceInfo;
+ }
+
+ /**
+ * Stop streaming this service.
+ * This may throw a {@link MbmsException} with the error code
+ * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+ *
+ * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ */
+ public void stopStreaming() throws MbmsException {
+ if (mService == null) {
+ throw new IllegalStateException("No streaming service attached");
+ }
+
+ try {
+ mService.stopStreaming(mSubscriptionId, mServiceInfo.getServiceId());
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService = null;
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ }
+ }
+
+ /**
+ * Disposes of this stream. Further operations on this object will fail with an
+ * {@link IllegalStateException}.
+ *
+ * This may throw a {@link MbmsException} with the error code
+ * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+ * May also throw an {@link IllegalStateException}
+ */
+ public void dispose() throws MbmsException {
+ if (mService == null) {
+ throw new IllegalStateException("No streaming service attached");
+ }
+
+ try {
+ mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId());
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException("StreamingService state inconsistent with middleware");
+ } finally {
+ mService = null;
+ }
+ }
+}
+
diff --git a/android/telephony/mbms/StreamingServiceCallback.java b/android/telephony/mbms/StreamingServiceCallback.java
new file mode 100644
index 00000000..b72c7157
--- /dev/null
+++ b/android/telephony/mbms/StreamingServiceCallback.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+/**
+ * A callback class for use when the application is actively streaming content. The middleware
+ * will provide updates on the status of the stream via this callback.
+ */
+public class StreamingServiceCallback {
+
+ /**
+ * Indicates broadcast signal strength is not available for this service.
+ *
+ * This may be due to the service no longer being available due to geography
+ * or timing (end of service) or because lack of demand has caused the service
+ * to be delivered via unicast.
+ */
+ public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+
+ /**
+ * Called by the middleware when it has detected an error condition in this stream. The
+ * possible error codes are listed in {@link MbmsException}.
+ * @param errorCode The error code.
+ * @param message A human-readable message generated by the middleware for debugging purposes.
+ */
+ public void onError(int errorCode, String message) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate this stream has changed state.
+ *
+ * See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
+ * and {@link StreamingService#STATE_STALLED}.
+ */
+ public void onStreamStateUpdated(@StreamingService.StreamingState int state,
+ @StreamingService.StreamingStateChangeReason int reason) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate the mpd of a the stream has changed.
+ *
+ * Depending on the Dash Client it may need to be either reset
+ * (less drastic, but original spec didn't allow mpd to change so not
+ * always supported) or restarted.
+ *
+ * This may be called when a looping stream hits the end or
+ * when parameters have changed to account for time drift.
+ */
+ public void onMediaDescriptionUpdated() {
+ // default implementation empty
+ }
+
+ /**
+ * Broadcast Signal Strength updated.
+ *
+ * This signal strength is the BROADCAST signal strength which,
+ * depending on technology in play and it's deployment, may be
+ * stronger or weaker than the traditional UNICAST signal
+ * strength. It a simple int from 0-4 for valid levels or
+ * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
+ * for this service due to timing, geography or popularity.
+ */
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ // default implementation empty
+ }
+
+ /**
+ * Notify of bcast/unicast method being used.
+ *
+ * This is intended to be informational. Indicates
+ * whether we're able to use cell broadcast or have
+ * had to fallback to unicast for this stream.
+ *
+ * This must be called once at the beginning of the stream
+ * around the same time as we change to STATE_STARTED, but
+ * strict ordering is not specified. It must be called
+ * again if we change modes, but if that doesn't happen
+ * the callback won't be used again.
+ *
+ * See {@link StreamingService#BROADCAST_METHOD} and
+ * {@link StreamingService#UNICAST_METHOD}
+ */
+ public void onStreamMethodUpdated(int methodType) {
+ // default implementation empty
+ }
+}
diff --git a/android/telephony/mbms/StreamingServiceInfo.java b/android/telephony/mbms/StreamingServiceInfo.java
new file mode 100644
index 00000000..c704f346
--- /dev/null
+++ b/android/telephony/mbms/StreamingServiceInfo.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Describes a single MBMS streaming service.
+ */
+public final class StreamingServiceInfo extends ServiceInfo implements Parcelable {
+
+ /**
+ * @param names User displayable names listed by language.
+ * @param className The class name for this service - used by frontend apps to categorize and
+ * filter.
+ * @param locales The languages available for this service content.
+ * @param serviceId The carrier's identifier for the service.
+ * @param start The start time indicating when this service will be available.
+ * @param end The end time indicating when this session stops being available.
+ * @hide
+ */
+ @SystemApi
+ public StreamingServiceInfo(Map<Locale, String> names, String className,
+ List<Locale> locales, String serviceId, Date start, Date end) {
+ super(names, className, locales, serviceId, start, end);
+ }
+
+ public static final Parcelable.Creator<StreamingServiceInfo> CREATOR =
+ new Parcelable.Creator<StreamingServiceInfo>() {
+ @Override
+ public StreamingServiceInfo createFromParcel(Parcel source) {
+ return new StreamingServiceInfo(source);
+ }
+
+ @Override
+ public StreamingServiceInfo[] newArray(int size) {
+ return new StreamingServiceInfo[size];
+ }
+ };
+
+ private StreamingServiceInfo(Parcel in) {
+ super(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/android/telephony/mbms/UriPathPair.java b/android/telephony/mbms/UriPathPair.java
new file mode 100644
index 00000000..7acc270e
--- /dev/null
+++ b/android/telephony/mbms/UriPathPair.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class UriPathPair implements Parcelable {
+ private final Uri mFilePathUri;
+ private final Uri mContentUri;
+
+ /** @hide */
+ public UriPathPair(Uri fileUri, Uri contentUri) {
+ if (fileUri == null || !ContentResolver.SCHEME_FILE.equals(fileUri.getScheme())) {
+ throw new IllegalArgumentException("File URI must have file scheme");
+ }
+ if (contentUri == null || !ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
+ throw new IllegalArgumentException("Content URI must have content scheme");
+ }
+
+ mFilePathUri = fileUri;
+ mContentUri = contentUri;
+ }
+
+ /** @hide */
+ protected UriPathPair(Parcel in) {
+ mFilePathUri = in.readParcelable(Uri.class.getClassLoader());
+ mContentUri = in.readParcelable(Uri.class.getClassLoader());
+ }
+
+ public static final Creator<UriPathPair> CREATOR = new Creator<UriPathPair>() {
+ @Override
+ public UriPathPair createFromParcel(Parcel in) {
+ return new UriPathPair(in);
+ }
+
+ @Override
+ public UriPathPair[] newArray(int size) {
+ return new UriPathPair[size];
+ }
+ };
+
+ /** future systemapi */
+ public Uri getFilePathUri() {
+ return mFilePathUri;
+ }
+
+ /** future systemapi */
+ public Uri getContentUri() {
+ return mContentUri;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mFilePathUri, flags);
+ dest.writeParcelable(mContentUri, flags);
+ }
+}
diff --git a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
new file mode 100644
index 00000000..71713d01
--- /dev/null
+++ b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms.vendor;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.FileServiceInfo;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsException;
+
+import java.util.List;
+
+/**
+ * Base class for MbmsDownloadService. The middleware should extend this base class rather than
+ * the aidl stub for compatibility
+ * @hide
+ * TODO: future systemapi
+ */
+public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
+ /**
+ * Initialize the download service for this app and subId, registering the listener.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
+ * will be intercepted and passed to the app as
+ * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+ *
+ * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
+ * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
+ * {@link IMbmsDownloadManagerCallback#error(int, String)}.
+ *
+ * @param callback The callback to use to communicate with the app.
+ * @param subscriptionId The subscription ID to use.
+ */
+ public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback)
+ throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * @hide
+ */
+ @Override
+ public final int initialize(int subscriptionId,
+ final IMbmsDownloadManagerCallback callback) throws RemoteException {
+ return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ callback.error(errorCode, message);
+ }
+
+ @Override
+ public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
+ callback.fileServicesUpdated(services);
+ }
+
+ @Override
+ public void middlewareReady() throws RemoteException {
+ callback.middlewareReady();
+ }
+ });
+ }
+
+ /**
+ * Registers serviceClasses of interest with the appName/subId key.
+ * Starts async fetching data on streaming services of matching classes to be reported
+ * later via {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
+ *
+ * Note that subsequent calls with the same uid and subId will replace
+ * the service class list.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceClasses The service classes that the app wishes to get info on. The strings
+ * may contain arbitrary data as negotiated between the app and the
+ * carrier.
+ * @return One of {@link MbmsException#SUCCESS} or
+ * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
+ */
+ @Override
+ public int getFileServices(int subscriptionId, List<String> serviceClasses)
+ throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Sets the temp file root directory for this app/subscriptionId combination. The middleware
+ * should persist {@code rootDirectoryPath} and send it back when sending intents to the
+ * app's {@link android.telephony.mbms.MbmsDownloadReceiver}.
+ *
+ * If the calling app (as identified by the calling UID) currently has any pending download
+ * requests that have not been canceled, the middleware must return
+ * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
+ *
+ * @param subscriptionId The subscription id the download is operating under.
+ * @param rootDirectoryPath The path to the app's temp file root directory.
+ * @return {@link MbmsException#SUCCESS},
+ * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
+ * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+ */
+ @Override
+ public int setTempFileRootDirectory(int subscriptionId,
+ String rootDirectoryPath) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Issues a request to download a set of files.
+ *
+ * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been
+ * called for this app between when the app was installed and when this method is called. If
+ * this is not the case, an {@link IllegalStateException} may be thrown.
+ *
+ * @param downloadRequest An object describing the set of files to be downloaded.
+ * @param listener A listener through which the middleware can provide progress updates to
+ * the app while both are still running.
+ * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors}
+ * or {@link MbmsException#SUCCESS}
+ */
+ public int download(DownloadRequest downloadRequest, DownloadProgressListener listener) {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * @hide
+ */
+ @Override
+ public final int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
+ throws RemoteException {
+ return download(downloadRequest, new DownloadProgressListener() {
+ @Override
+ public void progress(DownloadRequest request, FileInfo fileInfo, int
+ currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
+ fullDecodedSize) throws RemoteException {
+ listener.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
+ currentDecodedSize, fullDecodedSize);
+ }
+ });
+ }
+
+
+ /**
+ * Returns a list of pending {@link DownloadRequest}s that originated from the calling
+ * application, identified by its uid. A pending request is one that was issued via
+ * {@link #download(DownloadRequest, IDownloadProgressListener)} but not cancelled through
+ * {@link #cancelDownload(DownloadRequest)}.
+ * The middleware must return a non-null result synchronously or throw an exception
+ * inheriting from {@link RuntimeException}.
+ * @return A list, possibly empty, of {@link DownloadRequest}s
+ */
+ @Override
+ public @NonNull List<DownloadRequest> listPendingDownloads(int subscriptionId)
+ throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Issues a request to cancel the specified download request.
+ *
+ * If the middleware is unable to cancel the request for whatever reason, it should return
+ * synchronously with an error. If this method returns {@link MbmsException#SUCCESS}, the app
+ * will no longer be expecting any more file-completed intents from the middleware for this
+ * {@link DownloadRequest}.
+ * @param downloadRequest The request to cancel
+ * @return {@link MbmsException#SUCCESS},
+ * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
+ * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
+ @Override
+ public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Gets information about the status of a file pending download.
+ *
+ * If the middleware has not yet been properly initialized or if it has no records of the
+ * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+ * {@link android.telephony.MbmsDownloadManager#STATUS_UNKNOWN} must be returned.
+ *
+ * @param downloadRequest The download request to query.
+ * @param fileInfo The particular file within the request to get information on.
+ * @return The status of the download.
+ */
+ @Override
+ public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+ throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Resets the middleware's knowledge of previously-downloaded files in this download request.
+ *
+ * When this method is called, the middleware must attempt to re-download all the files
+ * specified by the {@link DownloadRequest}, even if the files have not changed on the server.
+ * In addition, current in-progress downloads must not be interrupted.
+ *
+ * If the middleware is not aware of the specified download request, return
+ * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+ *
+ * @param downloadRequest The request to re-download files for.
+ */
+ @Override
+ public int resetDownloadKnowledge(DownloadRequest downloadRequest)
+ throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Signals that the app wishes to dispose of the session identified by the
+ * {@code subscriptionId} argument and the caller's uid. No notification back to the
+ * app is required for this operation, and the corresponding callback provided via
+ * {@link #initialize(int, IMbmsDownloadManagerCallback)} should no longer be used
+ * after this method has been called by the app.
+ *
+ * Any download requests issued by the app should remain in effect until the app calls
+ * {@link #cancelDownload(DownloadRequest)} on another session.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ */
+ @Override
+ public void dispose(int subscriptionId) throws RemoteException {
+ }
+}
diff --git a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
new file mode 100644
index 00000000..843e0482
--- /dev/null
+++ b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms.vendor;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.IStreamingServiceCallback;
+import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+@SystemApi
+public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
+ /**
+ * Initialize streaming service for this app and subId, registering the listener.
+ *
+ * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
+ * will be intercepted and passed to the app as
+ * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+ *
+ * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
+ * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
+ * {@link IMbmsStreamingManagerCallback#error(int, String)}.
+ *
+ * @param callback The callback to use to communicate with the app.
+ * @param subscriptionId The subscription ID to use.
+ */
+ public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId)
+ throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation that hides the callback AIDL from the middleware.
+ * @hide
+ */
+ @Override
+ public final int initialize(final IMbmsStreamingManagerCallback callback,
+ final int subscriptionId) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+
+ return initialize(new MbmsStreamingManagerCallback() {
+ @Override
+ public void onError(int errorCode, String message) {
+ try {
+ callback.error(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+ try {
+ callback.streamingServicesUpdated(services);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onMiddlewareReady() {
+ try {
+ callback.middlewareReady();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ }, subscriptionId);
+ }
+
+
+ /**
+ * Registers serviceClasses of interest with the appName/subId key.
+ * Starts async fetching data on streaming services of matching classes to be reported
+ * later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
+ *
+ * Note that subsequent calls with the same uid and subId will replace
+ * the service class list.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceClasses The service classes that the app wishes to get info on. The strings
+ * may contain arbitrary data as negotiated between the app and the
+ * carrier.
+ * @return {@link MbmsException#SUCCESS} or any of the errors in
+ * {@link android.telephony.mbms.MbmsException.GeneralErrors}
+ */
+ @Override
+ public int getStreamingServices(int subscriptionId,
+ List<String> serviceClasses) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Starts streaming on a particular service. This method may perform asynchronous work. When
+ * the middleware is ready to send bits to the frontend, it should inform the app via
+ * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceId The ID of the streaming service that the app has requested.
+ * @param callback The callback object on which the app wishes to receive updates.
+ * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors}
+ */
+ public int startStreaming(int subscriptionId, String serviceId,
+ StreamingServiceCallback callback) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation of startStreaming that hides the callback AIDL from the
+ * middleware.
+ * @hide
+ */
+ @Override
+ public int startStreaming(int subscriptionId, String serviceId,
+ IStreamingServiceCallback callback) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+
+ return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
+ @Override
+ public void onError(int errorCode, String message) {
+ try {
+ callback.error(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onStreamStateUpdated(@StreamingService.StreamingState int state,
+ @StreamingService.StreamingStateChangeReason int reason) {
+ try {
+ callback.streamStateUpdated(state, reason);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onMediaDescriptionUpdated() {
+ try {
+ callback.mediaDescriptionUpdated();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ try {
+ callback.broadcastSignalStrengthUpdated(signalStrength);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onStreamMethodUpdated(int methodType) {
+ try {
+ callback.streamMethodUpdated(methodType);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ });
+ }
+
+ /**
+ * Retrieves the streaming URI for a particular service. If the middleware is not yet ready to
+ * stream the service, this method may return null.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceId The ID of the streaming service that the app has requested.
+ * @return An opaque {@link Uri} to be passed to a video player that understands the format.
+ */
+ @Override
+ public @Nullable Uri getPlaybackUri(int subscriptionId, String serviceId)
+ throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
+ * stream state change should be reported to the app via
+ * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceId The ID of the streaming service that the app wishes to stop.
+ */
+ @Override
+ public void stopStreaming(int subscriptionId, String serviceId)
+ throws RemoteException {
+ }
+
+ /**
+ * Dispose of the stream identified by {@code serviceId} for the app identified by the
+ * {@code appName} and {@code subscriptionId} arguments along with the caller's uid.
+ * No notification back to the app is required for this operation, and the callback provided via
+ * {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
+ * used after this method has called by the app.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceId The ID of the streaming service that the app wishes to dispose of.
+ */
+ @Override
+ public void disposeStream(int subscriptionId, String serviceId)
+ throws RemoteException {
+ }
+
+ /**
+ * Signals that the app wishes to dispose of the session identified by the
+ * {@code subscriptionId} argument and the caller's uid. No notification back to the
+ * app is required for this operation, and the corresponding callback provided via
+ * {@link #initialize(IMbmsStreamingManagerCallback, int)} should no longer be used
+ * after this method has been called by the app.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ */
+ @Override
+ public void dispose(int subscriptionId) throws RemoteException {
+ }
+
+ /**
+ * Indicates that the app identified by the given UID and subscription ID has died.
+ * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+ * @param subscriptionId The subscription ID the app is using.
+ */
+ public void onAppCallbackDied(int uid, int subscriptionId) {
+ }
+}
diff --git a/android/telephony/mbms/vendor/VendorIntents.java b/android/telephony/mbms/vendor/VendorIntents.java
new file mode 100644
index 00000000..367c995c
--- /dev/null
+++ b/android/telephony/mbms/vendor/VendorIntents.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms.vendor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.MbmsDownloadReceiver;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @hide
+ * TODO: future systemapi
+ */
+public class VendorIntents {
+
+ /**
+ * The MBMS middleware should send this when a download of single file has completed or
+ * failed. Mandatory extras are
+ * {@link android.telephony.MbmsDownloadManager#EXTRA_RESULT}
+ * {@link android.telephony.MbmsDownloadManager#EXTRA_FILE_INFO}
+ * {@link #EXTRA_REQUEST}
+ * {@link #EXTRA_TEMP_LIST}
+ * {@link #EXTRA_FINAL_URI}
+ */
+ public static final String ACTION_DOWNLOAD_RESULT_INTERNAL =
+ "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+
+ /**
+ * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
+ * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
+ * extras are
+ * {@link #EXTRA_REQUEST}
+ *
+ * Optional extras are
+ * {@link #EXTRA_FD_COUNT} (0 if not present)
+ * {@link #EXTRA_PAUSED_LIST} (empty if not present)
+ */
+ public static final String ACTION_FILE_DESCRIPTOR_REQUEST =
+ "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+
+ /**
+ * The MBMS middleware should send this when it wishes to clean up temp files in the app's
+ * filesystem. Mandatory extras are:
+ * {@link #EXTRA_TEMP_FILES_IN_USE}
+ */
+ public static final String ACTION_CLEANUP =
+ "android.telephony.mbms.action.CLEANUP";
+
+ /**
+ * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this
+ * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp
+ * files will be deleted upon receipt of the intent.
+ * May be null.
+ */
+ public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+
+ /**
+ * Extra containing an integer indicating the number of temp files requested.
+ */
+ public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+
+ /**
+ * Extra containing a list of {@link Uri}s that the middleware is requesting access to via
+ * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s
+ * should have scheme {@code file://}.
+ */
+ public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+
+ /**
+ * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+ * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant
+ * to be used for new file downloads.
+ */
+ public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+
+ /**
+ * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+ * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These
+ * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide
+ * access to previously paused downloads.
+ */
+ public static final String EXTRA_PAUSED_URI_LIST =
+ "android.telephony.mbms.extra.PAUSED_URI_LIST";
+
+ /**
+ * Extra containing a string that points to the middleware's knowledge of where the temp file
+ * root for the app is. The path should be a canonical path as returned by
+ * {@link File#getCanonicalPath()}
+ */
+ public static final String EXTRA_TEMP_FILE_ROOT =
+ "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+
+ /**
+ * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
+ * still using.
+ */
+ public static final String EXTRA_TEMP_FILES_IN_USE =
+ "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+
+ /**
+ * Extra containing the {@link DownloadRequest} for which the download result or file
+ * descriptor request is for. Must not be null.
+ */
+ public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
+
+ /**
+ * Extra containing a single {@link Uri} indicating the path to the temp file in which the
+ * decoded downloaded file resides. Must not be null.
+ */
+ public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+
+ /**
+ * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
+ * file-descriptor requests and cleanup requests to specify which service they want to
+ * request temp files or clean up temp files for, respectively.
+ */
+ public static final String EXTRA_SERVICE_INFO =
+ "android.telephony.mbms.extra.SERVICE_INFO";
+
+ /**
+ * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
+ * the various intents from the middleware should be targeted towards.
+ * @param uid The uid of the frontend app.
+ * @return The component name of the receiver that the middleware should send its intents to,
+ * or null if the app didn't declare it in the manifest.
+ */
+ public static ComponentName getAppReceiverFromUid(Context context, int uid) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null) {
+ return null;
+ }
+
+ for (String packageName : packageNames) {
+ ComponentName candidate = new ComponentName(packageName,
+ MbmsDownloadReceiver.class.getCanonicalName());
+ Intent queryIntent = new Intent();
+ queryIntent.setComponent(candidate);
+ List<ResolveInfo> receivers =
+ context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+ if (receivers != null && receivers.size() > 0) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+}