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: [<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 & 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