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