/*
 * 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.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.PendingIntent;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.database.CursorWindow;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.ISms;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.SmsRawData;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

/*
 * 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()}. To create an instance of
 * {@link SmsManager} associated with a specific subscription ID, call
 * {@link #getSmsManagerForSubscriptionId(int)}. This is typically used for devices that support
 * multiple active subscriptions at once.
 *
 * <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}.
 *
 * @see SubscriptionManager#getActiveSubscriptionInfoList()
 */
public final class SmsManager {
    private static final String TAG = "SmsManager";

    /** Singleton object constructed during class initialization. */
    private static final SmsManager sInstance = new SmsManager(
            SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
    private static final Object sLockObject = new Object();

    /** 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 */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    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;

    /**
     * 3gpp2 SMS priority is not specified
     * @hide
     */
    public static final int SMS_MESSAGE_PRIORITY_NOT_SPECIFIED = -1;
    /**
     * 3gpp SMS period is not specified
     * @hide
     */
    public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;

    /** @hide */
    @IntDef(prefix = { "PREMIUM_SMS_CONSENT" }, value = {
        SmsManager.PREMIUM_SMS_CONSENT_UNKNOWN,
        SmsManager.PREMIUM_SMS_CONSENT_ASK_USER,
        SmsManager.PREMIUM_SMS_CONSENT_NEVER_ALLOW,
        SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PremiumSmsConsent {}

    /** Premium SMS Consent for the package is unknown. This indicates that the user
     *  has not set a permission for this package, because this package has never tried
     *  to send a premium SMS.
     * @hide
     */
    @SystemApi
    public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0;

    /** Default premium SMS Consent (ask user for each premium SMS sent).
     * @hide
     */
    @SystemApi
    public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1;

    /** Premium SMS Consent when the owner has denied the app from sending premium SMS.
     * @hide
     */
    @SystemApi
    public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2;

    /** Premium SMS Consent when the owner has allowed the app to send premium SMS.
     * @hide
     */
    @SystemApi
    public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3;

    // result of asking the user for a subscription to perform an operation.
    private interface SubscriptionResolverResult {
        void onSuccess(int subId);
        void onFailure();
    }

    /**
     * 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>
     *
     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
     * suitable default subscription could be found. In this case, if {@code sentIntent} is
     * non-null, then the {@link PendingIntent} will be sent with an error code
     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
     * where this operation may fail.
     * </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>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_NETWORK_REJECT</code><br>
     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_INVALID_STATE</code><br>
     *  <code>RESULT_NO_MEMORY</code><br>
     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_SYSTEM_ERROR</code><br>
     *  <code>RESULT_MODEM_ERROR</code><br>
     *  <code>RESULT_NETWORK_ERROR</code><br>
     *  <code>RESULT_ENCODING_ERROR</code><br>
     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_INTERNAL_ERROR</code><br>
     *  <code>RESULT_NO_RESOURCES</code><br>
     *  <code>RESULT_CANCELLED</code><br>
     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
     *  <code>RESULT_RIL_INVALID_STATE</code><br>
     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_RIL_NO_MEMORY</code><br>
     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_RIL_MODEM_ERR</code><br>
     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
     *  <code>RESULT_RIL_CANCELLED</code><br>
     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
     *  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*/, null, null, 0L /* messageId */);
    }


    /**
     * Send a text based SMS. Same as {@link #sendTextMessage( String destinationAddress,
     * String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)}, but
     * adds an optional messageId.
     * @param messageId An id that uniquely identifies the message requested to be sent.
     * Used for logging and diagnostics purposes. The id may be 0.
     *
     * @throws IllegalArgumentException if destinationAddress or text are empty
     *
     */
    public void sendTextMessage(
            @NonNull String destinationAddress, @Nullable String scAddress, @NonNull String text,
            @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveryIntent,
            long messageId) {
        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                true /* persistMessage*/, null, null, messageId);
    }

