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
17package android.telephony;
18
19import android.os.Parcel;
20import android.util.Log;
21
22import com.android.internal.telephony.GsmAlphabet;
23import com.android.internal.telephony.SmsHeader;
24import com.android.internal.telephony.SmsMessageBase;
25import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
26import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
27
28import java.lang.Math;
29import java.util.ArrayList;
30import java.util.Arrays;
31
32import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
33
34
35/**
36 * A Short Message Service message.
37 */
38public 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