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