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