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 static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; 20 21 import android.Manifest; 22 import android.annotation.IntDef; 23 import android.annotation.IntRange; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.RequiresPermission; 27 import android.annotation.StringDef; 28 import android.annotation.SystemApi; 29 import android.compat.annotation.UnsupportedAppUsage; 30 import android.content.res.Resources; 31 import android.os.Binder; 32 import android.os.Build; 33 import android.text.TextUtils; 34 35 import com.android.internal.telephony.GsmAlphabet; 36 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 37 import com.android.internal.telephony.Sms7BitEncodingTranslator; 38 import com.android.internal.telephony.SmsConstants; 39 import com.android.internal.telephony.SmsHeader; 40 import com.android.internal.telephony.SmsMessageBase; 41 import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; 42 import com.android.internal.telephony.cdma.sms.UserData; 43 import com.android.telephony.Rlog; 44 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 50 /** 51 * A Short Message Service message. 52 * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent 53 */ 54 public class SmsMessage { 55 private static final String LOG_TAG = "SmsMessage"; 56 57 /** 58 * SMS Class enumeration. 59 * See TS 23.038. 60 * 61 */ 62 public enum MessageClass{ 63 UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3; 64 } 65 66 /** @hide */ 67 @IntDef(prefix = { "ENCODING_" }, value = { 68 ENCODING_UNKNOWN, 69 ENCODING_7BIT, 70 ENCODING_8BIT, 71 ENCODING_16BIT 72 }) 73 @Retention(RetentionPolicy.SOURCE) 74 public @interface EncodingSize {} 75 76 /** User data text encoding code unit size */ 77 public static final int ENCODING_UNKNOWN = 0; 78 public static final int ENCODING_7BIT = 1; 79 public static final int ENCODING_8BIT = 2; 80 public static final int ENCODING_16BIT = 3; 81 /** 82 * This value is not defined in global standard. Only in Korea, this is used. 83 */ 84 public static final int ENCODING_KSC5601 = 4; 85 86 /** The maximum number of payload bytes per message */ 87 public static final int MAX_USER_DATA_BYTES = 140; 88 89 /** 90 * The maximum number of payload bytes per message if a user data header 91 * is present. This assumes the header only contains the 92 * CONCATENATED_8_BIT_REFERENCE element. 93 */ 94 public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134; 95 96 /** The maximum number of payload septets per message */ 97 public static final int MAX_USER_DATA_SEPTETS = 160; 98 99 /** 100 * The maximum number of payload septets per message if a user data header 101 * is present. This assumes the header only contains the 102 * CONCATENATED_8_BIT_REFERENCE element. 103 */ 104 public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153; 105 106 /** @hide */ 107 @StringDef(prefix = { "FORMAT_" }, value = { 108 FORMAT_3GPP, 109 FORMAT_3GPP2 110 }) 111 @Retention(RetentionPolicy.SOURCE) 112 public @interface Format {} 113 114 /** 115 * Indicates a 3GPP format SMS message. 116 * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent) 117 */ 118 public static final String FORMAT_3GPP = "3gpp"; 119 120 /** 121 * Indicates a 3GPP2 format SMS message. 122 * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent) 123 */ 124 public static final String FORMAT_3GPP2 = "3gpp2"; 125 126 /** Contains actual SmsMessage. Only public for debugging and for framework layer. 127 * 128 * @hide 129 */ 130 @UnsupportedAppUsage 131 public SmsMessageBase mWrappedSmsMessage; 132 133 /** Indicates the subId 134 * 135 * @hide 136 */ 137 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 138 private int mSubId = 0; 139 140 /** set Subscription information 141 * 142 * @hide 143 */ 144 @UnsupportedAppUsage setSubId(int subId)145 public void setSubId(int subId) { 146 mSubId = subId; 147 } 148 149 /** get Subscription information 150 * 151 * @hide 152 */ 153 @UnsupportedAppUsage getSubId()154 public int getSubId() { 155 return mSubId; 156 } 157 158 public static class SubmitPdu { 159 160 public byte[] encodedScAddress; // Null if not applicable. 161 public byte[] encodedMessage; 162 163 @Override toString()164 public String toString() { 165 return "SubmitPdu: encodedScAddress = " 166 + Arrays.toString(encodedScAddress) 167 + ", encodedMessage = " 168 + Arrays.toString(encodedMessage); 169 } 170 171 /** 172 * @hide 173 */ SubmitPdu(SubmitPduBase spb)174 protected SubmitPdu(SubmitPduBase spb) { 175 this.encodedMessage = spb.encodedMessage; 176 this.encodedScAddress = spb.encodedScAddress; 177 } 178 179 } 180 181 /** 182 * @hide 183 */ SmsMessage(SmsMessageBase smb)184 public SmsMessage(SmsMessageBase smb) { 185 mWrappedSmsMessage = smb; 186 } 187 188 /** 189 * Create an SmsMessage from a raw PDU. Guess format based on Voice 190 * technology first, if it fails use other format. 191 * All applications which handle 192 * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast 193 * intent <b>must</b> now pass the new {@code format} String extra from the intent 194 * into the new method {@code createFromPdu(byte[], String)} which takes an 195 * extra format parameter. This is required in order to correctly decode the PDU on 196 * devices that require support for both 3GPP and 3GPP2 formats at the same time, 197 * such as dual-mode GSM/CDMA and CDMA/LTE phones. 198 * @deprecated Use {@link #createFromPdu(byte[], String)} instead. 199 */ 200 @Deprecated createFromPdu(byte[] pdu)201 public static SmsMessage createFromPdu(byte[] pdu) { 202 SmsMessage message = null; 203 204 // cdma(3gpp2) vs gsm(3gpp) format info was not given, 205 // guess from active voice phone type 206 int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); 207 String format = (PHONE_TYPE_CDMA == activePhone) ? 208 SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP; 209 return createFromPdu(pdu, format); 210 } 211 212 /** 213 * Create an SmsMessage from a raw PDU with the specified message format. The 214 * message format is passed in the 215 * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} as the {@code format} 216 * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format 217 * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. 218 * 219 * @param pdu the message PDU from the 220 * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent 221 * @param format the format extra from the 222 * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent 223 */ createFromPdu(byte[] pdu, String format)224 public static SmsMessage createFromPdu(byte[] pdu, String format) { 225 return createFromPdu(pdu, format, true); 226 } 227 createFromPdu(byte[] pdu, String format, boolean fallbackToOtherFormat)228 private static SmsMessage createFromPdu(byte[] pdu, String format, 229 boolean fallbackToOtherFormat) { 230 if (pdu == null) { 231 Rlog.i(LOG_TAG, "createFromPdu(): pdu is null"); 232 return null; 233 } 234 SmsMessageBase wrappedMessage; 235 String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP : 236 SmsConstants.FORMAT_3GPP2; 237 if (SmsConstants.FORMAT_3GPP2.equals(format)) { 238 wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu); 239 } else if (SmsConstants.FORMAT_3GPP.equals(format)) { 240 wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu); 241 } else { 242 Rlog.e(LOG_TAG, "createFromPdu(): unsupported message format " + format); 243 return null; 244 } 245 246 if (wrappedMessage != null) { 247 return new SmsMessage(wrappedMessage); 248 } else { 249 if (fallbackToOtherFormat) { 250 return createFromPdu(pdu, otherFormat, false); 251 } else { 252 Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null"); 253 return null; 254 } 255 } 256 } 257 258 /** 259 * Creates an SmsMessage from an SMS EF record. 260 * 261 * @param index Index of SMS EF record. 262 * @param data Record data. 263 * @return An SmsMessage representing the record. 264 * 265 * @hide 266 */ createFromEfRecord(int index, byte[] data)267 public static SmsMessage createFromEfRecord(int index, byte[] data) { 268 return createFromEfRecord(index, data, SmsManager.getDefaultSmsSubscriptionId()); 269 } 270 271 /** 272 * Creates an SmsMessage from an SMS EF record. 273 * 274 * @param index Index of SMS EF record. 275 * @param data Record data. 276 * @param subId Subscription Id associated with the record. 277 * @return An SmsMessage representing the record. 278 * 279 * @hide 280 */ createFromEfRecord(int index, byte[] data, int subId)281 public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) { 282 SmsMessageBase wrappedMessage; 283 284 if (isCdmaVoice(subId)) { 285 wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord( 286 index, data); 287 } else { 288 wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord( 289 index, data); 290 } 291 292 return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null; 293 } 294 295 /** 296 * Create an SmsMessage from a native SMS-Submit PDU, specified by Bluetooth Message Access 297 * Profile Specification v1.4.2 5.8. 298 * This is used by Bluetooth MAP profile to decode message when sending non UTF-8 SMS messages. 299 * 300 * @param data Message data. 301 * @param isCdma Indicates weather the type of the SMS is CDMA. 302 * @return An SmsMessage representing the message. 303 * 304 * @hide 305 */ 306 @SystemApi 307 @Nullable createFromNativeSmsSubmitPdu(@onNull byte[] data, boolean isCdma)308 public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) { 309 SmsMessageBase wrappedMessage; 310 311 if (isCdma) { 312 wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord( 313 0, data); 314 } else { 315 // Bluetooth uses its own method to decode GSM PDU so this part is not called. 316 wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord( 317 0, data); 318 } 319 320 return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null; 321 } 322 323 /** 324 * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the 325 * length in bytes (not hex chars) less the SMSC header 326 * 327 * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices. 328 * We should probably deprecate it and remove the obsolete test case. 329 */ getTPLayerLengthForPDU(String pdu)330 public static int getTPLayerLengthForPDU(String pdu) { 331 if (isCdmaVoice()) { 332 return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu); 333 } else { 334 return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu); 335 } 336 } 337 338 /* 339 * TODO(cleanup): It would make some sense if the result of 340 * preprocessing a message to determine the proper encoding (i.e. 341 * the resulting data structure from calculateLength) could be 342 * passed as an argument to the actual final encoding function. 343 * This would better ensure that the logic behind size calculation 344 * actually matched the encoding. 345 */ 346 347 /** 348 * Calculates the number of SMS's required to encode the message body and the number of 349 * characters remaining until the next message. 350 * 351 * @param msgBody the message to encode 352 * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding 353 * are counted as single space chars. If false, and if the messageBody contains non-7-bit 354 * encodable characters, length is calculated using a 16-bit encoding. 355 * @return an int[6] with int[0] being the number of SMS's required, int[1] the number of code 356 * units used, and int[2] is the number of code units remaining until the next message. 357 * int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in 358 * SmsConstants). int[4] is the GSM national language table to use, or 0 for the default 359 * 7-bit alphabet. int[5] The GSM national language shift table to use, or 0 for the default 360 * 7-bit extension table. 361 */ calculateLength(CharSequence msgBody, boolean use7bitOnly)362 public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) { 363 return calculateLength(msgBody, use7bitOnly, SmsManager.getDefaultSmsSubscriptionId()); 364 } 365 366 /** 367 * Calculates the number of SMS's required to encode the message body and the number of 368 * characters remaining until the next message. 369 * 370 * @param msgBody the message to encode 371 * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding 372 * are counted as single space chars. If false, and if the messageBody contains non-7-bit 373 * encodable characters, length is calculated using a 16-bit encoding. 374 * @param subId Subscription to take SMS format. 375 * @return an int[6] with int[0] being the number of SMS's required, int[1] the number of code 376 * units used, and int[2] is the number of code units remaining until the next message. 377 * int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in 378 * SmsConstants). int[4] is the GSM national language table to use, or 0 for the default 379 * 7-bit alphabet. int[5] The GSM national language shift table to use, or 0 for the default 380 * 7-bit extension table. 381 * @hide 382 */ calculateLength(CharSequence msgBody, boolean use7bitOnly, int subId)383 public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly, int subId) { 384 // this function is for MO SMS 385 TextEncodingDetails ted = 386 useCdmaFormatForMoSms(subId) 387 ? com.android.internal.telephony.cdma.SmsMessage.calculateLength( 388 msgBody, use7bitOnly, true) 389 : com.android.internal.telephony.gsm.SmsMessage.calculateLength( 390 msgBody, use7bitOnly); 391 int[] ret = new int[6]; 392 ret[0] = ted.msgCount; 393 ret[1] = ted.codeUnitCount; 394 ret[2] = ted.codeUnitsRemaining; 395 ret[3] = ted.codeUnitSize; 396 ret[4] = ted.languageTable; 397 ret[5] = ted.languageShiftTable; 398 return ret; 399 } 400 401 /** 402 * Divide a message text into several fragments, none bigger than the maximum SMS message text 403 * size. 404 * 405 * @param text text, must not be null. 406 * @return an <code>ArrayList</code> of strings that, in order, comprise the original msg text. 407 * @hide 408 */ 409 @UnsupportedAppUsage fragmentText(String text)410 public static ArrayList<String> fragmentText(String text) { 411 return fragmentText(text, SmsManager.getDefaultSmsSubscriptionId()); 412 } 413 414 /** 415 * Divide a message text into several fragments, none bigger than the maximum SMS message text 416 * size. 417 * 418 * @param text text, must not be null. 419 * @param subId Subscription to take SMS format. 420 * @return an <code>ArrayList</code> of strings that, in order, comprise the original msg text. 421 * @hide 422 */ fragmentText(String text, int subId)423 public static ArrayList<String> fragmentText(String text, int subId) { 424 // This function is for MO SMS 425 final boolean isCdma = useCdmaFormatForMoSms(subId); 426 427 TextEncodingDetails ted = 428 isCdma 429 ? com.android.internal.telephony.cdma.SmsMessage.calculateLength( 430 text, false, true) 431 : com.android.internal.telephony.gsm.SmsMessage.calculateLength( 432 text, false); 433 434 // TODO(cleanup): The code here could be rolled into the logic 435 // below cleanly if these MAX_* constants were defined more 436 // flexibly... 437 438 int limit; 439 if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) { 440 int udhLength; 441 if (ted.languageTable != 0 && ted.languageShiftTable != 0) { 442 udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES; 443 } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) { 444 udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE; 445 } else { 446 udhLength = 0; 447 } 448 449 if (ted.msgCount > 1) { 450 udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE; 451 } 452 453 if (udhLength != 0) { 454 udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH; 455 } 456 457 limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength; 458 } else { 459 if (ted.msgCount > 1) { 460 limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER; 461 // If EMS is not supported, break down EMS into single segment SMS 462 // and add page info " x/y". 463 // In the case of UCS2 encoding, we need 8 bytes for this, 464 // but we only have 6 bytes from UDH, so truncate the limit for 465 // each segment by 2 bytes (1 char). 466 // Make sure total number of segments is less than 10. 467 if (!hasEmsSupport() && ted.msgCount < 10) { 468 limit -= 2; 469 } 470 } else { 471 limit = SmsConstants.MAX_USER_DATA_BYTES; 472 } 473 } 474 475 String newMsgBody = null; 476 Resources r = Resources.getSystem(); 477 if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) { 478 // 7-bit ASCII table based translation is required only for CDMA single-part SMS since 479 // ENCODING_7BIT_ASCII is used for CDMA single-part SMS and ENCODING_GSM_7BIT_ALPHABET 480 // is used for CDMA multi-part SMS. 481 newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma && ted.msgCount == 1); 482 } 483 if (TextUtils.isEmpty(newMsgBody)) { 484 newMsgBody = text; 485 } 486 487 int pos = 0; // Index in code units. 488 int textLen = newMsgBody.length(); 489 ArrayList<String> result = new ArrayList<String>(ted.msgCount); 490 while (pos < textLen) { 491 int nextPos = 0; // Counts code units. 492 if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) { 493 if (isCdma && ted.msgCount == 1) { 494 // For a singleton CDMA message, the encoding must be ASCII... 495 nextPos = pos + Math.min(limit, textLen - pos); 496 } else { 497 // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode). 498 nextPos = GsmAlphabet.findGsmSeptetLimitIndex(newMsgBody, pos, limit, 499 ted.languageTable, ted.languageShiftTable); 500 } 501 } else { // Assume unicode. 502 nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody); 503 } 504 if ((nextPos <= pos) || (nextPos > textLen)) { 505 Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " + 506 nextPos + " >= " + textLen + ")"); 507 break; 508 } 509 result.add(newMsgBody.substring(pos, nextPos)); 510 pos = nextPos; 511 } 512 return result; 513 } 514 515 /** 516 * Calculates the number of SMS's required to encode the message body and the number of 517 * characters remaining until the next message, given the current encoding. 518 * 519 * @param messageBody the message to encode 520 * @param use7bitOnly if true, characters that are not part of the radio specific (GSM / CDMA) 521 * alphabet encoding are converted to as a single space characters. If false, a messageBody 522 * containing non-GSM or non-CDMA alphabet characters are encoded using 16-bit encoding. 523 * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code 524 * units used, and int[2] is the number of code units remaining until the next message. 525 * int[3] is the encoding type that should be used for the message. 526 */ calculateLength(String messageBody, boolean use7bitOnly)527 public static int[] calculateLength(String messageBody, boolean use7bitOnly) { 528 return calculateLength((CharSequence)messageBody, use7bitOnly); 529 } 530 531 /** 532 * Calculates the number of SMS's required to encode the message body and the number of 533 * characters remaining until the next message, given the current encoding. 534 * 535 * @param messageBody the message to encode 536 * @param use7bitOnly if true, characters that are not part of the radio specific (GSM / CDMA) 537 * alphabet encoding are converted to as a single space characters. If false, a messageBody 538 * containing non-GSM or non-CDMA alphabet characters are encoded using 16-bit encoding. 539 * @param subId Subscription to take SMS format. 540 * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code 541 * units used, and int[2] is the number of code units remaining until the next message. 542 * int[3] is the encoding type that should be used for the message. 543 * @hide 544 */ calculateLength(String messageBody, boolean use7bitOnly, int subId)545 public static int[] calculateLength(String messageBody, boolean use7bitOnly, int subId) { 546 return calculateLength((CharSequence) messageBody, use7bitOnly, subId); 547 } 548 549 /* 550 * TODO(cleanup): It looks like there is now no useful reason why 551 * apps should generate pdus themselves using these routines, 552 * instead of handing the raw data to SMSDispatcher (and thereby 553 * have the phone process do the encoding). Moreover, CDMA now 554 * has shared state (in the form of the msgId system property) 555 * which can only be modified by the phone process, and hence 556 * makes the output of these routines incorrect. Since they now 557 * serve no purpose, they should probably just return null 558 * directly, and be deprecated. Going further in that direction, 559 * the above parsers of serialized pdu data should probably also 560 * be gotten rid of, hiding all but the necessarily visible 561 * structured data from client apps. A possible concern with 562 * doing this is that apps may be using these routines to generate 563 * pdus that are then sent elsewhere, some network server, for 564 * example, and that always returning null would thereby break 565 * otherwise useful apps. 566 */ 567 568 /** 569 * Gets an SMS-SUBMIT PDU for a destination address and a message. 570 * This method will not attempt to use any GSM national language 7 bit encodings. 571 * 572 * @param scAddress Service Centre address. Null means use default. 573 * @param destinationAddress the address of the destination for the message. 574 * @param message string representation of the message payload. 575 * @param statusReportRequested indicates whether a report is requested for this message. 576 * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the 577 * encoded message. Returns null on encode error. 578 */ getSubmitPdu(String scAddress, String destinationAddress, String message, boolean statusReportRequested)579 public static SubmitPdu getSubmitPdu(String scAddress, 580 String destinationAddress, String message, boolean statusReportRequested) { 581 return getSubmitPdu( 582 scAddress, 583 destinationAddress, 584 message, 585 statusReportRequested, 586 SmsManager.getDefaultSmsSubscriptionId()); 587 } 588 589 /** 590 * Gets an SMS-SUBMIT PDU for a destination address and a message. 591 * This method will not attempt to use any GSM national language 7 bit encodings. 592 * 593 * @param scAddress Service Centre address. Null means use default. 594 * @param destinationAddress the address of the destination for the message. 595 * @param message string representation of the message payload. 596 * @param statusReportRequested indicates whether a report is requested for this message. 597 * @param subId subscription of the message. 598 * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the 599 * encoded message. Returns null on encode error. 600 * @hide 601 */ getSubmitPdu(String scAddress, String destinationAddress, String message, boolean statusReportRequested, int subId)602 public static SubmitPdu getSubmitPdu(String scAddress, 603 String destinationAddress, String message, boolean statusReportRequested, int subId) { 604 SubmitPduBase spb; 605 if (useCdmaFormatForMoSms(subId)) { 606 spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, 607 destinationAddress, message, statusReportRequested, null); 608 } else { 609 spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, 610 destinationAddress, message, statusReportRequested); 611 } 612 613 return spb != null ? new SubmitPdu(spb) : null; 614 } 615 616 /** 617 * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. 618 * This method will not attempt to use any GSM national language 7 bit encodings. 619 * 620 * @param scAddress Service Centre address. Null means use default. 621 * @param destinationAddress the address of the destination for the message. 622 * @param destinationPort the port to deliver the message to at the destination. 623 * @param data the data for the message. 624 * @param statusReportRequested indicates whether a report is requested for this message. 625 * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the 626 * encoded message. Returns null on encode error. 627 */ getSubmitPdu(String scAddress, String destinationAddress, short destinationPort, byte[] data, boolean statusReportRequested)628 public static SubmitPdu getSubmitPdu(String scAddress, 629 String destinationAddress, short destinationPort, byte[] data, 630 boolean statusReportRequested) { 631 SubmitPduBase spb; 632 633 if (useCdmaFormatForMoSms()) { 634 spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, 635 destinationAddress, destinationPort, data, statusReportRequested); 636 } else { 637 spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, 638 destinationAddress, destinationPort, data, statusReportRequested); 639 } 640 641 return spb != null ? new SubmitPdu(spb) : null; 642 } 643 644 // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new 645 // DeliverPdu accordingly. 646 647 /** 648 * Gets an SMS PDU to store in the ICC. 649 * 650 * @param subId subscription of the message. 651 * @param status message status. One of these status: 652 * <code>SmsManager.STATUS_ON_ICC_READ</code> 653 * <code>SmsManager.STATUS_ON_ICC_UNREAD</code> 654 * <code>SmsManager.STATUS_ON_ICC_SENT</code> 655 * <code>SmsManager.STATUS_ON_ICC_UNSENT</code> 656 * @param scAddress Service Centre address. Null means use default. 657 * @param address destination or originating address. 658 * @param message string representation of the message payload. 659 * @param date the time stamp the message was received. 660 * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the 661 * encoded message. Returns null on encode error. 662 * @hide 663 */ 664 @SystemApi 665 @Nullable getSmsPdu(int subId, @SmsManager.StatusOnIcc int status, @Nullable String scAddress, @NonNull String address, @NonNull String message, long date)666 public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status, 667 @Nullable String scAddress, @NonNull String address, @NonNull String message, 668 long date) { 669 SubmitPduBase spb; 670 if (isCdmaVoice(subId)) { // 3GPP2 format 671 if (status == SmsManager.STATUS_ON_ICC_READ 672 || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU 673 spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address, 674 message, date); 675 } else { // Submit PDU 676 spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, 677 address, message, false /* statusReportRequested */, null /* smsHeader */); 678 } 679 } else { // 3GPP format 680 if (status == SmsManager.STATUS_ON_ICC_READ 681 || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU 682 spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress, 683 address, message, date); 684 } else { // Submit PDU 685 spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, 686 address, message, false /* statusReportRequested */, null /* header */); 687 } 688 } 689 690 return spb != null ? new SubmitPdu(spb) : null; 691 } 692 693 /** 694 * Get an SMS-SUBMIT PDU's encoded message. 695 * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages. 696 * 697 * @param isTypeGsm true when message's type is GSM, false when type is CDMA 698 * @param destinationAddress the address of the destination for the message 699 * @param message message content 700 * @param encoding User data text encoding code unit size 701 * @param languageTable GSM national language table to use, specified by 3GPP 702 * 23.040 9.2.3.24.16 703 * @param languageShiftTable GSM national language shift table to use, specified by 3GPP 704 * 23.040 9.2.3.24.15 705 * @param refNumber reference number of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.1 706 * @param seqNumber sequence number of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.1 707 * @param msgCount count of messages of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.2 708 * @return a byte[] containing the encoded message 709 * 710 * @hide 711 */ 712 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) 713 @SystemApi 714 @NonNull getSubmitPduEncodedMessage(boolean isTypeGsm, @NonNull String destinationAddress, @NonNull String message, @EncodingSize int encoding, @IntRange(from = 0) int languageTable, @IntRange(from = 0) int languageShiftTable, @IntRange(from = 0, to = 255) int refNumber, @IntRange(from = 1, to = 255) int seqNumber, @IntRange(from = 1, to = 255) int msgCount)715 public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm, 716 @NonNull String destinationAddress, 717 @NonNull String message, 718 @EncodingSize int encoding, 719 @IntRange(from = 0) int languageTable, 720 @IntRange(from = 0) int languageShiftTable, 721 @IntRange(from = 0, to = 255) int refNumber, 722 @IntRange(from = 1, to = 255) int seqNumber, 723 @IntRange(from = 1, to = 255) int msgCount) { 724 byte[] data; 725 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 726 concatRef.refNumber = refNumber; 727 concatRef.seqNumber = seqNumber; // 1-based sequence 728 concatRef.msgCount = msgCount; 729 // We currently set this to true since our messaging app will never 730 // send more than 255 parts (it converts the message to MMS well before that). 731 // However, we should support 3rd party messaging apps that might need 16-bit 732 // references 733 // Note: It's not sufficient to just flip this bit to true; it will have 734 // ripple effects (several calculations assume 8-bit ref). 735 concatRef.isEightBits = true; 736 SmsHeader smsHeader = new SmsHeader(); 737 smsHeader.concatRef = concatRef; 738 739 /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding 740 * will be determined(again) by getSubmitPdu(). 741 * All packets need to be encoded using the same encoding, as the bMessage 742 * only have one filed to describe the encoding for all messages in a concatenated 743 * SMS... */ 744 if (encoding == ENCODING_7BIT) { 745 smsHeader.languageTable = languageTable; 746 smsHeader.languageShiftTable = languageShiftTable; 747 } 748 749 if (isTypeGsm) { 750 data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null, 751 destinationAddress, message, false, 752 SmsHeader.toByteArray(smsHeader), encoding, languageTable, 753 languageShiftTable).encodedMessage; 754 } else { // SMS_TYPE_CDMA 755 UserData uData = new UserData(); 756 uData.payloadStr = message; 757 uData.userDataHeader = smsHeader; 758 if (encoding == ENCODING_7BIT) { 759 uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; 760 } else { // assume UTF-16 761 uData.msgEncoding = UserData.ENCODING_UNICODE_16; 762 } 763 uData.msgEncodingSet = true; 764 data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 765 destinationAddress, uData, false).encodedMessage; 766 } 767 if (data == null) { 768 return new byte[0]; 769 } 770 return data; 771 } 772 773 /** 774 * Returns the address of the SMS service center that relayed this message 775 * or null if there is none. 776 */ getServiceCenterAddress()777 public String getServiceCenterAddress() { 778 return mWrappedSmsMessage.getServiceCenterAddress(); 779 } 780 781 /** 782 * Returns the originating address (sender) of this SMS message in String 783 * form or null if unavailable. 784 * 785 * <p>If the address is a GSM-formatted address, it will be in a format specified by 3GPP 786 * 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2 787 * C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the 788 * should be careful to avoid assumptions about the returned content. 789 * 790 * @return a String representation of the address; null if unavailable. 791 */ 792 @Nullable getOriginatingAddress()793 public String getOriginatingAddress() { 794 return mWrappedSmsMessage.getOriginatingAddress(); 795 } 796 797 /** 798 * Returns the originating address, or email from address if this message 799 * was from an email gateway. Returns null if originating address 800 * unavailable. 801 */ getDisplayOriginatingAddress()802 public String getDisplayOriginatingAddress() { 803 return mWrappedSmsMessage.getDisplayOriginatingAddress(); 804 } 805 806 /** 807 * Returns the message body as a String, if it exists and is text based. 808 * @return message body if there is one, otherwise null 809 */ getMessageBody()810 public String getMessageBody() { 811 return mWrappedSmsMessage.getMessageBody(); 812 } 813 814 /** 815 * Returns the class of this message. 816 */ getMessageClass()817 public MessageClass getMessageClass() { 818 switch(mWrappedSmsMessage.getMessageClass()) { 819 case CLASS_0: return MessageClass.CLASS_0; 820 case CLASS_1: return MessageClass.CLASS_1; 821 case CLASS_2: return MessageClass.CLASS_2; 822 case CLASS_3: return MessageClass.CLASS_3; 823 default: return MessageClass.UNKNOWN; 824 825 } 826 } 827 828 /** 829 * Returns the message body, or email message body if this message was from 830 * an email gateway. Returns null if message body unavailable. 831 */ getDisplayMessageBody()832 public String getDisplayMessageBody() { 833 return mWrappedSmsMessage.getDisplayMessageBody(); 834 } 835 836 /** 837 * Unofficial convention of a subject line enclosed in parens empty string 838 * if not present 839 */ getPseudoSubject()840 public String getPseudoSubject() { 841 return mWrappedSmsMessage.getPseudoSubject(); 842 } 843 844 /** 845 * Returns the service centre timestamp in currentTimeMillis() format 846 */ getTimestampMillis()847 public long getTimestampMillis() { 848 return mWrappedSmsMessage.getTimestampMillis(); 849 } 850 851 /** 852 * Returns true if message is an email. 853 * 854 * @return true if this message came through an email gateway and email 855 * sender / subject / parsed body are available 856 */ isEmail()857 public boolean isEmail() { 858 return mWrappedSmsMessage.isEmail(); 859 } 860 861 /** 862 * @return if isEmail() is true, body of the email sent through the gateway. 863 * null otherwise 864 */ getEmailBody()865 public String getEmailBody() { 866 return mWrappedSmsMessage.getEmailBody(); 867 } 868 869 /** 870 * @return if isEmail() is true, email from address of email sent through 871 * the gateway. null otherwise 872 */ getEmailFrom()873 public String getEmailFrom() { 874 return mWrappedSmsMessage.getEmailFrom(); 875 } 876 877 /** 878 * Get protocol identifier. 879 */ getProtocolIdentifier()880 public int getProtocolIdentifier() { 881 return mWrappedSmsMessage.getProtocolIdentifier(); 882 } 883 884 /** 885 * See TS 23.040 9.2.3.9 returns true if this is a "replace short message" 886 * SMS 887 */ isReplace()888 public boolean isReplace() { 889 return mWrappedSmsMessage.isReplace(); 890 } 891 892 /** 893 * Returns true for CPHS MWI toggle message. 894 * 895 * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section 896 * B.4.2 897 */ isCphsMwiMessage()898 public boolean isCphsMwiMessage() { 899 return mWrappedSmsMessage.isCphsMwiMessage(); 900 } 901 902 /** 903 * returns true if this message is a CPHS voicemail / message waiting 904 * indicator (MWI) clear message 905 */ isMWIClearMessage()906 public boolean isMWIClearMessage() { 907 return mWrappedSmsMessage.isMWIClearMessage(); 908 } 909 910 /** 911 * returns true if this message is a CPHS voicemail / message waiting 912 * indicator (MWI) set message 913 */ isMWISetMessage()914 public boolean isMWISetMessage() { 915 return mWrappedSmsMessage.isMWISetMessage(); 916 } 917 918 /** 919 * returns true if this message is a "Message Waiting Indication Group: 920 * Discard Message" notification and should not be stored. 921 */ isMwiDontStore()922 public boolean isMwiDontStore() { 923 return mWrappedSmsMessage.isMwiDontStore(); 924 } 925 926 /** 927 * returns the user data section minus the user data header if one was 928 * present. 929 */ getUserData()930 public byte[] getUserData() { 931 return mWrappedSmsMessage.getUserData(); 932 } 933 934 /** 935 * Returns the raw PDU for the message. 936 * 937 * @return the raw PDU for the message. 938 */ getPdu()939 public byte[] getPdu() { 940 return mWrappedSmsMessage.getPdu(); 941 } 942 943 /** 944 * Returns the status of the message on the SIM (read, unread, sent, unsent). 945 * 946 * @return the status of the message on the SIM. These are: 947 * SmsManager.STATUS_ON_SIM_FREE 948 * SmsManager.STATUS_ON_SIM_READ 949 * SmsManager.STATUS_ON_SIM_UNREAD 950 * SmsManager.STATUS_ON_SIM_SEND 951 * SmsManager.STATUS_ON_SIM_UNSENT 952 * @deprecated Use getStatusOnIcc instead. 953 */ getStatusOnSim()954 @Deprecated public int getStatusOnSim() { 955 return mWrappedSmsMessage.getStatusOnIcc(); 956 } 957 958 /** 959 * Returns the status of the message on the ICC (read, unread, sent, unsent). 960 * 961 * @return the status of the message on the ICC. These are: 962 * SmsManager.STATUS_ON_ICC_FREE 963 * SmsManager.STATUS_ON_ICC_READ 964 * SmsManager.STATUS_ON_ICC_UNREAD 965 * SmsManager.STATUS_ON_ICC_SEND 966 * SmsManager.STATUS_ON_ICC_UNSENT 967 */ getStatusOnIcc()968 public int getStatusOnIcc() { 969 return mWrappedSmsMessage.getStatusOnIcc(); 970 } 971 972 /** 973 * Returns the record index of the message on the SIM (1-based index). 974 * @return the record index of the message on the SIM, or -1 if this 975 * SmsMessage was not created from a SIM SMS EF record. 976 * @deprecated Use getIndexOnIcc instead. 977 */ getIndexOnSim()978 @Deprecated public int getIndexOnSim() { 979 return mWrappedSmsMessage.getIndexOnIcc(); 980 } 981 982 /** 983 * Returns the record index of the message on the ICC (1-based index). 984 * @return the record index of the message on the ICC, or -1 if this 985 * SmsMessage was not created from a ICC SMS EF record. 986 */ getIndexOnIcc()987 public int getIndexOnIcc() { 988 return mWrappedSmsMessage.getIndexOnIcc(); 989 } 990 991 /** 992 * GSM: For an SMS-STATUS-REPORT message, this returns the status field from the status report. 993 * This field indicates the status of a previously submitted SMS, if requested. 994 * See TS 23.040, 9.2.3.15 TP-Status for a description of values. 995 * CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16. 996 * The value is composed of an error class (bits 25-24) and a status code (bits 23-16). Possible 997 * codes are described in C.S0015-B, v2.0, 4.5.21. 998 * 999 * @return 0 for GSM or 2 shifted left by 16 for CDMA indicates the previously sent message was 1000 * received. See TS 23.040, 9.2.3.15 and C.S0015-B, v2.0, 4.5.21 for a description of 1001 * other possible values. 1002 */ getStatus()1003 public int getStatus() { 1004 return mWrappedSmsMessage.getStatus(); 1005 } 1006 1007 /** 1008 * Return true iff the message is a SMS-STATUS-REPORT message. 1009 */ isStatusReportMessage()1010 public boolean isStatusReportMessage() { 1011 return mWrappedSmsMessage.isStatusReportMessage(); 1012 } 1013 1014 /** 1015 * Returns true iff the <code>TP-Reply-Path</code> bit is set in 1016 * this message. 1017 */ isReplyPathPresent()1018 public boolean isReplyPathPresent() { 1019 return mWrappedSmsMessage.isReplyPathPresent(); 1020 } 1021 1022 /** 1023 * Return the encoding type of a received SMS message, which is specified using ENCODING_* 1024 * GSM: defined in android.telephony.SmsConstants 1025 * CDMA: defined in android.telephony.cdma.UserData 1026 * 1027 * @hide 1028 */ getReceivedEncodingType()1029 public int getReceivedEncodingType() { 1030 return mWrappedSmsMessage.getReceivedEncodingType(); 1031 } 1032 1033 /** 1034 * Check if format of the message is 3GPP. 1035 * 1036 * @hide 1037 */ is3gpp()1038 public boolean is3gpp() { 1039 return (mWrappedSmsMessage instanceof com.android.internal.telephony.gsm.SmsMessage); 1040 } 1041 1042 /** 1043 * Determines whether or not to use CDMA format for MO SMS. 1044 * If SMS over IMS is supported, then format is based on IMS SMS format, 1045 * otherwise format is based on current phone type. 1046 * 1047 * @return true if Cdma format should be used for MO SMS, false otherwise. 1048 */ 1049 @UnsupportedAppUsage useCdmaFormatForMoSms()1050 private static boolean useCdmaFormatForMoSms() { 1051 // IMS is registered with SMS support, check the SMS format supported 1052 return useCdmaFormatForMoSms(SmsManager.getDefaultSmsSubscriptionId()); 1053 } 1054 1055 /** 1056 * Determines whether or not to use CDMA format for MO SMS. 1057 * If SMS over IMS is supported, then format is based on IMS SMS format, 1058 * otherwise format is based on current phone type. 1059 * 1060 * @param subId Subscription for which phone type is returned. 1061 * 1062 * @return true if Cdma format should be used for MO SMS, false otherwise. 1063 */ 1064 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) useCdmaFormatForMoSms(int subId)1065 private static boolean useCdmaFormatForMoSms(int subId) { 1066 SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId); 1067 if (!smsManager.isImsSmsSupported()) { 1068 // use Voice technology to determine SMS format. 1069 return isCdmaVoice(subId); 1070 } 1071 // IMS is registered with SMS support, check the SMS format supported 1072 return (SmsConstants.FORMAT_3GPP2.equals(smsManager.getImsSmsFormat())); 1073 } 1074 1075 /** 1076 * Determines whether or not to current phone type is cdma. 1077 * 1078 * @return true if current phone type is cdma, false otherwise. 1079 */ isCdmaVoice()1080 private static boolean isCdmaVoice() { 1081 return isCdmaVoice(SmsManager.getDefaultSmsSubscriptionId()); 1082 } 1083 1084 /** 1085 * Determines whether or not to current phone type is cdma 1086 * 1087 * @return true if current phone type is cdma, false otherwise. 1088 */ isCdmaVoice(int subId)1089 private static boolean isCdmaVoice(int subId) { 1090 int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId); 1091 return (PHONE_TYPE_CDMA == activePhone); 1092 } 1093 1094 /** 1095 * Decide if the carrier supports long SMS. 1096 * {@hide} 1097 */ hasEmsSupport()1098 public static boolean hasEmsSupport() { 1099 if (!isNoEmsSupportConfigListExisted()) { 1100 return true; 1101 } 1102 1103 String simOperator; 1104 String gid; 1105 final long identity = Binder.clearCallingIdentity(); 1106 try { 1107 simOperator = TelephonyManager.getDefault().getSimOperatorNumeric(); 1108 gid = TelephonyManager.getDefault().getGroupIdLevel1(); 1109 } finally { 1110 Binder.restoreCallingIdentity(identity); 1111 } 1112 1113 if (!TextUtils.isEmpty(simOperator)) { 1114 for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) { 1115 if (currentConfig == null) { 1116 Rlog.w("SmsMessage", "hasEmsSupport currentConfig is null"); 1117 continue; 1118 } 1119 1120 if (simOperator.startsWith(currentConfig.mOperatorNumber) && 1121 (TextUtils.isEmpty(currentConfig.mGid1) || 1122 (!TextUtils.isEmpty(currentConfig.mGid1) && 1123 currentConfig.mGid1.equalsIgnoreCase(gid)))) { 1124 return false; 1125 } 1126 } 1127 } 1128 return true; 1129 } 1130 1131 /** 1132 * Check where to add " x/y" in each SMS segment, begin or end. 1133 * {@hide} 1134 */ shouldAppendPageNumberAsPrefix()1135 public static boolean shouldAppendPageNumberAsPrefix() { 1136 if (!isNoEmsSupportConfigListExisted()) { 1137 return false; 1138 } 1139 1140 String simOperator; 1141 String gid; 1142 final long identity = Binder.clearCallingIdentity(); 1143 try { 1144 simOperator = TelephonyManager.getDefault().getSimOperatorNumeric(); 1145 gid = TelephonyManager.getDefault().getGroupIdLevel1(); 1146 } finally { 1147 Binder.restoreCallingIdentity(identity); 1148 } 1149 1150 for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) { 1151 if (simOperator.startsWith(currentConfig.mOperatorNumber) && 1152 (TextUtils.isEmpty(currentConfig.mGid1) || 1153 (!TextUtils.isEmpty(currentConfig.mGid1) 1154 && currentConfig.mGid1.equalsIgnoreCase(gid)))) { 1155 return currentConfig.mIsPrefix; 1156 } 1157 } 1158 return false; 1159 } 1160 1161 private static class NoEmsSupportConfig { 1162 String mOperatorNumber; 1163 String mGid1; 1164 boolean mIsPrefix; 1165 NoEmsSupportConfig(String[] config)1166 public NoEmsSupportConfig(String[] config) { 1167 mOperatorNumber = config[0]; 1168 mIsPrefix = "prefix".equals(config[1]); 1169 mGid1 = config.length > 2 ? config[2] : null; 1170 } 1171 1172 @Override toString()1173 public String toString() { 1174 return "NoEmsSupportConfig { mOperatorNumber = " + mOperatorNumber 1175 + ", mIsPrefix = " + mIsPrefix + ", mGid1 = " + mGid1 + " }"; 1176 } 1177 } 1178 1179 private static NoEmsSupportConfig[] mNoEmsSupportConfigList = null; 1180 private static boolean mIsNoEmsSupportConfigListLoaded = false; 1181 isNoEmsSupportConfigListExisted()1182 private static boolean isNoEmsSupportConfigListExisted() { 1183 synchronized (SmsMessage.class) { 1184 if (!mIsNoEmsSupportConfigListLoaded) { 1185 Resources r = Resources.getSystem(); 1186 if (r != null) { 1187 String[] listArray = r.getStringArray( 1188 com.android.internal.R.array.no_ems_support_sim_operators); 1189 if ((listArray != null) && (listArray.length > 0)) { 1190 mNoEmsSupportConfigList = new NoEmsSupportConfig[listArray.length]; 1191 for (int i = 0; i < listArray.length; i++) { 1192 mNoEmsSupportConfigList[i] = new NoEmsSupportConfig( 1193 listArray[i].split(";")); 1194 } 1195 } 1196 mIsNoEmsSupportConfigListLoaded = true; 1197 } 1198 } 1199 } 1200 1201 if (mNoEmsSupportConfigList != null && mNoEmsSupportConfigList.length != 0) { 1202 return true; 1203 } 1204 1205 return false; 1206 } 1207 1208 /** 1209 * Returns the recipient address(receiver) of this SMS message in String form or null if 1210 * unavailable. 1211 * {@hide} 1212 */ 1213 @Nullable getRecipientAddress()1214 public String getRecipientAddress() { 1215 return mWrappedSmsMessage.getRecipientAddress(); 1216 } 1217 } 1218