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