Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.telephony;
     18 
     19 import android.os.Parcel;
     20 import android.util.Log;
     21 
     22 import com.android.internal.telephony.GsmAlphabet;
     23 import com.android.internal.telephony.SmsHeader;
     24 import com.android.internal.telephony.SmsMessageBase;
     25 import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
     26 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
     27 
     28 import java.lang.Math;
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 
     32 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
     33 
     34 
     35 /**
     36  * A Short Message Service message.
     37  */
     38 public class SmsMessage {
     39     private static final String LOG_TAG = "SMS";
     40 
     41     /**
     42      * SMS Class enumeration.
     43      * See TS 23.038.
     44      *
     45      */
     46     public enum MessageClass{
     47         UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
     48     }
     49 
     50     /** User data text encoding code unit size */
     51     public static final int ENCODING_UNKNOWN = 0;
     52     public static final int ENCODING_7BIT = 1;
     53     public static final int ENCODING_8BIT = 2;
     54     public static final int ENCODING_16BIT = 3;
     55     /**
     56      * @hide This value is not defined in global standard. Only in Korea, this is used.
     57      */
     58     public static final int ENCODING_KSC5601 = 4;
     59 
     60     /** The maximum number of payload bytes per message */
     61     public static final int MAX_USER_DATA_BYTES = 140;
     62 
     63     /**
     64      * The maximum number of payload bytes per message if a user data header
     65      * is present.  This assumes the header only contains the
     66      * CONCATENATED_8_BIT_REFERENCE element.
     67      */
     68     public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
     69 
     70     /** The maximum number of payload septets per message */
     71     public static final int MAX_USER_DATA_SEPTETS = 160;
     72 
     73     /**
     74      * The maximum number of payload septets per message if a user data header
     75      * is present.  This assumes the header only contains the
     76      * CONCATENATED_8_BIT_REFERENCE element.
     77      */
     78     public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
     79 
     80     /**
     81      * Indicates a 3GPP format SMS message.
     82      * @hide pending API council approval
     83      */
     84     public static final String FORMAT_3GPP = "3gpp";
     85 
     86     /**
     87      * Indicates a 3GPP2 format SMS message.
     88      * @hide pending API council approval
     89      */
     90     public static final String FORMAT_3GPP2 = "3gpp2";
     91 
     92     /** Contains actual SmsMessage. Only public for debugging and for framework layer.
     93      *
     94      * @hide
     95      */
     96     public SmsMessageBase mWrappedSmsMessage;
     97 
     98     public static class SubmitPdu {
     99 
    100         public byte[] encodedScAddress; // Null if not applicable.
    101         public byte[] encodedMessage;
    102 
    103         public String toString() {
    104             return "SubmitPdu: encodedScAddress = "
    105                     + Arrays.toString(encodedScAddress)
    106                     + ", encodedMessage = "
    107                     + Arrays.toString(encodedMessage);
    108         }
    109 
    110         /**
    111          * @hide
    112          */
    113         protected SubmitPdu(SubmitPduBase spb) {
    114             this.encodedMessage = spb.encodedMessage;
    115             this.encodedScAddress = spb.encodedScAddress;
    116         }
    117 
    118     }
    119 
    120     private SmsMessage(SmsMessageBase smb) {
    121         mWrappedSmsMessage = smb;
    122     }
    123 
    124     /**
    125      * Create an SmsMessage from a raw PDU.
    126      *
    127      * <p><b>This method will soon be deprecated</b> and all applications which handle
    128      * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
    129      * intent <b>must</b> now pass the new {@code format} String extra from the intent
    130      * into the new method {@code createFromPdu(byte[], String)} which takes an
    131      * extra format parameter. This is required in order to correctly decode the PDU on
    132      * devices that require support for both 3GPP and 3GPP2 formats at the same time,
    133      * such as dual-mode GSM/CDMA and CDMA/LTE phones.
    134      */
    135     public static SmsMessage createFromPdu(byte[] pdu) {
    136         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    137         String format = (PHONE_TYPE_CDMA == activePhone) ? FORMAT_3GPP2 : FORMAT_3GPP;
    138         return createFromPdu(pdu, format);
    139     }
    140 
    141     /**
    142      * Create an SmsMessage from a raw PDU with the specified message format. The
    143      * message format is passed in the {@code SMS_RECEIVED_ACTION} as the {@code format}
    144      * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
    145      * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
    146      *
    147      * @param pdu the message PDU from the SMS_RECEIVED_ACTION intent
    148      * @param format the format extra from the SMS_RECEIVED_ACTION intent
    149      * @hide pending API council approval
    150      */
    151     public static SmsMessage createFromPdu(byte[] pdu, String format) {
    152         SmsMessageBase wrappedMessage;
    153 
    154         if (FORMAT_3GPP2.equals(format)) {
    155             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
    156         } else if (FORMAT_3GPP.equals(format)) {
    157             wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
    158         } else {
    159             Log.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
    160             return null;
    161         }
    162 
    163         return new SmsMessage(wrappedMessage);
    164     }
    165 
    166     /**
    167      * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
    168      * +CMT unsolicited response (PDU mode, of course)
    169      *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
    170      *
    171      * Only public for debugging and for RIL
    172      *
    173      * {@hide}
    174      */
    175     public static SmsMessage newFromCMT(String[] lines) {
    176         // received SMS in 3GPP format
    177         SmsMessageBase wrappedMessage =
    178                 com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
    179 
    180         return new SmsMessage(wrappedMessage);
    181     }
    182 
    183     /** @hide */
    184     public static SmsMessage newFromParcel(Parcel p) {
    185         // received SMS in 3GPP2 format
    186         SmsMessageBase wrappedMessage =
    187                 com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
    188 
    189         return new SmsMessage(wrappedMessage);
    190     }
    191 
    192     /**
    193      * Create an SmsMessage from an SMS EF record.
    194      *
    195      * @param index Index of SMS record. This should be index in ArrayList
    196      *              returned by SmsManager.getAllMessagesFromSim + 1.
    197      * @param data Record data.
    198      * @return An SmsMessage representing the record.
    199      *
    200      * @hide
    201      */
    202     public static SmsMessage createFromEfRecord(int index, byte[] data) {
    203         SmsMessageBase wrappedMessage;
    204         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    205 
    206         if (PHONE_TYPE_CDMA == activePhone) {
    207             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
    208                     index, data);
    209         } else {
    210             wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
    211                     index, data);
    212         }
    213 
    214         return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
    215     }
    216 
    217     /**
    218      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
    219      * length in bytes (not hex chars) less the SMSC header
    220      *
    221      * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
    222      * We should probably deprecate it and remove the obsolete test case.
    223      */
    224     public static int getTPLayerLengthForPDU(String pdu) {
    225         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    226 
    227         if (PHONE_TYPE_CDMA == activePhone) {
    228             return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
    229         } else {
    230             return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
    231         }
    232     }
    233 
    234     /*
    235      * TODO(cleanup): It would make some sense if the result of
    236      * preprocessing a message to determine the proper encoding (i.e.
    237      * the resulting data structure from calculateLength) could be
    238      * passed as an argument to the actual final encoding function.
    239      * This would better ensure that the logic behind size calculation
    240      * actually matched the encoding.
    241      */
    242 
    243     /**
    244      * Calculates the number of SMS's required to encode the message body and
    245      * the number of characters remaining until the next message.
    246      *
    247      * @param msgBody the message to encode
    248      * @param use7bitOnly if true, characters that are not part of the
    249      *         radio-specific 7-bit encoding are counted as single
    250      *         space chars.  If false, and if the messageBody contains
    251      *         non-7-bit encodable characters, length is calculated
    252      *         using a 16-bit encoding.
    253      * @return an int[4] with int[0] being the number of SMS's
    254      *         required, int[1] the number of code units used, and
    255      *         int[2] is the number of code units remaining until the
    256      *         next message. int[3] is an indicator of the encoding
    257      *         code unit size (see the ENCODING_* definitions in this
    258      *         class).
    259      */
    260     public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
    261         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    262         TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
    263             com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly) :
    264             com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
    265         int ret[] = new int[4];
    266         ret[0] = ted.msgCount;
    267         ret[1] = ted.codeUnitCount;
    268         ret[2] = ted.codeUnitsRemaining;
    269         ret[3] = ted.codeUnitSize;
    270         return ret;
    271     }
    272 
    273     /**
    274      * Divide a message text into several fragments, none bigger than
    275      * the maximum SMS message text size.
    276      *
    277      * @param text text, must not be null.
    278      * @return an <code>ArrayList</code> of strings that, in order,
    279      *   comprise the original msg text
    280      *
    281      * @hide
    282      */
    283     public static ArrayList<String> fragmentText(String text) {
    284         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    285         TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
    286             com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :
    287             com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
    288 
    289         // TODO(cleanup): The code here could be rolled into the logic
    290         // below cleanly if these MAX_* constants were defined more
    291         // flexibly...
    292 
    293         int limit;
    294         if (ted.codeUnitSize == ENCODING_7BIT) {
    295             int udhLength;
    296             if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
    297                 udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
    298             } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
    299                 udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
    300             } else {
    301                 udhLength = 0;
    302             }
    303 
    304             if (ted.msgCount > 1) {
    305                 udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
    306             }
    307 
    308             if (udhLength != 0) {
    309                 udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
    310             }
    311 
    312             limit = MAX_USER_DATA_SEPTETS - udhLength;
    313         } else {
    314             if (ted.msgCount > 1) {
    315                 limit = MAX_USER_DATA_BYTES_WITH_HEADER;
    316             } else {
    317                 limit = MAX_USER_DATA_BYTES;
    318             }
    319         }
    320 
    321         int pos = 0;  // Index in code units.
    322         int textLen = text.length();
    323         ArrayList<String> result = new ArrayList<String>(ted.msgCount);
    324         while (pos < textLen) {
    325             int nextPos = 0;  // Counts code units.
    326             if (ted.codeUnitSize == ENCODING_7BIT) {
    327                 if (activePhone == PHONE_TYPE_CDMA && ted.msgCount == 1) {
    328                     // For a singleton CDMA message, the encoding must be ASCII...
    329                     nextPos = pos + Math.min(limit, textLen - pos);
    330                 } else {
    331                     // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
    332                     nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit,
    333                             ted.languageTable, ted.languageShiftTable);
    334                 }
    335             } else {  // Assume unicode.
    336                 nextPos = pos + Math.min(limit / 2, textLen - pos);
    337             }
    338             if ((nextPos <= pos) || (nextPos > textLen)) {
    339                 Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
    340                           nextPos + " >= " + textLen + ")");
    341                 break;
    342             }
    343             result.add(text.substring(pos, nextPos));
    344             pos = nextPos;
    345         }
    346         return result;
    347     }
    348 
    349     /**
    350      * Calculates the number of SMS's required to encode the message body and
    351      * the number of characters remaining until the next message, given the
    352      * current encoding.
    353      *
    354      * @param messageBody the message to encode
    355      * @param use7bitOnly if true, characters that are not part of the radio
    356      *         specific (GSM / CDMA) alphabet encoding are converted to as a
    357      *         single space characters. If false, a messageBody containing
    358      *         non-GSM or non-CDMA alphabet characters are encoded using
    359      *         16-bit encoding.
    360      * @return an int[4] with int[0] being the number of SMS's required, int[1]
    361      *         the number of code units used, and int[2] is the number of code
    362      *         units remaining until the next message. int[3] is the encoding
    363      *         type that should be used for the message.
    364      */
    365     public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
    366         return calculateLength((CharSequence)messageBody, use7bitOnly);
    367     }
    368 
    369     /*
    370      * TODO(cleanup): It looks like there is now no useful reason why
    371      * apps should generate pdus themselves using these routines,
    372      * instead of handing the raw data to SMSDispatcher (and thereby
    373      * have the phone process do the encoding).  Moreover, CDMA now
    374      * has shared state (in the form of the msgId system property)
    375      * which can only be modified by the phone process, and hence
    376      * makes the output of these routines incorrect.  Since they now
    377      * serve no purpose, they should probably just return null
    378      * directly, and be deprecated.  Going further in that direction,
    379      * the above parsers of serialized pdu data should probably also
    380      * be gotten rid of, hiding all but the necessarily visible
    381      * structured data from client apps.  A possible concern with
    382      * doing this is that apps may be using these routines to generate
    383      * pdus that are then sent elsewhere, some network server, for
    384      * example, and that always returning null would thereby break
    385      * otherwise useful apps.
    386      */
    387 
    388     /**
    389      * Get an SMS-SUBMIT PDU for a destination address and a message.
    390      * This method will not attempt to use any GSM national language 7 bit encodings.
    391      *
    392      * @param scAddress Service Centre address.  Null means use default.
    393      * @return a <code>SubmitPdu</code> containing the encoded SC
    394      *         address, if applicable, and the encoded message.
    395      *         Returns null on encode error.
    396      */
    397     public static SubmitPdu getSubmitPdu(String scAddress,
    398             String destinationAddress, String message, boolean statusReportRequested) {
    399         SubmitPduBase spb;
    400         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    401 
    402         if (PHONE_TYPE_CDMA == activePhone) {
    403             spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
    404                     destinationAddress, message, statusReportRequested, null);
    405         } else {
    406             spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
    407                     destinationAddress, message, statusReportRequested);
    408         }
    409 
    410         return new SubmitPdu(spb);
    411     }
    412 
    413     /**
    414      * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
    415      * This method will not attempt to use any GSM national language 7 bit encodings.
    416      *
    417      * @param scAddress Service Centre address. null == use default
    418      * @param destinationAddress the address of the destination for the message
    419      * @param destinationPort the port to deliver the message to at the
    420      *        destination
    421      * @param data the data for the message
    422      * @return a <code>SubmitPdu</code> containing the encoded SC
    423      *         address, if applicable, and the encoded message.
    424      *         Returns null on encode error.
    425      */
    426     public static SubmitPdu getSubmitPdu(String scAddress,
    427             String destinationAddress, short destinationPort, byte[] data,
    428             boolean statusReportRequested) {
    429         SubmitPduBase spb;
    430         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    431 
    432         if (PHONE_TYPE_CDMA == activePhone) {
    433             spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
    434                     destinationAddress, destinationPort, data, statusReportRequested);
    435         } else {
    436             spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
    437                     destinationAddress, destinationPort, data, statusReportRequested);
    438         }
    439 
    440         return new SubmitPdu(spb);
    441     }
    442 
    443     /**
    444      * Returns the address of the SMS service center that relayed this message
    445      * or null if there is none.
    446      */
    447     public String getServiceCenterAddress() {
    448         return mWrappedSmsMessage.getServiceCenterAddress();
    449     }
    450 
    451     /**
    452      * Returns the originating address (sender) of this SMS message in String
    453      * form or null if unavailable
    454      */
    455     public String getOriginatingAddress() {
    456         return mWrappedSmsMessage.getOriginatingAddress();
    457     }
    458 
    459     /**
    460      * Returns the originating address, or email from address if this message
    461      * was from an email gateway. Returns null if originating address
    462      * unavailable.
    463      */
    464     public String getDisplayOriginatingAddress() {
    465         return mWrappedSmsMessage.getDisplayOriginatingAddress();
    466     }
    467 
    468     /**
    469      * Returns the message body as a String, if it exists and is text based.
    470      * @return message body is there is one, otherwise null
    471      */
    472     public String getMessageBody() {
    473         return mWrappedSmsMessage.getMessageBody();
    474     }
    475 
    476     /**
    477      * Returns the class of this message.
    478      */
    479     public MessageClass getMessageClass() {
    480         return mWrappedSmsMessage.getMessageClass();
    481     }
    482 
    483     /**
    484      * Returns the message body, or email message body if this message was from
    485      * an email gateway. Returns null if message body unavailable.
    486      */
    487     public String getDisplayMessageBody() {
    488         return mWrappedSmsMessage.getDisplayMessageBody();
    489     }
    490 
    491     /**
    492      * Unofficial convention of a subject line enclosed in parens empty string
    493      * if not present
    494      */
    495     public String getPseudoSubject() {
    496         return mWrappedSmsMessage.getPseudoSubject();
    497     }
    498 
    499     /**
    500      * Returns the service centre timestamp in currentTimeMillis() format
    501      */
    502     public long getTimestampMillis() {
    503         return mWrappedSmsMessage.getTimestampMillis();
    504     }
    505 
    506     /**
    507      * Returns true if message is an email.
    508      *
    509      * @return true if this message came through an email gateway and email
    510      *         sender / subject / parsed body are available
    511      */
    512     public boolean isEmail() {
    513         return mWrappedSmsMessage.isEmail();
    514     }
    515 
    516      /**
    517      * @return if isEmail() is true, body of the email sent through the gateway.
    518      *         null otherwise
    519      */
    520     public String getEmailBody() {
    521         return mWrappedSmsMessage.getEmailBody();
    522     }
    523 
    524     /**
    525      * @return if isEmail() is true, email from address of email sent through
    526      *         the gateway. null otherwise
    527      */
    528     public String getEmailFrom() {
    529         return mWrappedSmsMessage.getEmailFrom();
    530     }
    531 
    532     /**
    533      * Get protocol identifier.
    534      */
    535     public int getProtocolIdentifier() {
    536         return mWrappedSmsMessage.getProtocolIdentifier();
    537     }
    538 
    539     /**
    540      * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
    541      * SMS
    542      */
    543     public boolean isReplace() {
    544         return mWrappedSmsMessage.isReplace();
    545     }
    546 
    547     /**
    548      * Returns true for CPHS MWI toggle message.
    549      *
    550      * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
    551      *         B.4.2
    552      */
    553     public boolean isCphsMwiMessage() {
    554         return mWrappedSmsMessage.isCphsMwiMessage();
    555     }
    556 
    557     /**
    558      * returns true if this message is a CPHS voicemail / message waiting
    559      * indicator (MWI) clear message
    560      */
    561     public boolean isMWIClearMessage() {
    562         return mWrappedSmsMessage.isMWIClearMessage();
    563     }
    564 
    565     /**
    566      * returns true if this message is a CPHS voicemail / message waiting
    567      * indicator (MWI) set message
    568      */
    569     public boolean isMWISetMessage() {
    570         return mWrappedSmsMessage.isMWISetMessage();
    571     }
    572 
    573     /**
    574      * returns true if this message is a "Message Waiting Indication Group:
    575      * Discard Message" notification and should not be stored.
    576      */
    577     public boolean isMwiDontStore() {
    578         return mWrappedSmsMessage.isMwiDontStore();
    579     }
    580 
    581     /**
    582      * returns the user data section minus the user data header if one was
    583      * present.
    584      */
    585     public byte[] getUserData() {
    586         return mWrappedSmsMessage.getUserData();
    587     }
    588 
    589     /**
    590      * Returns the raw PDU for the message.
    591      *
    592      * @return the raw PDU for the message.
    593      */
    594     public byte[] getPdu() {
    595         return mWrappedSmsMessage.getPdu();
    596     }
    597 
    598     /**
    599      * Returns the status of the message on the SIM (read, unread, sent, unsent).
    600      *
    601      * @return the status of the message on the SIM.  These are:
    602      *         SmsManager.STATUS_ON_SIM_FREE
    603      *         SmsManager.STATUS_ON_SIM_READ
    604      *         SmsManager.STATUS_ON_SIM_UNREAD
    605      *         SmsManager.STATUS_ON_SIM_SEND
    606      *         SmsManager.STATUS_ON_SIM_UNSENT
    607      * @deprecated Use getStatusOnIcc instead.
    608      */
    609     @Deprecated public int getStatusOnSim() {
    610         return mWrappedSmsMessage.getStatusOnIcc();
    611     }
    612 
    613     /**
    614      * Returns the status of the message on the ICC (read, unread, sent, unsent).
    615      *
    616      * @return the status of the message on the ICC.  These are:
    617      *         SmsManager.STATUS_ON_ICC_FREE
    618      *         SmsManager.STATUS_ON_ICC_READ
    619      *         SmsManager.STATUS_ON_ICC_UNREAD
    620      *         SmsManager.STATUS_ON_ICC_SEND
    621      *         SmsManager.STATUS_ON_ICC_UNSENT
    622      */
    623     public int getStatusOnIcc() {
    624         return mWrappedSmsMessage.getStatusOnIcc();
    625     }
    626 
    627     /**
    628      * Returns the record index of the message on the SIM (1-based index).
    629      * @return the record index of the message on the SIM, or -1 if this
    630      *         SmsMessage was not created from a SIM SMS EF record.
    631      * @deprecated Use getIndexOnIcc instead.
    632      */
    633     @Deprecated public int getIndexOnSim() {
    634         return mWrappedSmsMessage.getIndexOnIcc();
    635     }
    636 
    637     /**
    638      * Returns the record index of the message on the ICC (1-based index).
    639      * @return the record index of the message on the ICC, or -1 if this
    640      *         SmsMessage was not created from a ICC SMS EF record.
    641      */
    642     public int getIndexOnIcc() {
    643         return mWrappedSmsMessage.getIndexOnIcc();
    644     }
    645 
    646     /**
    647      * GSM:
    648      * For an SMS-STATUS-REPORT message, this returns the status field from
    649      * the status report.  This field indicates the status of a previously
    650      * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
    651      * description of values.
    652      * CDMA:
    653      * For not interfering with status codes from GSM, the value is
    654      * shifted to the bits 31-16.
    655      * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
    656      * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
    657      *
    658      * @return 0 indicates the previously sent message was received.
    659      *         See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
    660      *         for a description of other possible values.
    661      */
    662     public int getStatus() {
    663         return mWrappedSmsMessage.getStatus();
    664     }
    665 
    666     /**
    667      * Return true iff the message is a SMS-STATUS-REPORT message.
    668      */
    669     public boolean isStatusReportMessage() {
    670         return mWrappedSmsMessage.isStatusReportMessage();
    671     }
    672 
    673     /**
    674      * Returns true iff the <code>TP-Reply-Path</code> bit is set in
    675      * this message.
    676      */
    677     public boolean isReplyPathPresent() {
    678         return mWrappedSmsMessage.isReplyPathPresent();
    679     }
    680 }
    681