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