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