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