    /**
     * Send a text based SMS with messaging options.
     *
     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
     * suitable default subscription could be found. In this case, if {@code sentIntent} is
     * non-null, then the {@link PendingIntent} will be sent with an error code
     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
     * where this operation may fail.
     * </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>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_NETWORK_REJECT</code><br>
     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_INVALID_STATE</code><br>
     *  <code>RESULT_NO_MEMORY</code><br>
     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_SYSTEM_ERROR</code><br>
     *  <code>RESULT_MODEM_ERROR</code><br>
     *  <code>RESULT_NETWORK_ERROR</code><br>
     *  <code>RESULT_ENCODING_ERROR</code><br>
     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_INTERNAL_ERROR</code><br>
     *  <code>RESULT_NO_RESOURCES</code><br>
     *  <code>RESULT_CANCELLED</code><br>
     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
     *  <code>RESULT_RIL_INVALID_STATE</code><br>
     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_RIL_NO_MEMORY</code><br>
     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_RIL_MODEM_ERR</code><br>
     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
     *  <code>RESULT_RIL_CANCELLED</code><br>
     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
     *  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").
     * @param priority Priority level of the message
     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
     *  ---------------------------------
     *  PRIORITY      | Level of Priority
     *  ---------------------------------
     *      '00'      |     Normal
     *      '01'      |     Interactive
     *      '10'      |     Urgent
     *      '11'      |     Emergency
     *  ----------------------------------
     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
     * @param expectMore is a boolean to indicate the sending messages through same link or not.
     * @param validityPeriod Validity Period of the message in mins.
     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
     *  Validity Period(Minimum) -> 5 mins
     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
     *  Any Other values included Negative considered as Invalid Validity Period of the message.
     *
     * @throws IllegalArgumentException if destinationAddress or text are empty
     * {@hide}
     */
    @UnsupportedAppUsage
    public void sendTextMessage(
            String destinationAddress, String scAddress, String text,
            PendingIntent sentIntent, PendingIntent deliveryIntent,
            int priority, boolean expectMore, int validityPeriod) {
        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                true /* persistMessage*/, priority, expectMore, validityPeriod);
    }

    private void sendTextMessageInternal(String destinationAddress, String scAddress,
            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
            boolean persistMessage, String packageName, String attributionTag, long messageId) {
        if (TextUtils.isEmpty(destinationAddress)) {
            throw new IllegalArgumentException("Invalid destinationAddress");
        }

        if (TextUtils.isEmpty(text)) {
            throw new IllegalArgumentException("Invalid message body");
        }

        // We will only show the SMS disambiguation dialog in the case that the message is being
        // persisted. This is for two reasons:
        // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
        //    subscription and require special permissions. These messages are usually not sent by
        //    the device user and should not have an SMS disambiguation dialog associated with them
        //    because the device user did not trigger them.
        // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
        //    permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
        //    the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
        //    an incorrect SecurityException.
        if (persistMessage) {
            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
                @Override
                public void onSuccess(int subId) {
                    ISms iSms = getISmsServiceOrThrow();
                    try {
                        iSms.sendTextForSubscriber(subId, packageName, attributionTag,
                                destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                                persistMessage, messageId);
                    } catch (RemoteException e) {
                        Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
                                + e.getMessage() + " id: " + messageId);
                        notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
                    }
                }

                @Override
                public void onFailure() {
                    notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
                }
            });
        } else {
            // Not persisting the message, used by sendTextMessageWithoutPersisting() and is not
            // visible to the user.
            ISms iSms = getISmsServiceOrThrow();
            try {
                iSms.sendTextForSubscriber(getSubscriptionId(), packageName, attributionTag,
                        destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                        persistMessage, messageId);
            } catch (RemoteException e) {
                Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - "
                        + e.getMessage() + " id: " + messageId);
                notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
            }
        }
    }

    /**
     * Send a text based SMS without writing it into the SMS Provider.
     *
     * <p>
     * The message will be sent directly over the network and will not be visible in SMS
     * applications. Intended for internal carrier use only.
     * </p>
     *
     * <p>Requires Permission: Both {@link android.Manifest.permission#SEND_SMS} and
     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or that the calling app has carrier
     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is
     * the default IMS app (see
     * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}).
     * </p>
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the SMS being sent on the subscription associated with logical
     * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
     * correct subscription.
     * </p>
     *
     * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
     */
    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
    @RequiresPermission(allOf = {
            android.Manifest.permission.MODIFY_PHONE_STATE,
            android.Manifest.permission.SEND_SMS
    })
    public void sendTextMessageWithoutPersisting(
            String destinationAddress, String scAddress, String text,
            PendingIntent sentIntent, PendingIntent deliveryIntent) {
        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                false /* persistMessage */, null, null,
                0L /* messageId */);
    }

    private void sendTextMessageInternal(
            String destinationAddress, String scAddress, String text,
            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
            int priority, boolean expectMore, int validityPeriod) {
        if (TextUtils.isEmpty(destinationAddress)) {
            throw new IllegalArgumentException("Invalid destinationAddress");
        }

        if (TextUtils.isEmpty(text)) {
            throw new IllegalArgumentException("Invalid message body");
        }

        if (priority < 0x00 || priority > 0x03) {
            priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
        }

        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
            validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
        }

        final int finalPriority = priority;
        final int finalValidity = validityPeriod;
        // We will only show the SMS disambiguation dialog in the case that the message is being
        // persisted. This is for two reasons:
        // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
        //    subscription and require special permissions. These messages are usually not sent by
        //    the device user and should not have an SMS disambiguation dialog associated with them
        //    because the device user did not trigger them.
        // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
        //    permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
        //    the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
        //    an incorrect SecurityException.
        if (persistMessage) {
            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
                @Override
                public void onSuccess(int subId) {
                    try {
                        ISms iSms = getISmsServiceOrThrow();
                        if (iSms != null) {
                            iSms.sendTextForSubscriberWithOptions(subId,
                                    null, null, destinationAddress,
                                    scAddress,
                                    text, sentIntent, deliveryIntent, persistMessage, finalPriority,
                                    expectMore, finalValidity);
                        }
                    } catch (RemoteException e) {
                        Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
                                + e.getMessage());
                        notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
                    }
                }

                @Override
                public void onFailure() {
                    notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
                }
            });
        } else {
            try {
                ISms iSms = getISmsServiceOrThrow();
                if (iSms != null) {
                    iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
                            null, null, destinationAddress,
                            scAddress,
                            text, sentIntent, deliveryIntent, persistMessage, finalPriority,
                            expectMore, finalValidity);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "sendTextMessageInternal(no persist): Couldn't send SMS, exception - "
                        + e.getMessage());
                notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
            }
        }
    }

    /**
     * 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>
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the SMS being sent on the subscription associated with logical
     * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
     * correct subscription.
     * </p>
     *
     * @see #sendTextMessage(String, String, String, PendingIntent,
     * PendingIntent, int, boolean, int)
     * @hide
     */
    @UnsupportedAppUsage
    public void sendTextMessageWithoutPersisting(
            String destinationAddress, String scAddress, String text,
            PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
            boolean expectMore, int validityPeriod) {
        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                false /* persistMessage */, priority, expectMore, validityPeriod);
    }

    /**
     *
     * Inject an SMS PDU into the android application framework.
     *
     * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
     * privileges per {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the SMS being injected on the subscription associated with
     * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is
     * delivered to the correct subscription.
     * </p>
     *
     * @param pdu is the byte array of pdu to be injected into android application framework
     * @param format is the format of SMS pdu ({@link SmsMessage#FORMAT_3GPP} or
     *  {@link SmsMessage#FORMAT_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 {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_HANDLED}
     *  for success, or {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_GENERIC_ERROR} or
     *  {@link #RESULT_REMOTE_EXCEPTION} for error.
     *
     * @throws IllegalArgumentException if the format is invalid.
     */
    public void injectSmsPdu(
            byte[] pdu, @SmsMessage.Format 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 iSms = TelephonyManager.getSmsService();
            if (iSms != null) {
                iSms.injectSmsPduForSubscriber(
                        getSubscriptionId(), pdu, format, receivedIntent);
            }
        } catch (RemoteException ex) {
            try {
                if (receivedIntent != null) {
                    receivedIntent.send(RESULT_REMOTE_EXCEPTION);
                }
            } catch (PendingIntent.CanceledException cx) {
                // Don't worry about it, we do not need to notify the caller if this is the case.
            }
        }
    }

    /**
     * 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, getSubscriptionId());
    }

    /**
     * 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>
     *
     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
     * suitable default subscription could be found. In this case, if {@code sentIntent} is
     * non-null, then the {@link PendingIntent} will be sent with an error code
     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
     * where this operation may fail.
     * </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>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_NETWORK_REJECT</code><br>
     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_INVALID_STATE</code><br>
     *  <code>RESULT_NO_MEMORY</code><br>
     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_SYSTEM_ERROR</code><br>
     *  <code>RESULT_MODEM_ERROR</code><br>
     *  <code>RESULT_NETWORK_ERROR</code><br>
     *  <code>RESULT_ENCODING_ERROR</code><br>
     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_INTERNAL_ERROR</code><br>
     *  <code>RESULT_NO_RESOURCES</code><br>
     *  <code>RESULT_CANCELLED</code><br>
     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
     *  <code>RESULT_RIL_INVALID_STATE</code><br>
     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_RIL_NO_MEMORY</code><br>
     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_RIL_MODEM_ERR</code><br>
     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
     *  <code>RESULT_RIL_CANCELLED</code><br>
     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
     *  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 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*/, null, null,
                0L /* messageId */);
    }

    /**
     * Send a multi-part text based SMS. Same as #sendMultipartTextMessage(String, String,
     * ArrayList, ArrayList, ArrayList), but adds an optional messageId.
     * @param messageId An id that uniquely identifies the message requested to be sent.
     * Used for logging and diagnostics purposes. The id may be 0.
     *
     * @throws IllegalArgumentException if destinationAddress or data are empty
     *
     */
    public void sendMultipartTextMessage(
            @NonNull String destinationAddress, @Nullable String scAddress,
            @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
            @Nullable List<PendingIntent> deliveryIntents, long messageId) {
        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
                deliveryIntents, true /* persistMessage*/, null, null,
                messageId);
    }

    /**
     * Similar method as #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
     * With an additional argument.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony
     * framework and will never trigger an SMS disambiguation dialog. If this method is called on a
     * device that has multiple active subscriptions, this {@link SmsManager} instance has been
     * created with {@link #getDefault()}, and no user-defined default subscription is defined, the
     * subscription ID associated with this message will be INVALID, which will result in the SMS
     * being sent on the subscription associated with logical slot 0. Use
     * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct
     * subscription.
     * </p>
     *
     * @param packageName serves as the default package name if the package name that is
     *        associated with the user id is null.
     */
    public void sendMultipartTextMessage(
            @NonNull String destinationAddress, @Nullable String scAddress,
            @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
            @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName,
            @Nullable String attributionTag) {
        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
                deliveryIntents, true /* persistMessage*/, packageName, attributionTag,
                0L /* messageId */);
    }

    private void sendMultipartTextMessageInternal(
            String destinationAddress, String scAddress, List<String> parts,
            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
            boolean persistMessage, String packageName, @Nullable String attributionTag,
            long messageId) {
        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) {
            // We will only show the SMS disambiguation dialog in the case that the message is being
            // persisted. This is for two reasons:
            // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
            //    subscription and require special permissions. These messages are usually not sent
            //    by the device user and should not have an SMS disambiguation dialog associated
            //    with them because the device user did not trigger them.
            // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the
            //    SEND_SMS permission. If we call resolveSubscriptionForOperation from a carrier/OEM
            //    app that has the correct MODIFY_PHONE_STATE or carrier permissions, but no
            //    SEND_SMS, it will throw an incorrect SecurityException.
            if (persistMessage) {
                resolveSubscriptionForOperation(new SubscriptionResolverResult() {
                    @Override
                    public void onSuccess(int subId) {
                        try {
                            ISms iSms = getISmsServiceOrThrow();
                            iSms.sendMultipartTextForSubscriber(subId, packageName, attributionTag,
                                    destinationAddress, scAddress, parts, sentIntents,
                                    deliveryIntents, persistMessage, messageId);
                        } catch (RemoteException e) {
                            Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
                                    + e.getMessage() + " id: "
                                    + messageId);
                            notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
                        }
                    }

                    @Override
                    public void onFailure() {
                        notifySmsError(sentIntents, RESULT_NO_DEFAULT_SMS_APP);
                    }
                });
            } else {
                // Called by apps that are not user facing, don't show disambiguation dialog.
                try {
                    ISms iSms = getISmsServiceOrThrow();
                    if (iSms != null) {
                        iSms.sendMultipartTextForSubscriber(getSubscriptionId(), packageName,
                                attributionTag, destinationAddress, scAddress, parts, sentIntents,
                                deliveryIntents, persistMessage, messageId);
                    }
                } catch (RemoteException e) {
                    Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
                            + e.getMessage() + " id: " + messageId);
                    notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
                }
            }
        } 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);
            }
            sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
                    sentIntent, deliveryIntent, true, packageName, attributionTag, messageId);
        }
    }

    /**
     * Send a multi-part text based SMS without writing it into the SMS Provider.
     *
     * <p>
     * If this method is called on a device with multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the SMS sent on the subscription associated with slot
     * 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent using the
     * correct subscription.
     * </p>
     *
     * <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*/, null, null,
                0L /* messageId */);
    }

    /**
     * Send a multi-part text based SMS with messaging options. 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>
     *
     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
     * suitable default subscription could be found. In this case, if {@code sentIntent} is
     * non-null, then the {@link PendingIntent} will be sent with an error code
     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
     * where this operation may fail.
     * </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>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_NETWORK_REJECT</code><br>
     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_INVALID_STATE</code><br>
     *  <code>RESULT_NO_MEMORY</code><br>
     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_SYSTEM_ERROR</code><br>
     *  <code>RESULT_MODEM_ERROR</code><br>
     *  <code>RESULT_NETWORK_ERROR</code><br>
     *  <code>RESULT_ENCODING_ERROR</code><br>
     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_INTERNAL_ERROR</code><br>
     *  <code>RESULT_NO_RESOURCES</code><br>
     *  <code>RESULT_CANCELLED</code><br>
     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
     *  <code>RESULT_RIL_INVALID_STATE</code><br>
     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_RIL_NO_MEMORY</code><br>
     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_RIL_MODEM_ERR</code><br>
     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
     *  <code>RESULT_RIL_CANCELLED</code><br>
     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
     *  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 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").
     * @param priority Priority level of the message
     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
     *  ---------------------------------
     *  PRIORITY      | Level of Priority
     *  ---------------------------------
     *      '00'      |     Normal
     *      '01'      |     Interactive
     *      '10'      |     Urgent
     *      '11'      |     Emergency
     *  ----------------------------------
     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
     * @param expectMore is a boolean to indicate the sending messages through same link or not.
     * @param validityPeriod Validity Period of the message in mins.
     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
     *  Validity Period(Minimum) -> 5 mins
     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
     *  Any Other values included Negative considered as Invalid Validity Period of the message.
     *
     * @throws IllegalArgumentException if destinationAddress or data are empty
     * {@hide}
     */
    @UnsupportedAppUsage
    public void sendMultipartTextMessage(
            String destinationAddress, String scAddress, ArrayList<String> parts,
            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
            int priority, boolean expectMore, int validityPeriod) {
        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
                deliveryIntents, true /* persistMessage*/, priority, expectMore,
                validityPeriod);
    }

    private void sendMultipartTextMessageInternal(
            String destinationAddress, String scAddress, List<String> parts,
            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
            boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
        if (TextUtils.isEmpty(destinationAddress)) {
            throw new IllegalArgumentException("Invalid destinationAddress");
        }
        if (parts == null || parts.size() < 1) {
            throw new IllegalArgumentException("Invalid message body");
        }

        if (priority < 0x00 || priority > 0x03) {
            priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
        }

        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
            validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
        }

        if (parts.size() > 1) {
            final int finalPriority = priority;
            final int finalValidity = validityPeriod;
            if (persistMessage) {
                resolveSubscriptionForOperation(new SubscriptionResolverResult() {
                    @Override
                    public void onSuccess(int subId) {
                        try {
                            ISms iSms = getISmsServiceOrThrow();
                            if (iSms != null) {
                                iSms.sendMultipartTextForSubscriberWithOptions(subId,
                                        null, null, destinationAddress,
                                        scAddress, parts, sentIntents, deliveryIntents,
                                        persistMessage, finalPriority, expectMore, finalValidity);
                            }
                        } catch (RemoteException e) {
                            Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
                                    + e.getMessage());
                            notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
                        }
                    }

                    @Override
                    public void onFailure() {
                        notifySmsError(sentIntents, RESULT_NO_DEFAULT_SMS_APP);
                    }
                });
            } else {
                // Sent by apps that are not user visible, so don't show SIM disambiguation dialog.
                try {
                    ISms iSms = getISmsServiceOrThrow();
                    if (iSms != null) {
                        iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
                                null, null, destinationAddress,
                                scAddress, parts, sentIntents, deliveryIntents,
                                persistMessage, finalPriority, expectMore, finalValidity);
                    }
                } catch (RemoteException e) {
                    Log.e(TAG, "sendMultipartTextMessageInternal (no persist): Couldn't send SMS - "
                            + e.getMessage());
                    notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
                }
            }
        } 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);
            }
            sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
                    sentIntent, deliveryIntent, persistMessage, priority, expectMore,
                    validityPeriod);
        }
    }

    /**
     * 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>
     *
     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
     * suitable default subscription could be found. In this case, if {@code sentIntent} is
     * non-null, then the {@link PendingIntent} will be sent with an error code
     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
     * where this operation may fail.
     * </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>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_NETWORK_REJECT</code><br>
     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_INVALID_STATE</code><br>
     *  <code>RESULT_NO_MEMORY</code><br>
     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_SYSTEM_ERROR</code><br>
     *  <code>RESULT_MODEM_ERROR</code><br>
     *  <code>RESULT_NETWORK_ERROR</code><br>
     *  <code>RESULT_ENCODING_ERROR</code><br>
     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_INTERNAL_ERROR</code><br>
     *  <code>RESULT_NO_RESOURCES</code><br>
     *  <code>RESULT_CANCELLED</code><br>
     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
     *  <code>RESULT_RIL_INVALID_STATE</code><br>
     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
     *  <code>RESULT_RIL_NO_MEMORY</code><br>
     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
     *  <code>RESULT_RIL_MODEM_ERR</code><br>
     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
     *  <code>RESULT_RIL_CANCELLED</code><br>
     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
     *  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");
        }

        resolveSubscriptionForOperation(new SubscriptionResolverResult() {
            @Override
            public void onSuccess(int subId) {
                try {
                    ISms iSms = getISmsServiceOrThrow();
                    iSms.sendDataForSubscriber(subId, null, null, destinationAddress, scAddress,
                            destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
                } catch (RemoteException e) {
                    Log.e(TAG, "sendDataMessage: Couldn't send SMS - Exception: " + e.getMessage());
                    notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
                }
            }
            @Override
            public void onFailure() {
                notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
            }
        });
    }

    /**
     * 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 changes.
     *
     * <p class="note"><strong>Note:</strong> For devices that support multiple active subscriptions
     * at a time, SmsManager will track the subscription set by the user as the default SMS
     * subscription. If the user has not set a default, {@link SmsManager} may
     * start an activity to kick off a subscription disambiguation dialog. Most operations will not
     * complete until the user has chosen the subscription that will be associated with the
     * operation. If the user cancels the dialog without choosing a subscription, one of the
     * following will happen, depending on the target SDK version of the application. For
     * compatibility purposes, if the target SDK level is <= 28, telephony will still send the SMS
     * over the first available subscription. If the target SDK level is > 28, the operation will
     * fail to complete.
     * </p>
     *
     * <p class="note"><strong>Note:</strong> If this method is used to perform an operation on a
     * device that has multiple active subscriptions, the user has not set a default SMS
     * subscription, and the operation is being performed while the application is not in the
     * foreground, the SMS disambiguation dialog will not be shown. The result of the operation will
     * conclude as if the user cancelled the disambiguation dialog and the operation will finish as
     * outlined above, depending on the target SDK version of the calling application. It is safer
     * to use {@link #getSmsManagerForSubscriptionId(int)} if the application will perform the
     * operation while in the background because this can cause unpredictable results, such as the
     * operation being sent over the wrong subscription or failing completely, depending on the
     * user's default SMS subscription setting.
     * </p>
     *
     * @return the {@link SmsManager} associated with the default subscription id.
     *
     * @see SubscriptionManager#getDefaultSmsSubscriptionId()
     */
    public static SmsManager getDefault() {
        return sInstance;
    }

    /**
     * Get the instance of the SmsManager associated with a particular subscription ID.
     *
     * <p class="note"><strong>Note:</strong> Constructing an {@link SmsManager} in this manner will
     * never cause an SMS disambiguation dialog to appear, unlike {@link #getDefault()}.
     * </p>
     *
     * @param subId an SMS subscription ID, typically accessed using {@link SubscriptionManager}
     * @return the instance of the SmsManager associated with subscription
     *
     * @see SubscriptionManager#getActiveSubscriptionInfoList()
     * @see SubscriptionManager#getDefaultSmsSubscriptionId()
     */
    public static SmsManager getSmsManagerForSubscriptionId(int subId) {
        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).
     *
     * <p class="note"><strong>Note:</strong> This method used to display a disambiguation dialog to
     * the user asking them to choose a default subscription to send SMS messages over if they
     * haven't chosen yet. Starting in API level 29, we allow the user to not have a default set as
     * a valid option for the default SMS subscription on multi-SIM devices. We no longer show the
     * disambiguation dialog and return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if the
     * device has multiple active subscriptions and no default is set.
     * </p>
     *
     * @return associated subscription ID or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if
     * the default subscription id cannot be determined or the device has multiple active
     * subscriptions and and no default is set ("ask every time") by the user.
     */
    public int getSubscriptionId() {
        try {
            return (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
                    ? getISmsServiceOrThrow().getPreferredSmsSubscription() : mSubId;
        } catch (RemoteException e) {
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        }
    }

    /**
     * Resolves the subscription id to use for the associated operation if
     * {@link #getSubscriptionId()} returns {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
     *
     * If app targets API level 28 or below and they are either sending the SMS from the background
     * or the device has more than one active subscription available and no default is set, we will
     * use the first logical slot to send the SMS and possibly fail later in the SMS sending
     * process.
     *
     * Regardless of the API level, if the app is the foreground app, then we will show the SMS
     * disambiguation dialog. If the app is in the background and tries to perform an operation, we
     * will not show the disambiguation dialog.
     *
     * See {@link #getDefault()} for a detailed explanation of how this method operates.
     *
     * @param resolverResult The callback that will be called when the subscription is resolved or
     *                       fails to be resolved.
     */
    private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) {
        int subId = getSubscriptionId();
        boolean isSmsSimPickActivityNeeded = false;
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                // Determines if the SMS SIM pick activity should be shown. This is only shown if:
                // 1) The device has multiple active subscriptions and an SMS default subscription
                //    hasn't been set, and
                // 2) SmsManager is being called from the foreground app.
                // Android does not allow background activity starts, so we need to block this.
                // if Q+, do not perform requested operation if these two operations are not set. If
                // <P, perform these operations on phone 0 (for compatibility purposes, since we
                // used to not wait for the result of this activity).
                isSmsSimPickActivityNeeded = iSms.isSmsSimPickActivityNeeded(subId);
            }
        } catch (RemoteException ex) {
            Log.e(TAG, "resolveSubscriptionForOperation", ex);
        }
        if (!isSmsSimPickActivityNeeded) {
            sendResolverResult(resolverResult, subId, false /*pickActivityShown*/);
            return;
        }
        // We need to ask the user pick an appropriate subid for the operation.
        Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for calling"
                + " package. ");
        try {
            // Create the SMS pick activity and call back once the activity is complete. Can't do
            // it here because we do not have access to the activity context that is performing this
            // operation.
            // Requires that the calling process has the SEND_SMS permission.
            getITelephony().enqueueSmsPickResult(null, null,
                    new IIntegerConsumer.Stub() {
                        @Override
                        public void accept(int subId) {
                            // Runs on binder thread attached to this app's process.
                            sendResolverResult(resolverResult, subId, true /*pickActivityShown*/);
                        }
                    });
        } catch (RemoteException ex) {
            Log.e(TAG, "Unable to launch activity", ex);
            // pickActivityShown is true here because we want to call sendResolverResult and always
            // have this operation fail. This is because we received a RemoteException here, which
            // means that telephony is not available and the next operation to Telephony will fail
            // as well anyways, so we might as well shortcut fail here first.
            sendResolverResult(resolverResult, subId, true /*pickActivityShown*/);
        }
    }

    /**
     * To check the SDK version for SmsManager.sendResolverResult method.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
    private static final long GET_TARGET_SDK_VERSION_CODE_CHANGE = 145147528L;

    private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId,
            boolean pickActivityShown) {
        if (SubscriptionManager.isValidSubscriptionId(subId)) {
            resolverResult.onSuccess(subId);
            return;
        }

        if (!Compatibility.isChangeEnabled(GET_TARGET_SDK_VERSION_CODE_CHANGE)
                && !pickActivityShown) {
            // Do not fail, return a success with an INVALID subid for apps targeting P or below
            // that tried to perform an operation and the SMS disambiguation dialog was never shown,
            // as these applications may not have been written to handle the failure case properly.
            // This will resolve to performing the operation on phone 0 in telephony.
            resolverResult.onSuccess(subId);
        } else {
            // Fail if the app targets Q or above or it targets P and below and the disambiguation
            // dialog was shown and the user clicked out of it.
            resolverResult.onFailure();
        }
    }

    private static ITelephony getITelephony() {
        ITelephony binder = ITelephony.Stub.asInterface(
                TelephonyFrameworkInitializer
                        .getTelephonyServiceManager()
                        .getTelephonyServiceRegisterer()
                        .get());
        if (binder == null) {
            throw new RuntimeException("Could not find Telephony Service.");
        }
        return binder;
    }

    private static void notifySmsError(PendingIntent pendingIntent, int error) {
        if (pendingIntent != null) {
            try {
                pendingIntent.send(error);
            } catch (PendingIntent.CanceledException e) {
                // Don't worry about it, we do not need to notify the caller if this is the case.
            }
        }
    }

    private static void notifySmsError(List<PendingIntent> pendingIntents, int error) {
        if (pendingIntents != null) {
            for (PendingIntent pendingIntent : pendingIntents) {
                notifySmsError(pendingIntent, error);
            }
        }
    }

    /**
     * Returns the ISms service, or throws an UnsupportedOperationException if
     * the service does not exist.
     */
    private static ISms getISmsServiceOrThrow() {
        ISms iSms = TelephonyManager.getSmsService();
        if (iSms == null) {
            throw new UnsupportedOperationException("Sms is not supported");
        }
        return iSms;
    }

    private static ISms getISmsService() {
        return TelephonyManager.getSmsService();
    }

    /**
     * Copies 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.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @param smsc the SMSC for this messag or null for the default SMSC.
     * @param pdu the raw PDU to store.
     * @param status message status. One of these status:
     *               <code>STATUS_ON_ICC_READ</code>
     *               <code>STATUS_ON_ICC_UNREAD</code>
     *               <code>STATUS_ON_ICC_SENT</code>
     *               <code>STATUS_ON_ICC_UNSENT</code>
     * @return true for success. Otherwise false.
     *
     * @throws IllegalArgumentException if pdu is null.
     * @hide
     */
    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
    public boolean copyMessageToIcc(
            @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) {
        boolean success = false;

        if (pdu == null) {
            throw new IllegalArgumentException("pdu is null");
        }
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                success = iSms.copyMessageToIccEfForSubscriber(getSubscriptionId(),
                        null,
                        status, pdu, smsc);
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return success;
    }

    /**
     * Deletes 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.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @param messageIndex This is the same index used to access a message
     * from {@link #getMessagesFromIcc()}.
     * @return true for success, false if the operation fails. Failure can be due to IPC failure,
     * RIL/modem error which results in SMS failed to be deleted on SIM
     *
     * {@hide}
     */
    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
    public boolean deleteMessageFromIcc(int messageIndex) {
        boolean success = false;

        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
                        null,
                        messageIndex, STATUS_ON_ICC_FREE, null /* 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.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @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}
     */
    @UnsupportedAppUsage
    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
    public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
        boolean success = false;

        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
                        null,
                        messageIndex, newStatus, pdu);
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return success;
    }

    /**
     * Retrieves all messages currently stored on the ICC.
     * ICC (Integrated Circuit Card) is the card of the device.
     * For example, this can be the SIM or USIM for GSM.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @return <code>List</code> of <code>SmsMessage</code> objects
     *
     * {@hide}
     */
    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
    public @NonNull List<SmsMessage> getMessagesFromIcc() {
        return getAllMessagesFromIcc();
    }

    /**
     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
     *
     * This is similar to {@link #getMessagesFromIcc} except that it will return ArrayList.
     * Suggested to use {@link #getMessagesFromIcc} instead.
     *
     * {@hide}
     */
    @UnsupportedAppUsage
    public ArrayList<SmsMessage> getAllMessagesFromIcc() {
        List<SmsRawData> records = null;

        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                records = iSms.getAllMessagesFromIccEfForSubscriber(
                        getSubscriptionId(),
                        null);
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return createMessageListFromRawRecords(records);
    }

    /**
     * Enable reception of cell broadcast (SMS-CB) messages with the given
     * message identifier range and RAN type. The RAN type specifies if this message ID
     * belongs 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.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}, which will result in the operation
     * being completed on the subscription associated with logical slot 0. Use
     * {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is performed on the
     * correct subscription.
     * </p>
     *
     * <p>Requires {@link android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST}</p>
     *
     * @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 the message format as defined in {@link SmsCbMessage}
     * @return true if successful, false if the modem reports a failure (e.g. the given range or
     * RAN type is invalid).
     * @see #disableCellBroadcastRange(int, int, int)
     *
     * @throws IllegalArgumentException if endMessageId < startMessageId
     * {@hide}
     */
    @SystemApi
    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId,
            @android.telephony.SmsCbMessage.MessageFormat int ranType) {
        boolean success = false;

        if (endMessageId < startMessageId) {
            throw new IllegalArgumentException("endMessageId < startMessageId");
        }
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
                // the default phone internally.
                success = iSms.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.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * <p>Requires {@link android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST}</p>
     *
     * @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 the message format as defined in {@link SmsCbMessage}
     * @return true if successful, false if the modem reports a failure (e.g. the given range or
     * RAN type is invalid).
     *
     * @see #enableCellBroadcastRange(int, int, int)
     *
     * @throws IllegalArgumentException if endMessageId < startMessageId
     * {@hide}
     */
    @SystemApi
    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId,
            @android.telephony.SmsCbMessage.MessageFormat int ranType) {
        boolean success = false;

        if (endMessageId < startMessageId) {
            throw new IllegalArgumentException("endMessageId < startMessageId");
        }
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
                // the default phone internally.
                success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(),
                        startMessageId, endMessageId, ranType);
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return success;
    }

    /**
     * Creates a list of <code>SmsMessage</code>s from a list of SmsRawData records.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @param records SMS EF records.
     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
     */
    private 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(),
                            getSubscriptionId());
                    if (sms != null) {
                        messages.add(sms);
                    }
                }
            }
        }
        return messages;
    }

    /**
     * SMS over IMS is supported if IMS is registered and SMS is supported
     * on IMS.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @return true if SMS over IMS is supported, false otherwise
     *
     * @see #getImsSmsFormat()
     *
     * @hide
     */
    public boolean isImsSmsSupported() {
        boolean boSupported = false;
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                boSupported = iSms.isImsSmsSupportedForSubscriber(getSubscriptionId());
            }
        } catch (RemoteException ex) {
            // ignore it
        }
        return boSupported;
    }

    /**
     * Gets SMS format supported on IMS.  SMS over IMS format is either 3GPP or 3GPP2.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @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 iSms = getISmsService();
            if (iSms != null) {
                format = iSms.getImsSmsFormatForSubscriber(getSubscriptionId());
            }
        } catch (RemoteException ex) {
            // ignore it
        }
        return format;
    }

    /**
     * Get default sms subscription id.
     *
     * <p class="note"><strong>Note:</strong>This returns a value different from
     * {@link SubscriptionManager#getDefaultSmsSubscriptionId} if the user has not chosen a default.
     * In this case it returns the active subscription id if there's only one active subscription
     * available.
     *
     * @return the user-defined default SMS subscription id, or the active subscription id if
     * there's only one active subscription available, otherwise
     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
     */
    public static int getDefaultSmsSubscriptionId() {
        try {
            return getISmsService().getPreferredSmsSubscription();
        } catch (RemoteException e) {
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        } catch (NullPointerException e) {
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        }
    }

    /**
     * Get SMS prompt property,  enabled or not
     *
     * @return true if enabled, false otherwise
     * @hide
     */
    @UnsupportedAppUsage
    public boolean isSMSPromptEnabled() {
        ISms iSms = null;
        try {
            iSms = TelephonyManager.getSmsService();
            return iSms.isSMSPromptEnabled();
        } catch (RemoteException ex) {
            return false;
        } catch (NullPointerException ex) {
            return false;
        }
    }

    /**
     * Gets the total capacity of SMS storage on RUIM and SIM cards
     * <p>
     * This is the number of 176 byte EF-SMS records which can be stored on the RUIM or SIM card.
     * <p>
     * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information
     *
     * @return the total number of SMS records which can be stored on the RUIM or SIM cards.
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    public int getSmsCapacityOnIcc() {
        int ret = 0;
        try {
            ISms iccISms = getISmsService();
            if (iccISms != null) {
                ret = iccISms.getSmsCapacityOnIccForSubscriber(getSubscriptionId());
            }
        } catch (RemoteException ex) {
            //ignore it
        }
        return ret;
    }

    /** @hide */
    @IntDef(prefix = { "STATUS_ON_ICC_" }, value = {
            STATUS_ON_ICC_FREE,
            STATUS_ON_ICC_READ,
            STATUS_ON_ICC_UNREAD,
            STATUS_ON_ICC_SENT,
            STATUS_ON_ICC_UNSENT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StatusOnIcc {}

    // see SmsMessage.getStatusOnIcc

    /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    public static final int STATUS_ON_ICC_FREE      = 0;

    /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    public static final int STATUS_ON_ICC_READ      = 1;

    /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    public static final int STATUS_ON_ICC_UNREAD    = 3;

    /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    public static final int STATUS_ON_ICC_SENT      = 5;

    /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    public static final int STATUS_ON_ICC_UNSENT    = 7;

    // SMS send failure result codes

    /** @hide */
    @IntDef(prefix = { "RESULT" }, value = {
            RESULT_ERROR_NONE,
            RESULT_ERROR_GENERIC_FAILURE,
            RESULT_ERROR_RADIO_OFF,
            RESULT_ERROR_NULL_PDU,
            RESULT_ERROR_NO_SERVICE,
            RESULT_ERROR_LIMIT_EXCEEDED,
            RESULT_ERROR_FDN_CHECK_FAILURE,
            RESULT_ERROR_SHORT_CODE_NOT_ALLOWED,
            RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED,
            RESULT_RADIO_NOT_AVAILABLE,
            RESULT_NETWORK_REJECT,
            RESULT_INVALID_ARGUMENTS,
            RESULT_INVALID_STATE,
            RESULT_NO_MEMORY,
            RESULT_INVALID_SMS_FORMAT,
            RESULT_SYSTEM_ERROR,
            RESULT_MODEM_ERROR,
            RESULT_NETWORK_ERROR,
            RESULT_INVALID_SMSC_ADDRESS,
            RESULT_OPERATION_NOT_ALLOWED,
            RESULT_INTERNAL_ERROR,
            RESULT_NO_RESOURCES,
            RESULT_CANCELLED,
            RESULT_REQUEST_NOT_SUPPORTED,
            RESULT_NO_BLUETOOTH_SERVICE,
            RESULT_INVALID_BLUETOOTH_ADDRESS,
            RESULT_BLUETOOTH_DISCONNECTED,
            RESULT_UNEXPECTED_EVENT_STOP_SENDING,
            RESULT_SMS_BLOCKED_DURING_EMERGENCY,
            RESULT_SMS_SEND_RETRY_FAILED,
            RESULT_REMOTE_EXCEPTION,
            RESULT_NO_DEFAULT_SMS_APP,
            RESULT_RIL_RADIO_NOT_AVAILABLE,
            RESULT_RIL_SMS_SEND_FAIL_RETRY,
            RESULT_RIL_NETWORK_REJECT,
            RESULT_RIL_INVALID_STATE,
            RESULT_RIL_INVALID_ARGUMENTS,
            RESULT_RIL_NO_MEMORY,
            RESULT_RIL_REQUEST_RATE_LIMITED,
            RESULT_RIL_INVALID_SMS_FORMAT,
            RESULT_RIL_SYSTEM_ERR,
            RESULT_RIL_ENCODING_ERR,
            RESULT_RIL_INVALID_SMSC_ADDRESS,
            RESULT_RIL_MODEM_ERR,
            RESULT_RIL_NETWORK_ERR,
            RESULT_RIL_INTERNAL_ERR,
            RESULT_RIL_REQUEST_NOT_SUPPORTED,
            RESULT_RIL_INVALID_MODEM_STATE,
            RESULT_RIL_NETWORK_NOT_READY,
            RESULT_RIL_OPERATION_NOT_ALLOWED,
            RESULT_RIL_NO_RESOURCES,
            RESULT_RIL_CANCELLED,
            RESULT_RIL_SIM_ABSENT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Result {}

    /**
     * No error.
     */
    public static final int RESULT_ERROR_NONE    = 0;

    /** Generic failure cause */
    public static final int RESULT_ERROR_GENERIC_FAILURE    = 1;

    /** Failed because radio was explicitly turned off */
    public static final int RESULT_ERROR_RADIO_OFF          = 2;

    /** Failed because no pdu provided */
    public static final int RESULT_ERROR_NULL_PDU           = 3;

    /** Failed because service is currently unavailable */
    public static final int RESULT_ERROR_NO_SERVICE         = 4;

    /** Failed because we reached the sending queue limit. */
    public static final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;

    /**
     * Failed because FDN is enabled.
     */
    public static final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;

    /** Failed because user denied the sending of this short code. */
    public static final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;

    /** Failed because the user has denied this app ever send premium short codes. */
    public static final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;

    /**
     * Failed because the radio was not available
     */
    public static final int RESULT_RADIO_NOT_AVAILABLE = 9;

    /**
     * Failed because of network rejection
     */
    public static final int RESULT_NETWORK_REJECT = 10;

    /**
     * Failed because of invalid arguments
     */
    public static final int RESULT_INVALID_ARGUMENTS = 11;

    /**
     * Failed because of an invalid state
     */
    public static final int RESULT_INVALID_STATE = 12;

    /**
     * Failed because there is no memory
     */
    public static final int RESULT_NO_MEMORY = 13;

    /**
     * Failed because the sms format is not valid
     */
    public static final int RESULT_INVALID_SMS_FORMAT = 14;

    /**
     * Failed because of a system error
     */
    public static final int RESULT_SYSTEM_ERROR = 15;

    /**
     * Failed because of a modem error
     */
    public static final int RESULT_MODEM_ERROR = 16;

    /**
     * Failed because of a network error
     */
    public static final int RESULT_NETWORK_ERROR = 17;

    /**
     * Failed because of an encoding error
     */
    public static final int RESULT_ENCODING_ERROR = 18;

    /**
     * Failed because of an invalid smsc address
     */
    public static final int RESULT_INVALID_SMSC_ADDRESS = 19;

    /**
     * Failed because the operation is not allowed
     */
    public static final int RESULT_OPERATION_NOT_ALLOWED = 20;

    /**
     * Failed because of an internal error
     */
    public static final int RESULT_INTERNAL_ERROR = 21;

    /**
     * Failed because there are no resources
     */
    public static final int RESULT_NO_RESOURCES = 22;

    /**
     * Failed because the operation was cancelled
     */
    public static final int RESULT_CANCELLED = 23;

    /**
     * Failed because the request is not supported
     */
    public static final int RESULT_REQUEST_NOT_SUPPORTED = 24;

    /**
     * Failed sending via bluetooth because the bluetooth service is not available
     */
    public static final int RESULT_NO_BLUETOOTH_SERVICE = 25;

    /**
     * Failed sending via bluetooth because the bluetooth device address is invalid
     */
    public static final int RESULT_INVALID_BLUETOOTH_ADDRESS = 26;

    /**
     * Failed sending via bluetooth because bluetooth disconnected
     */
    public static final int RESULT_BLUETOOTH_DISCONNECTED = 27;

    /**
     * Failed sending because the user denied or canceled the dialog displayed for a premium
     * shortcode sms or rate-limited sms.
     */
    public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28;

    /**
     * Failed sending during an emergency call
     */
    public static final int RESULT_SMS_BLOCKED_DURING_EMERGENCY = 29;

    /**
     * Failed to send an sms retry
     */
    public static final int RESULT_SMS_SEND_RETRY_FAILED = 30;

    /**
     * Set by BroadcastReceiver to indicate a remote exception while handling a message.
     */
    public static final int RESULT_REMOTE_EXCEPTION = 31;

    /**
     * Set by BroadcastReceiver to indicate there's no default sms app.
     */
    public static final int RESULT_NO_DEFAULT_SMS_APP = 32;

    // Radio Error results

    /**
     * The radio did not start or is resetting.
     */
    public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100;

    /**
     * The radio failed to send the sms and needs to retry.
     */
    public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101;

    /**
     * The sms request was rejected by the network.
     */
    public static final int RESULT_RIL_NETWORK_REJECT = 102;

    /**
     * The radio returned an unexpected request for the current state.
     */
    public static final int RESULT_RIL_INVALID_STATE = 103;

    /**
     * The radio received invalid arguments in the request.
     */
    public static final int RESULT_RIL_INVALID_ARGUMENTS = 104;

    /**
     * The radio didn't have sufficient memory to process the request.
     */
    public static final int RESULT_RIL_NO_MEMORY = 105;

    /**
     * The radio denied the operation due to overly-frequent requests.
     */
    public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106;

    /**
     * The radio returned an error indicating invalid sms format.
     */
    public static final int RESULT_RIL_INVALID_SMS_FORMAT = 107;

    /**
     * The radio encountered a platform or system error.
     */
    public static final int RESULT_RIL_SYSTEM_ERR = 108;

    /**
     * The SMS message was not encoded properly.
     */
    public static final int RESULT_RIL_ENCODING_ERR = 109;

    /**
     * The specified SMSC address was invalid.
     */
    public static final int RESULT_RIL_INVALID_SMSC_ADDRESS = 110;

    /**
     * The vendor RIL received an unexpected or incorrect response.
     */
    public static final int RESULT_RIL_MODEM_ERR = 111;

    /**
     * The radio received an error from the network.
     */
    public static final int RESULT_RIL_NETWORK_ERR = 112;

    /**
     * The modem encountered an unexpected error scenario while handling the request.
     */
    public static final int RESULT_RIL_INTERNAL_ERR = 113;

    /**
     * The request was not supported by the radio.
     */
    public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114;

    /**
     * The radio cannot process the request in the current modem state.
     */
    public static final int RESULT_RIL_INVALID_MODEM_STATE = 115;

    /**
     * The network is not ready to perform the request.
     */
    public static final int RESULT_RIL_NETWORK_NOT_READY = 116;

    /**
     * The radio reports the request is not allowed.
     */
    public static final int RESULT_RIL_OPERATION_NOT_ALLOWED = 117;

    /**
     * There are insufficient resources to process the request.
     */
    public static final int RESULT_RIL_NO_RESOURCES = 118;

    /**
     * The request has been cancelled.
     */
    public static final int RESULT_RIL_CANCELLED = 119;

    /**
     * The radio failed to set the location where the CDMA subscription
     * can be retrieved because the SIM or RUIM is absent.
     */
    public static final int RESULT_RIL_SIM_ABSENT = 120;

    // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION}

    /**
     * SMS receive dispatch failure.
     */
    public static final int RESULT_RECEIVE_DISPATCH_FAILURE = 500;

    /**
     * SMS receive injected null PDU.
     */
    public static final int RESULT_RECEIVE_INJECTED_NULL_PDU = 501;

    /**
     * SMS receive encountered runtime exception.
     */
    public static final int RESULT_RECEIVE_RUNTIME_EXCEPTION = 502;

    /**
     * SMS received null message from the radio interface layer.
     */
    public static final int RESULT_RECEIVE_NULL_MESSAGE_FROM_RIL = 503;

    /**
     * SMS short code received while the phone is in encrypted state.
     */
    public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504;

    /**
     * SMS receive encountered an SQL exception.
     */
    public static final int RESULT_RECEIVE_SQL_EXCEPTION = 505;

    /**
     * SMS receive an exception parsing a uri.
     */
    public static final int RESULT_RECEIVE_URI_EXCEPTION = 506;



    /**
     * Send an MMS message
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @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");
        }
        MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
        if (m != null) {
            m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
                    sentIntent, 0L /* messageId */);
        }
    }

    /**
     * Download an MMS message from carrier by a given location URL
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @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");
        }
        MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
        if (m != null) {
            m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
                    configOverrides, downloadedIntent, 0L /* messageId */);
        }
    }

    // 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";

    /**
     * Get carrier-dependent MMS configuration values.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation dialog.
     * If this method is called on a device that has multiple active subscriptions, this {@link
     * SmsManager} instance has been created with {@link #getDefault()}, and no user-defined default
     * subscription is defined, the subscription ID associated with this message will be INVALID,
     * which will result in the operation being completed on the subscription associated with
     * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is
     * performed on the correct subscription.
     * </p>
     *
     * @return the bundle key/values pairs that contains MMS configuration values
     *  or an empty Bundle if they cannot be found.
     */
    @NonNull public Bundle getCarrierConfigValues() {
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                return iSms.getCarrierConfigValuesForSubscriber(getSubscriptionId());
            }
        } catch (RemoteException ex) {
            // ignore it
        }
        return new Bundle();
    }

    /**
     * Create a single use app specific incoming SMS request for 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.
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @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(),
                    null, intent);

        } catch (RemoteException ex) {
            ex.rethrowFromSystemServer();
            return null;
        }
    }

    /**
     * callback for providing asynchronous sms messages for financial app.
     */
    public abstract static class FinancialSmsCallback {
        /**
         * Callback to send sms messages back to financial app asynchronously.
         *
         * @param msgs SMS messages.
         */
        public abstract void onFinancialSmsMessages(CursorWindow msgs);
    };

    /**
     * Get SMS messages for the calling financial app.
     * The result will be delivered asynchronously in the passing in callback interface.
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @param params the parameters to filter SMS messages returned.
     * @param executor the executor on which callback will be invoked.
     * @param callback a callback to receive CursorWindow with SMS messages.
     *
     */
    @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS)
    public void getSmsMessagesForFinancialApp(
            Bundle params,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull FinancialSmsCallback callback) {
        // This API is not functional and thus removed to avoid future confusion.
    }

    /**
     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
     * The prefixes is a list of prefix {@code String} separated by this delimiter.
     * @hide
     */
    public static final String REGEX_PREFIX_DELIMITER = ",";
    /**
     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
     * The success status to be added into the intent to be sent to the calling package.
     * @hide
     */
    public static final int RESULT_STATUS_SUCCESS = 0;
    /**
     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
     * The timeout status to be added into the intent to be sent to the calling package.
     * @hide
     */
    public static final int RESULT_STATUS_TIMEOUT = 1;
    /**
     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
     * Intent extra key of the retrieved SMS message as a {@code String}.
     * @hide
     */
    public static final String EXTRA_SMS_MESSAGE = "android.telephony.extra.SMS_MESSAGE";
    /**
     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
     * Intent extra key of SMS retriever status, which indicates whether the request for the
     * coming SMS message is SUCCESS or TIMEOUT
     * @hide
     */
    public static final String EXTRA_STATUS = "android.telephony.extra.STATUS";
    /**
     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
     * [Optional] Intent extra key of the retrieved Sim card subscription Id if any. {@code int}
     * @hide
     */
    public static final String EXTRA_SIM_SUBSCRIPTION_ID =
            "android.telephony.extra.SIM_SUBSCRIPTION_ID";

    /**
     * Create a single use app specific incoming SMS request for the calling package.
     *
     * This method returns a token that if included in a subsequent incoming SMS message, and the
     * SMS message has a prefix from the given prefixes list, the provided {@code intent} will be
     * sent with the SMS data to the calling package.
     *
     * The token is only good for one use within a reasonable amount of time. 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.
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @param prefixes this is a list of prefixes string separated by REGEX_PREFIX_DELIMITER. The
     *  matching SMS message should have at least one of the prefixes in the beginning of the
     *  message.
     * @param intent this intent is sent when the matching SMS message is received.
     * @return Token to include in an SMS message.
     */
    @Nullable
    public String createAppSpecificSmsTokenWithPackageInfo(
            @Nullable String prefixes, @NonNull PendingIntent intent) {
        try {
            ISms iccSms = getISmsServiceOrThrow();
            return iccSms.createAppSpecificSmsTokenWithPackageInfo(getSubscriptionId(),
                    null, prefixes, intent);

        } catch (RemoteException ex) {
            ex.rethrowFromSystemServer();
            return null;
        }
    }

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"SMS_CATEGORY_"},
            value = {
                    SmsManager.SMS_CATEGORY_NOT_SHORT_CODE,
                    SmsManager.SMS_CATEGORY_FREE_SHORT_CODE,
                    SmsManager.SMS_CATEGORY_STANDARD_SHORT_CODE,
                    SmsManager.SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE,
                    SmsManager.SMS_CATEGORY_PREMIUM_SHORT_CODE})
    public @interface SmsShortCodeCategory {}

    /**
     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for regular
     * phone numbers.
     * @hide
     */
    @TestApi
    public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0;
    /**
     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for free
     * (no cost) short codes.
     * @hide
     */
    @TestApi
    public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1;
    /**
     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for
     * standard rate (non-premium)
     * short codes.
     * @hide
     */
    @TestApi
    public static final int SMS_CATEGORY_STANDARD_SHORT_CODE = 2;
    /**
     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for possible
     * premium short codes.
     * @hide
     */
    @TestApi
    public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
    /**
     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for
     * premium short codes.
     * @hide
     */
    @TestApi
    public static final int SMS_CATEGORY_PREMIUM_SHORT_CODE = 4;

    /**
     * Check if the destination address is a possible premium short code.
     * NOTE: the caller is expected to strip non-digits from the destination number with
     * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
     *
     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
     * applications or the Telephony framework and will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this message will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
     * operation is performed on the correct subscription.
     * </p>
     *
     * @param destAddress the destination address to test for possible short code
     * @param countryIso the ISO country code
     *
     * @return
     * {@link SmsManager#SMS_CATEGORY_NOT_SHORT_CODE},
     * {@link SmsManager#SMS_CATEGORY_FREE_SHORT_CODE},
     * {@link SmsManager#SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE},
     * {@link SmsManager#SMS_CATEGORY_PREMIUM_SHORT_CODE}, or
     * {@link SmsManager#SMS_CATEGORY_STANDARD_SHORT_CODE}
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
    @TestApi
    public @SmsShortCodeCategory int checkSmsShortCodeDestination(
            String destAddress, String countryIso) {
        try {
            ISms iccISms = getISmsServiceOrThrow();
            if (iccISms != null) {
                return iccISms.checkSmsShortCodeDestination(getSubscriptionId(),
                        null, null, destAddress, countryIso);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "checkSmsShortCodeDestination() RemoteException", e);
        }
        return SmsManager.SMS_CATEGORY_NOT_SHORT_CODE;
    }

    /**
     * Gets the SMSC address from (U)SIM.
     *
     * <p class="note"><strong>Note:</strong> Using this method requires that your app is the
     * default SMS application, or READ_PRIVILEGED_PHONE_STATE permission, or has the carrier
     * privileges.</p>
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this method will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation
     * is performed on the correct subscription.
     * </p>
     *
     * @return the SMSC address string, null if failed.
     */
    @SuppressAutoDoc // for carrier privileges and default SMS application.
    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    @Nullable
    public String getSmscAddress() {
        String smsc = null;

        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                smsc = iSms.getSmscAddressFromIccEfForSubscriber(
                        getSubscriptionId(), null);
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
        return smsc;
    }

    /**
     * Sets the SMSC address on (U)SIM.
     *
     * <p class="note"><strong>Note:</strong> Using this method requires that your app is the
     * default SMS application, or has {@link android.Manifest.permission#MODIFY_PHONE_STATE}
     * permission, or has the carrier privileges.</p>
     *
     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
     * dialog. If this method is called on a device that has multiple active subscriptions, this
     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
     * default subscription is defined, the subscription ID associated with this method will be
     * INVALID, which will result in the operation being completed on the subscription associated
     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation
     * is performed on the correct subscription.
     * </p>
     *
     * @param smsc the SMSC address string.
     * @return true for success, false otherwise. Failure can be due modem returning an error.
     */
    @SuppressAutoDoc // for carrier privileges and default SMS application.
    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
    public boolean setSmscAddress(@NonNull String smsc) {
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                return iSms.setSmscAddressOnIccEfForSubscriber(
                        smsc, getSubscriptionId(), null);
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
        return false;
    }

    /**
     * Gets the premium SMS permission for the specified package. If the package has never
     * been seen before, the default {@link SmsManager#PREMIUM_SMS_CONSENT_UNKNOWN}
     * will be returned.
     * @param packageName the name of the package to query permission
     * @return one of {@link SmsManager#PREMIUM_SMS_CONSENT_UNKNOWN},
     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
     *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    public @PremiumSmsConsent int getPremiumSmsConsent(@NonNull String packageName) {
        int permission = 0;
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                permission = iSms.getPremiumSmsPermission(packageName);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "getPremiumSmsPermission() RemoteException", e);
        }
        return permission;
    }

    /**
     * Sets the premium SMS permission for the specified package and save the value asynchronously
     * to persistent storage.
     * @param packageName the name of the package to set permission
     * @param permission one of {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
     *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
    public void setPremiumSmsConsent(
            @NonNull String packageName, @PremiumSmsConsent int permission) {
        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                iSms.setPremiumSmsPermission(packageName, permission);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "setPremiumSmsPermission() RemoteException", e);
        }
    }

    /**
     * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
     *
     * @return {@code true} if succeeded, otherwise {@code false}.
     *
     * // TODO: Unhide the API in S.
     * @hide
     */
    public boolean resetAllCellBroadcastRanges() {
        boolean success = false;

        try {
            ISms iSms = getISmsService();
            if (iSms != null) {
                // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
                // the default phone internally.
                success = iSms.resetAllCellBroadcastRanges(getSubscriptionId());
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return success;
    }
}
