Add SDK 29 sources.

Test: N/A
Change-Id: Iedb7a31029e003928eb16f7e69ed147e72bb6235
diff --git a/android/telephony/SmsMessage.java b/android/telephony/SmsMessage.java
new file mode 100644
index 0000000..7d4bcb7
--- /dev/null
+++ b/android/telephony/SmsMessage.java
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.os.Binder;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
+import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+
+/**
+ * A Short Message Service message.
+ * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+ */
+public class SmsMessage {
+    private static final String LOG_TAG = "SmsMessage";
+
+    /**
+     * SMS Class enumeration.
+     * See TS 23.038.
+     *
+     */
+    public enum MessageClass{
+        UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+    }
+
+    /** User data text encoding code unit size */
+    public static final int ENCODING_UNKNOWN = 0;
+    public static final int ENCODING_7BIT = 1;
+    public static final int ENCODING_8BIT = 2;
+    public static final int ENCODING_16BIT = 3;
+    /**
+     * @hide This value is not defined in global standard. Only in Korea, this is used.
+     */
+    public static final int ENCODING_KSC5601 = 4;
+
+    /** The maximum number of payload bytes per message */
+    public static final int MAX_USER_DATA_BYTES = 140;
+
+    /**
+     * The maximum number of payload bytes per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     */
+    public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+    /** The maximum number of payload septets per message */
+    public static final int MAX_USER_DATA_SEPTETS = 160;
+
+    /**
+     * The maximum number of payload septets per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     */
+    public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+    /** @hide */
+    @StringDef(prefix = { "FORMAT_" }, value = {
+            FORMAT_3GPP,
+            FORMAT_3GPP2
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Format {}
+
+    /**
+     * Indicates a 3GPP format SMS message.
+     * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent)
+     */
+    public static final String FORMAT_3GPP = "3gpp";
+
+    /**
+     * Indicates a 3GPP2 format SMS message.
+     * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent)
+     */
+    public static final String FORMAT_3GPP2 = "3gpp2";
+
+    /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SmsMessageBase mWrappedSmsMessage;
+
+    /** Indicates the subId
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    private int mSubId = 0;
+
+    /** set Subscription information
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void setSubId(int subId) {
+        mSubId = subId;
+    }
+
+    /** get Subscription information
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getSubId() {
+        return mSubId;
+    }
+
+    public static class SubmitPdu {
+
+        public byte[] encodedScAddress; // Null if not applicable.
+        public byte[] encodedMessage;
+
+        @Override
+        public String toString() {
+            return "SubmitPdu: encodedScAddress = "
+                    + Arrays.toString(encodedScAddress)
+                    + ", encodedMessage = "
+                    + Arrays.toString(encodedMessage);
+        }
+
+        /**
+         * @hide
+         */
+        protected SubmitPdu(SubmitPduBase spb) {
+            this.encodedMessage = spb.encodedMessage;
+            this.encodedScAddress = spb.encodedScAddress;
+        }
+
+    }
+
+    /**
+     * @hide
+     */
+    public SmsMessage(SmsMessageBase smb) {
+        mWrappedSmsMessage = smb;
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU. Guess format based on Voice
+     * technology first, if it fails use other format.
+     * All applications which handle
+     * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+     * intent <b>must</b> now pass the new {@code format} String extra from the intent
+     * into the new method {@code createFromPdu(byte[], String)} which takes an
+     * extra format parameter. This is required in order to correctly decode the PDU on
+     * devices that require support for both 3GPP and 3GPP2 formats at the same time,
+     * such as dual-mode GSM/CDMA and CDMA/LTE phones.
+     * @deprecated Use {@link #createFromPdu(byte[], String)} instead.
+     */
+    @Deprecated
+    public static SmsMessage createFromPdu(byte[] pdu) {
+         SmsMessage message = null;
+
+        // cdma(3gpp2) vs gsm(3gpp) format info was not given,
+        // guess from active voice phone type
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        String format = (PHONE_TYPE_CDMA == activePhone) ?
+                SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
+        return createFromPdu(pdu, format);
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU with the specified message format. The
+     * message format is passed in the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} as the {@code format}
+     * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+     *
+     * @param pdu the message PDU from the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+     * @param format the format extra from the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+     */
+    public static SmsMessage createFromPdu(byte[] pdu, String format) {
+        return createFromPdu(pdu, format, true);
+    }
+
+    private static SmsMessage createFromPdu(byte[] pdu, String format,
+            boolean fallbackToOtherFormat) {
+        if (pdu == null) {
+            Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
+            return null;
+        }
+        SmsMessageBase wrappedMessage;
+        String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP :
+                SmsConstants.FORMAT_3GPP2;
+        if (SmsConstants.FORMAT_3GPP2.equals(format)) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+        } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+        } else {
+            Rlog.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
+            return null;
+        }
+
+        if (wrappedMessage != null) {
+            return new SmsMessage(wrappedMessage);
+        } else {
+            if (fallbackToOtherFormat) {
+                return createFromPdu(pdu, otherFormat, false);
+            } else {
+                Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
+     * +CMT unsolicited response (PDU mode, of course)
+     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
+     *
+     * Only public for debugging and for RIL
+     *
+     * {@hide}
+     */
+    public static SmsMessage newFromCMT(byte[] pdu) {
+        // received SMS in 3GPP format
+        SmsMessageBase wrappedMessage =
+                com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu);
+
+        if (wrappedMessage != null) {
+            return new SmsMessage(wrappedMessage);
+        } else {
+            Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null");
+            return null;
+        }
+    }
+
+    /**
+     * Create an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS record. This should be index in ArrayList
+     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param data Record data.
+     * @return An SmsMessage representing the record.
+     *
+     * @hide
+     */
+    public static SmsMessage createFromEfRecord(int index, byte[] data) {
+        SmsMessageBase wrappedMessage;
+
+        if (isCdmaVoice()) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+                    index, data);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+                    index, data);
+        }
+
+        if (wrappedMessage != null) {
+            return new SmsMessage(wrappedMessage);
+        } else {
+            Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null");
+            return null;
+        }
+    }
+
+    /**
+     * Create an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS record. This should be index in ArrayList
+     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param data Record data.
+     * @param subId Subscription Id of the SMS
+     * @return An SmsMessage representing the record.
+     *
+     * @hide
+     */
+    public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) {
+        SmsMessageBase wrappedMessage;
+
+        if (isCdmaVoice(subId)) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+                    index, data);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+                    index, data);
+        }
+
+        return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+    }
+
+    /**
+     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+     * length in bytes (not hex chars) less the SMSC header
+     *
+     * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
+     * We should probably deprecate it and remove the obsolete test case.
+     */
+    public static int getTPLayerLengthForPDU(String pdu) {
+        if (isCdmaVoice()) {
+            return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+        } else {
+            return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+        }
+    }
+
+    /*
+     * TODO(cleanup): It would make some sense if the result of
+     * preprocessing a message to determine the proper encoding (i.e.
+     * the resulting data structure from calculateLength) could be
+     * passed as an argument to the actual final encoding function.
+     * This would better ensure that the logic behind size calculation
+     * actually matched the encoding.
+     */
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message.
+     *
+     * @param msgBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding
+     *     are counted as single space chars. If false, and if the messageBody contains non-7-bit
+     *     encodable characters, length is calculated using a 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in
+     *     SmsConstants).
+     */
+    public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
+        return calculateLength(msgBody, use7bitOnly, SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message.
+     *
+     * @param msgBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding
+     *     are counted as single space chars. If false, and if the messageBody contains non-7-bit
+     *     encodable characters, length is calculated using a 16-bit encoding.
+     * @param subId Subscription to take SMS format.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in
+     *     SmsConstants).
+     * @hide
+     */
+    public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly, int subId) {
+        // this function is for MO SMS
+        TextEncodingDetails ted =
+                useCdmaFormatForMoSms(subId)
+                        ? com.android.internal.telephony.cdma.SmsMessage.calculateLength(
+                                msgBody, use7bitOnly, true)
+                        : com.android.internal.telephony.gsm.SmsMessage.calculateLength(
+                                msgBody, use7bitOnly);
+        int ret[] = new int[4];
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
+        return ret;
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than the maximum SMS message text
+     * size.
+     *
+     * @param text text, must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order, comprise the original msg text.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static ArrayList<String> fragmentText(String text) {
+        return fragmentText(text, SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than the maximum SMS message text
+     * size.
+     *
+     * @param text text, must not be null.
+     * @param subId Subscription to take SMS format.
+     * @return an <code>ArrayList</code> of strings that, in order, comprise the original msg text.
+     * @hide
+     */
+    public static ArrayList<String> fragmentText(String text, int subId) {
+        // This function is for MO SMS
+        final boolean isCdma = useCdmaFormatForMoSms(subId);
+
+        TextEncodingDetails ted =
+                isCdma
+                        ? com.android.internal.telephony.cdma.SmsMessage.calculateLength(
+                                text, false, true)
+                        : com.android.internal.telephony.gsm.SmsMessage.calculateLength(
+                                text, false);
+
+        // TODO(cleanup): The code here could be rolled into the logic
+        // below cleanly if these MAX_* constants were defined more
+        // flexibly...
+
+        int limit;
+        if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+            int udhLength;
+            if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
+                udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+            } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
+                udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+            } else {
+                udhLength = 0;
+            }
+
+            if (ted.msgCount > 1) {
+                udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+            }
+
+            if (udhLength != 0) {
+                udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
+            }
+
+            limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
+        } else {
+            if (ted.msgCount > 1) {
+                limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+                // If EMS is not supported, break down EMS into single segment SMS
+                // and add page info " x/y".
+                // In the case of UCS2 encoding, we need 8 bytes for this,
+                // but we only have 6 bytes from UDH, so truncate the limit for
+                // each segment by 2 bytes (1 char).
+                // Make sure total number of segments is less than 10.
+                if (!hasEmsSupport() && ted.msgCount < 10) {
+                    limit -= 2;
+                }
+            } else {
+                limit = SmsConstants.MAX_USER_DATA_BYTES;
+            }
+        }
+
+        String newMsgBody = null;
+        Resources r = Resources.getSystem();
+        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
+            newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma);
+        }
+        if (TextUtils.isEmpty(newMsgBody)) {
+            newMsgBody = text;
+        }
+
+        int pos = 0;  // Index in code units.
+        int textLen = newMsgBody.length();
+        ArrayList<String> result = new ArrayList<String>(ted.msgCount);
+        while (pos < textLen) {
+            int nextPos = 0;  // Counts code units.
+            if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+                if (isCdma && ted.msgCount == 1) {
+                    // For a singleton CDMA message, the encoding must be ASCII...
+                    nextPos = pos + Math.min(limit, textLen - pos);
+                } else {
+                    // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(newMsgBody, pos, limit,
+                            ted.languageTable, ted.languageShiftTable);
+                }
+            } else {  // Assume unicode.
+                nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody);
+            }
+            if ((nextPos <= pos) || (nextPos > textLen)) {
+                Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+                          nextPos + " >= " + textLen + ")");
+                break;
+            }
+            result.add(newMsgBody.substring(pos, nextPos));
+            pos = nextPos;
+        }
+        return result;
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message, given the current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio specific (GSM / CDMA)
+     *     alphabet encoding are converted to as a single space characters. If false, a messageBody
+     *     containing non-GSM or non-CDMA alphabet characters are encoded using 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is the encoding type that should be used for the message.
+     */
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+        return calculateLength((CharSequence)messageBody, use7bitOnly);
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message, given the current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio specific (GSM / CDMA)
+     *     alphabet encoding are converted to as a single space characters. If false, a messageBody
+     *     containing non-GSM or non-CDMA alphabet characters are encoded using 16-bit encoding.
+     * @param subId Subscription to take SMS format.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is the encoding type that should be used for the message.
+     * @hide
+     */
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly, int subId) {
+        return calculateLength((CharSequence) messageBody, use7bitOnly, subId);
+    }
+
+    /*
+     * TODO(cleanup): It looks like there is now no useful reason why
+     * apps should generate pdus themselves using these routines,
+     * instead of handing the raw data to SMSDispatcher (and thereby
+     * have the phone process do the encoding).  Moreover, CDMA now
+     * has shared state (in the form of the msgId system property)
+     * which can only be modified by the phone process, and hence
+     * makes the output of these routines incorrect.  Since they now
+     * serve no purpose, they should probably just return null
+     * directly, and be deprecated.  Going further in that direction,
+     * the above parsers of serialized pdu data should probably also
+     * be gotten rid of, hiding all but the necessarily visible
+     * structured data from client apps.  A possible concern with
+     * doing this is that apps may be using these routines to generate
+     * pdus that are then sent elsewhere, some network server, for
+     * example, and that always returning null would thereby break
+     * otherwise useful apps.
+     */
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested) {
+        return getSubmitPdu(
+                scAddress,
+                destinationAddress,
+                message,
+                statusReportRequested,
+                SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message String representation of the message payload.
+     * @param statusReportRequested Indicates whether a report is requested for this message.
+     * @param subId Subscription of the message
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @hide
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested, int subId) {
+        SubmitPduBase spb;
+        if (useCdmaFormatForMoSms(subId)) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, null);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address. null == use default
+     * @param destinationAddress the address of the destination for the message
+     * @param destinationPort the port to deliver the message to at the
+     *        destination
+     * @param data the data for the message
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, short destinationPort, byte[] data,
+            boolean statusReportRequested) {
+        SubmitPduBase spb;
+
+        if (useCdmaFormatForMoSms()) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Returns the address of the SMS service center that relayed this message
+     * or null if there is none.
+     */
+    public String getServiceCenterAddress() {
+        return mWrappedSmsMessage.getServiceCenterAddress();
+    }
+
+    /**
+     * Returns the originating address (sender) of this SMS message in String
+     * form or null if unavailable.
+     *
+     * <p>If the address is a GSM-formatted address, it will be in a format specified by 3GPP
+     * 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2
+     * C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the
+     * should be careful to avoid assumptions about the returned content.
+     *
+     * @return a String representation of the address; null if unavailable.
+     */
+    @Nullable
+    public String getOriginatingAddress() {
+        return mWrappedSmsMessage.getOriginatingAddress();
+    }
+
+    /**
+     * Returns the originating address, or email from address if this message
+     * was from an email gateway. Returns null if originating address
+     * unavailable.
+     */
+    public String getDisplayOriginatingAddress() {
+        return mWrappedSmsMessage.getDisplayOriginatingAddress();
+    }
+
+    /**
+     * Returns the message body as a String, if it exists and is text based.
+     * @return message body if there is one, otherwise null
+     */
+    public String getMessageBody() {
+        return mWrappedSmsMessage.getMessageBody();
+    }
+
+    /**
+     * Returns the class of this message.
+     */
+    public MessageClass getMessageClass() {
+        switch(mWrappedSmsMessage.getMessageClass()) {
+            case CLASS_0: return MessageClass.CLASS_0;
+            case CLASS_1: return MessageClass.CLASS_1;
+            case CLASS_2: return MessageClass.CLASS_2;
+            case CLASS_3: return MessageClass.CLASS_3;
+            default: return MessageClass.UNKNOWN;
+
+        }
+    }
+
+    /**
+     * Returns the message body, or email message body if this message was from
+     * an email gateway. Returns null if message body unavailable.
+     */
+    public String getDisplayMessageBody() {
+        return mWrappedSmsMessage.getDisplayMessageBody();
+    }
+
+    /**
+     * Unofficial convention of a subject line enclosed in parens empty string
+     * if not present
+     */
+    public String getPseudoSubject() {
+        return mWrappedSmsMessage.getPseudoSubject();
+    }
+
+    /**
+     * Returns the service centre timestamp in currentTimeMillis() format
+     */
+    public long getTimestampMillis() {
+        return mWrappedSmsMessage.getTimestampMillis();
+    }
+
+    /**
+     * Returns true if message is an email.
+     *
+     * @return true if this message came through an email gateway and email
+     *         sender / subject / parsed body are available
+     */
+    public boolean isEmail() {
+        return mWrappedSmsMessage.isEmail();
+    }
+
+     /**
+     * @return if isEmail() is true, body of the email sent through the gateway.
+     *         null otherwise
+     */
+    public String getEmailBody() {
+        return mWrappedSmsMessage.getEmailBody();
+    }
+
+    /**
+     * @return if isEmail() is true, email from address of email sent through
+     *         the gateway. null otherwise
+     */
+    public String getEmailFrom() {
+        return mWrappedSmsMessage.getEmailFrom();
+    }
+
+    /**
+     * Get protocol identifier.
+     */
+    public int getProtocolIdentifier() {
+        return mWrappedSmsMessage.getProtocolIdentifier();
+    }
+
+    /**
+     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
+     * SMS
+     */
+    public boolean isReplace() {
+        return mWrappedSmsMessage.isReplace();
+    }
+
+    /**
+     * Returns true for CPHS MWI toggle message.
+     *
+     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
+     *         B.4.2
+     */
+    public boolean isCphsMwiMessage() {
+        return mWrappedSmsMessage.isCphsMwiMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) clear message
+     */
+    public boolean isMWIClearMessage() {
+        return mWrappedSmsMessage.isMWIClearMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) set message
+     */
+    public boolean isMWISetMessage() {
+        return mWrappedSmsMessage.isMWISetMessage();
+    }
+
+    /**
+     * returns true if this message is a "Message Waiting Indication Group:
+     * Discard Message" notification and should not be stored.
+     */
+    public boolean isMwiDontStore() {
+        return mWrappedSmsMessage.isMwiDontStore();
+    }
+
+    /**
+     * returns the user data section minus the user data header if one was
+     * present.
+     */
+    public byte[] getUserData() {
+        return mWrappedSmsMessage.getUserData();
+    }
+
+    /**
+     * Returns the raw PDU for the message.
+     *
+     * @return the raw PDU for the message.
+     */
+    public byte[] getPdu() {
+        return mWrappedSmsMessage.getPdu();
+    }
+
+    /**
+     * Returns the status of the message on the SIM (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the SIM.  These are:
+     *         SmsManager.STATUS_ON_SIM_FREE
+     *         SmsManager.STATUS_ON_SIM_READ
+     *         SmsManager.STATUS_ON_SIM_UNREAD
+     *         SmsManager.STATUS_ON_SIM_SEND
+     *         SmsManager.STATUS_ON_SIM_UNSENT
+     * @deprecated Use getStatusOnIcc instead.
+     */
+    @Deprecated public int getStatusOnSim() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the status of the message on the ICC (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the ICC.  These are:
+     *         SmsManager.STATUS_ON_ICC_FREE
+     *         SmsManager.STATUS_ON_ICC_READ
+     *         SmsManager.STATUS_ON_ICC_UNREAD
+     *         SmsManager.STATUS_ON_ICC_SEND
+     *         SmsManager.STATUS_ON_ICC_UNSENT
+     */
+    public int getStatusOnIcc() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the SIM (1-based index).
+     * @return the record index of the message on the SIM, or -1 if this
+     *         SmsMessage was not created from a SIM SMS EF record.
+     * @deprecated Use getIndexOnIcc instead.
+     */
+    @Deprecated public int getIndexOnSim() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the ICC (1-based index).
+     * @return the record index of the message on the ICC, or -1 if this
+     *         SmsMessage was not created from a ICC SMS EF record.
+     */
+    public int getIndexOnIcc() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * GSM:
+     * For an SMS-STATUS-REPORT message, this returns the status field from
+     * the status report.  This field indicates the status of a previously
+     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
+     * description of values.
+     * CDMA:
+     * For not interfering with status codes from GSM, the value is
+     * shifted to the bits 31-16.
+     * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+     * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+     *
+     * @return 0 indicates the previously sent message was received.
+     *         See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+     *         for a description of other possible values.
+     */
+    public int getStatus() {
+        return mWrappedSmsMessage.getStatus();
+    }
+
+    /**
+     * Return true iff the message is a SMS-STATUS-REPORT message.
+     */
+    public boolean isStatusReportMessage() {
+        return mWrappedSmsMessage.isStatusReportMessage();
+    }
+
+    /**
+     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+     * this message.
+     */
+    public boolean isReplyPathPresent() {
+        return mWrappedSmsMessage.isReplyPathPresent();
+    }
+
+    /**
+     * Determines whether or not to use CDMA format for MO SMS.
+     * If SMS over IMS is supported, then format is based on IMS SMS format,
+     * otherwise format is based on current phone type.
+     *
+     * @return true if Cdma format should be used for MO SMS, false otherwise.
+     */
+    @UnsupportedAppUsage
+    private static boolean useCdmaFormatForMoSms() {
+        // IMS is registered with SMS support, check the SMS format supported
+        return useCdmaFormatForMoSms(SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Determines whether or not to use CDMA format for MO SMS.
+     * If SMS over IMS is supported, then format is based on IMS SMS format,
+     * otherwise format is based on current phone type.
+     *
+     * @param subId Subscription for which phone type is returned.
+     *
+     * @return true if Cdma format should be used for MO SMS, false otherwise.
+     */
+    @UnsupportedAppUsage
+    private static boolean useCdmaFormatForMoSms(int subId) {
+        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+        if (!smsManager.isImsSmsSupported()) {
+            // use Voice technology to determine SMS format.
+            return isCdmaVoice(subId);
+        }
+        // IMS is registered with SMS support, check the SMS format supported
+        return (SmsConstants.FORMAT_3GPP2.equals(smsManager.getImsSmsFormat()));
+    }
+
+    /**
+     * Determines whether or not to current phone type is cdma.
+     *
+     * @return true if current phone type is cdma, false otherwise.
+     */
+    private static boolean isCdmaVoice() {
+        return isCdmaVoice(SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+     /**
+      * Determines whether or not to current phone type is cdma
+      *
+      * @return true if current phone type is cdma, false otherwise.
+      */
+     private static boolean isCdmaVoice(int subId) {
+         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
+         return (PHONE_TYPE_CDMA == activePhone);
+   }
+
+    /**
+     * Decide if the carrier supports long SMS.
+     * {@hide}
+     */
+    public static boolean hasEmsSupport() {
+        if (!isNoEmsSupportConfigListExisted()) {
+            return true;
+        }
+
+        String simOperator;
+        String gid;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
+            gid = TelephonyManager.getDefault().getGroupIdLevel1();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        if (!TextUtils.isEmpty(simOperator)) {
+            for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
+                if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
+                        (TextUtils.isEmpty(currentConfig.mGid1) ||
+                                (!TextUtils.isEmpty(currentConfig.mGid1) &&
+                                        currentConfig.mGid1.equalsIgnoreCase(gid)))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check where to add " x/y" in each SMS segment, begin or end.
+     * {@hide}
+     */
+    public static boolean shouldAppendPageNumberAsPrefix() {
+        if (!isNoEmsSupportConfigListExisted()) {
+            return false;
+        }
+
+        String simOperator;
+        String gid;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
+            gid = TelephonyManager.getDefault().getGroupIdLevel1();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
+            if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
+                (TextUtils.isEmpty(currentConfig.mGid1) ||
+                (!TextUtils.isEmpty(currentConfig.mGid1)
+                && currentConfig.mGid1.equalsIgnoreCase(gid)))) {
+                return currentConfig.mIsPrefix;
+            }
+        }
+        return false;
+    }
+
+    private static class NoEmsSupportConfig {
+        String mOperatorNumber;
+        String mGid1;
+        boolean mIsPrefix;
+
+        public NoEmsSupportConfig(String[] config) {
+            mOperatorNumber = config[0];
+            mIsPrefix = "prefix".equals(config[1]);
+            mGid1 = config.length > 2 ? config[2] : null;
+        }
+
+        @Override
+        public String toString() {
+            return "NoEmsSupportConfig { mOperatorNumber = " + mOperatorNumber
+                    + ", mIsPrefix = " + mIsPrefix + ", mGid1 = " + mGid1 + " }";
+        }
+    }
+
+    private static NoEmsSupportConfig[] mNoEmsSupportConfigList = null;
+    private static boolean mIsNoEmsSupportConfigListLoaded = false;
+
+    private static boolean isNoEmsSupportConfigListExisted() {
+        if (!mIsNoEmsSupportConfigListLoaded) {
+            Resources r = Resources.getSystem();
+            if (r != null) {
+                String[] listArray = r.getStringArray(
+                        com.android.internal.R.array.no_ems_support_sim_operators);
+                if ((listArray != null) && (listArray.length > 0)) {
+                    mNoEmsSupportConfigList = new NoEmsSupportConfig[listArray.length];
+                    for (int i=0; i<listArray.length; i++) {
+                        mNoEmsSupportConfigList[i] = new NoEmsSupportConfig(listArray[i].split(";"));
+                    }
+                }
+                mIsNoEmsSupportConfigListLoaded = true;
+            }
+        }
+
+        if (mNoEmsSupportConfigList != null && mNoEmsSupportConfigList.length != 0) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * {@hide}
+     * Returns the recipient address(receiver) of this SMS message in String form or null if
+     * unavailable.
+     */
+    public String getRecipientAddress() {
+        return mWrappedSmsMessage.getRecipientAddress();
+    }
+}