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