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 com.android.internal.telephony.cdma.sms;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.content.res.Resources;
21 import android.os.Build;
22 import android.telephony.SmsCbCmasInfo;
23 import android.telephony.cdma.CdmaSmsCbProgramData;
24 import android.telephony.cdma.CdmaSmsCbProgramResults;
25 import android.text.TextUtils;
26 
27 import com.android.internal.telephony.GsmAlphabet;
28 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
29 import com.android.internal.telephony.Sms7BitEncodingTranslator;
30 import com.android.internal.telephony.SmsConstants;
31 import com.android.internal.telephony.SmsHeader;
32 import com.android.internal.telephony.SmsMessageBase;
33 import com.android.internal.telephony.uicc.IccUtils;
34 import com.android.internal.util.BitwiseInputStream;
35 import com.android.internal.util.BitwiseOutputStream;
36 import com.android.telephony.Rlog;
37 
38 import java.io.ByteArrayOutputStream;
39 import java.time.DateTimeException;
40 import java.time.Instant;
41 import java.time.LocalDateTime;
42 import java.time.ZoneId;
43 import java.util.ArrayList;
44 
45 /**
46  * An object to encode and decode CDMA SMS bearer data.
47  */
48 public final class BearerData {
49     private final static String LOG_TAG = "BearerData";
50 
51     /**
52      * Bearer Data Subparameter Identifiers
53      * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
54      * NOTE: Commented subparameter types are not implemented.
55      */
56     private final static byte SUBPARAM_MESSAGE_IDENTIFIER               = 0x00;
57     private final static byte SUBPARAM_USER_DATA                        = 0x01;
58     private final static byte SUBPARAM_USER_RESPONSE_CODE               = 0x02;
59     private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP        = 0x03;
60     private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
61     private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
62     private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
63     private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
64     private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
65     private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
66     private final static byte SUBPARAM_REPLY_OPTION                     = 0x0A;
67     private final static byte SUBPARAM_NUMBER_OF_MESSAGES               = 0x0B;
68     private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY        = 0x0C;
69     private final static byte SUBPARAM_LANGUAGE_INDICATOR               = 0x0D;
70     private final static byte SUBPARAM_CALLBACK_NUMBER                  = 0x0E;
71     private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
72     //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA      = 0x10;
73     private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
74     private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
75     private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
76     private final static byte SUBPARAM_MESSAGE_STATUS                   = 0x14;
77     //private final static byte SUBPARAM_TP_FAILURE_CAUSE                 = 0x15;
78     //private final static byte SUBPARAM_ENHANCED_VMN                     = 0x16;
79     //private final static byte SUBPARAM_ENHANCED_VMN_ACK                 = 0x17;
80 
81     // All other values after this are reserved.
82     private final static byte SUBPARAM_ID_LAST_DEFINED                    = 0x17;
83 
84     /**
85      * Supported message types for CDMA SMS messages
86      * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
87      */
88     public static final int MESSAGE_TYPE_DELIVER        = 0x01;
89     public static final int MESSAGE_TYPE_SUBMIT         = 0x02;
90     public static final int MESSAGE_TYPE_CANCELLATION   = 0x03;
91     public static final int MESSAGE_TYPE_DELIVERY_ACK   = 0x04;
92     public static final int MESSAGE_TYPE_USER_ACK       = 0x05;
93     public static final int MESSAGE_TYPE_READ_ACK       = 0x06;
94     public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
95     public static final int MESSAGE_TYPE_SUBMIT_REPORT  = 0x08;
96 
97     public int messageType;
98 
99     /**
100      * 16-bit value indicating the message ID, which increments modulo 65536.
101      * (Special rules apply for WAP-messages.)
102      * (See 3GPP2 C.S0015-B, v2, 4.5.1)
103      */
104     @UnsupportedAppUsage
105     public int messageId;
106 
107     /**
108      * Supported priority modes for CDMA SMS messages
109      * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
110      */
111     public static final int PRIORITY_NORMAL        = 0x0;
112     public static final int PRIORITY_INTERACTIVE   = 0x1;
113     public static final int PRIORITY_URGENT        = 0x2;
114     public static final int PRIORITY_EMERGENCY     = 0x3;
115 
116     @UnsupportedAppUsage
117     public boolean priorityIndicatorSet = false;
118     @UnsupportedAppUsage
119     public int priority = PRIORITY_NORMAL;
120 
121     /**
122      * Supported privacy modes for CDMA SMS messages
123      * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1)
124      */
125     public static final int PRIVACY_NOT_RESTRICTED = 0x0;
126     public static final int PRIVACY_RESTRICTED     = 0x1;
127     public static final int PRIVACY_CONFIDENTIAL   = 0x2;
128     public static final int PRIVACY_SECRET         = 0x3;
129 
130     public boolean privacyIndicatorSet = false;
131     public int privacy = PRIVACY_NOT_RESTRICTED;
132 
133     /**
134      * Supported alert priority modes for CDMA SMS messages
135      * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1)
136      */
137     public static final int ALERT_DEFAULT          = 0x0;
138     public static final int ALERT_LOW_PRIO         = 0x1;
139     public static final int ALERT_MEDIUM_PRIO      = 0x2;
140     public static final int ALERT_HIGH_PRIO        = 0x3;
141 
142     public boolean alertIndicatorSet = false;
143     public int alert = ALERT_DEFAULT;
144 
145     /**
146      * Supported display modes for CDMA SMS messages.  Display mode is
147      * a 2-bit value used to indicate to the mobile station when to
148      * display the received message.  (See 3GPP2 C.S0015-B, v2,
149      * 4.5.16)
150      */
151     public static final int DISPLAY_MODE_IMMEDIATE      = 0x0;
152     public static final int DISPLAY_MODE_DEFAULT        = 0x1;
153     public static final int DISPLAY_MODE_USER           = 0x2;
154 
155     public boolean displayModeSet = false;
156     @UnsupportedAppUsage
157     public int displayMode = DISPLAY_MODE_DEFAULT;
158 
159     /**
160      * Language Indicator values.  NOTE: the spec (3GPP2 C.S0015-B,
161      * v2, 4.5.14) is ambiguous as to the meaning of this field, as it
162      * refers to C.R1001-D but that reference has been crossed out.
163      * It would seem reasonable to assume the values from C.R1001-F
164      * (table 9.2-1) are to be used instead.
165      */
166     public static final int LANGUAGE_UNKNOWN  = 0x00;
167     public static final int LANGUAGE_ENGLISH  = 0x01;
168     public static final int LANGUAGE_FRENCH   = 0x02;
169     public static final int LANGUAGE_SPANISH  = 0x03;
170     public static final int LANGUAGE_JAPANESE = 0x04;
171     public static final int LANGUAGE_KOREAN   = 0x05;
172     public static final int LANGUAGE_CHINESE  = 0x06;
173     public static final int LANGUAGE_HEBREW   = 0x07;
174 
175     public boolean languageIndicatorSet = false;
176     public int language = LANGUAGE_UNKNOWN;
177 
178     /**
179      * SMS Message Status Codes.  The first component of the Message
180      * status indicates if an error has occurred and whether the error
181      * is considered permanent or temporary.  The second component of
182      * the Message status indicates the cause of the error (if any).
183      * (See 3GPP2 C.S0015-B, v2.0, 4.5.21)
184      */
185     /* no-error codes */
186     public static final int ERROR_NONE                   = 0x00;
187     public static final int STATUS_ACCEPTED              = 0x00;
188     public static final int STATUS_DEPOSITED_TO_INTERNET = 0x01;
189     public static final int STATUS_DELIVERED             = 0x02;
190     public static final int STATUS_CANCELLED             = 0x03;
191     /* temporary-error and permanent-error codes */
192     public static final int ERROR_TEMPORARY              = 0x02;
193     public static final int STATUS_NETWORK_CONGESTION    = 0x04;
194     public static final int STATUS_NETWORK_ERROR         = 0x05;
195     public static final int STATUS_UNKNOWN_ERROR         = 0x1F;
196     /* permanent-error codes */
197     public static final int ERROR_PERMANENT              = 0x03;
198     public static final int STATUS_CANCEL_FAILED         = 0x06;
199     public static final int STATUS_BLOCKED_DESTINATION   = 0x07;
200     public static final int STATUS_TEXT_TOO_LONG         = 0x08;
201     public static final int STATUS_DUPLICATE_MESSAGE     = 0x09;
202     public static final int STATUS_INVALID_DESTINATION   = 0x0A;
203     public static final int STATUS_MESSAGE_EXPIRED       = 0x0D;
204     /* undefined-status codes */
205     public static final int ERROR_UNDEFINED              = 0xFF;
206     public static final int STATUS_UNDEFINED             = 0xFF;
207 
208     public boolean messageStatusSet = false;
209     public int errorClass = ERROR_UNDEFINED;
210     public int messageStatus = STATUS_UNDEFINED;
211 
212     /**
213      * 1-bit value that indicates whether a User Data Header (UDH) is present.
214      * (See 3GPP2 C.S0015-B, v2, 4.5.1)
215      *
216      * NOTE: during encoding, this value will be set based on the
217      * presence of a UDH in the structured data, any existing setting
218      * will be overwritten.
219      */
220     @UnsupportedAppUsage
221     public boolean hasUserDataHeader;
222 
223     /**
224      * provides the information for the user data
225      * (e.g. padding bits, user data, user data header, etc)
226      * (See 3GPP2 C.S.0015-B, v2, 4.5.2)
227      */
228     @UnsupportedAppUsage
229     public UserData userData;
230 
231     /**
232      * The User Response Code subparameter is used in the SMS User
233      * Acknowledgment Message to respond to previously received short
234      * messages. This message center-specific element carries the
235      * identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2,
236      * 4.5.3)
237      */
238     public boolean userResponseCodeSet = false;
239     public int userResponseCode;
240 
241     @UnsupportedAppUsage
BearerData()242     public BearerData() {
243     }
244 
245     /**
246      * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4
247      */
248     public static class TimeStamp {
249 
250         public int second;
251         public int minute;
252         public int hour;
253         public int monthDay;
254 
255         /** Month in the range 1(Jan) - 12(Dec). */
256         public int monthOrdinal;
257 
258         /** Full year in the range 1996 - 2095. */
259         public int year;
260 
261         private ZoneId mZoneId;
262 
263         @UnsupportedAppUsage
TimeStamp()264         public TimeStamp() {
265             mZoneId = ZoneId.systemDefault();   // 3GPP2 timestamps use the local timezone
266         }
267 
fromByteArray(byte[] data)268         public static TimeStamp fromByteArray(byte[] data) {
269             TimeStamp ts = new TimeStamp();
270             // C.S0015-B v2.0, 4.5.4: range is 1996-2095
271             int year = IccUtils.cdmaBcdByteToInt(data[0]);
272             if (year > 99 || year < 0) return null;
273             ts.year = year >= 96 ? year + 1900 : year + 2000;
274             int month = IccUtils.cdmaBcdByteToInt(data[1]);
275             if (month < 1 || month > 12) return null;
276             ts.monthOrdinal = month;
277             int day = IccUtils.cdmaBcdByteToInt(data[2]);
278             if (day < 1 || day > 31) return null;
279             ts.monthDay = day;
280             int hour = IccUtils.cdmaBcdByteToInt(data[3]);
281             if (hour < 0 || hour > 23) return null;
282             ts.hour = hour;
283             int minute = IccUtils.cdmaBcdByteToInt(data[4]);
284             if (minute < 0 || minute > 59) return null;
285             ts.minute = minute;
286             int second = IccUtils.cdmaBcdByteToInt(data[5]);
287             if (second < 0 || second > 59) return null;
288             ts.second = second;
289             return ts;
290         }
291 
fromMillis(long timeInMillis)292         public static TimeStamp fromMillis(long timeInMillis) {
293             TimeStamp ts = new TimeStamp();
294             LocalDateTime localDateTime =
295                     Instant.ofEpochMilli(timeInMillis).atZone(ts.mZoneId).toLocalDateTime();
296             int year = localDateTime.getYear();
297             if (year < 1996 || year > 2095) return null;
298             ts.year = year;
299             ts.monthOrdinal = localDateTime.getMonthValue();
300             ts.monthDay = localDateTime.getDayOfMonth();
301             ts.hour = localDateTime.getHour();
302             ts.minute = localDateTime.getMinute();
303             ts.second = localDateTime.getSecond();
304             return ts;
305         }
306 
toByteArray()307         public byte[] toByteArray() {
308             int year = this.year % 100; // 00 - 99
309             ByteArrayOutputStream outStream = new ByteArrayOutputStream(6);
310             outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F));
311             outStream.write((((monthOrdinal / 10) << 4) & 0xF0) | ((monthOrdinal % 10) & 0x0F));
312             outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F));
313             outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F));
314             outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F));
315             outStream.write((((second / 10) << 4) & 0xF0) | ((second % 10) & 0x0F));
316             return outStream.toByteArray();
317         }
318 
toMillis()319         public long toMillis() {
320             try {
321                 LocalDateTime localDateTime =
322                         LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
323                 Instant instant =
324                         localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
325                 return instant.toEpochMilli();
326             } catch (DateTimeException ex) {
327                 Rlog.e(LOG_TAG, "Invalid timestamp", ex);
328             }
329             return 0;
330         }
331 
332 
333         @Override
toString()334         public String toString() {
335             StringBuilder builder = new StringBuilder();
336             builder.append("TimeStamp ");
337             builder.append("{ year=" + year);
338             builder.append(", month=" + monthOrdinal);
339             builder.append(", day=" + monthDay);
340             builder.append(", hour=" + hour);
341             builder.append(", minute=" + minute);
342             builder.append(", second=" + second);
343             builder.append(" }");
344             return builder.toString();
345         }
346     }
347 
348     @UnsupportedAppUsage
349     public TimeStamp msgCenterTimeStamp;
350     public TimeStamp validityPeriodAbsolute;
351     public TimeStamp deferredDeliveryTimeAbsolute;
352 
353     /**
354      * Relative time is specified as one byte, the value of which
355      * falls into a series of ranges, as specified below.  The idea is
356      * that shorter time intervals allow greater precision -- the
357      * value means minutes from zero until the MINS_LIMIT (inclusive),
358      * upon which it means hours until the HOURS_LIMIT, and so
359      * forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1)
360      */
361     public static final int RELATIVE_TIME_MINS_LIMIT      = 143;
362     public static final int RELATIVE_TIME_HOURS_LIMIT     = 167;
363     public static final int RELATIVE_TIME_DAYS_LIMIT      = 196;
364     public static final int RELATIVE_TIME_WEEKS_LIMIT     = 244;
365     public static final int RELATIVE_TIME_INDEFINITE      = 245;
366     public static final int RELATIVE_TIME_NOW             = 246;
367     public static final int RELATIVE_TIME_MOBILE_INACTIVE = 247;
368     public static final int RELATIVE_TIME_RESERVED        = 248;
369 
370     public boolean validityPeriodRelativeSet;
371     public int validityPeriodRelative;
372     public boolean deferredDeliveryTimeRelativeSet;
373     public int deferredDeliveryTimeRelative;
374 
375     /**
376      * The Reply Option subparameter contains 1-bit values which
377      * indicate whether SMS acknowledgment is requested or not.  (See
378      * 3GPP2 C.S0015-B, v2, 4.5.11)
379      */
380     public boolean userAckReq;
381     public boolean deliveryAckReq;
382     public boolean readAckReq;
383     public boolean reportReq;
384 
385     /**
386      * The Number of Messages subparameter (8-bit value) is a decimal
387      * number in the 0 to 99 range representing the number of messages
388      * stored at the Voice Mail System. This element is used by the
389      * Voice Mail Notification service.  (See 3GPP2 C.S0015-B, v2,
390      * 4.5.12)
391      */
392     public int numberOfMessages;
393 
394     /**
395      * The Message Deposit Index subparameter is assigned by the
396      * message center as a unique index to the contents of the User
397      * Data subparameter in each message sent to a particular mobile
398      * station. The mobile station, when replying to a previously
399      * received short message which included a Message Deposit Index
400      * subparameter, may include the Message Deposit Index of the
401      * received message to indicate to the message center that the
402      * original contents of the message are to be included in the
403      * reply.  (See 3GPP2 C.S0015-B, v2, 4.5.18)
404      */
405     public int depositIndex;
406 
407     /**
408      * 4-bit or 8-bit value that indicates the number to be dialed in reply to a
409      * received SMS message.
410      * (See 3GPP2 C.S0015-B, v2, 4.5.15)
411      */
412     public CdmaSmsAddress callbackNumber;
413 
414     /**
415      * CMAS warning notification information.
416      * @see #decodeCmasUserData(BearerData, int)
417      */
418     public SmsCbCmasInfo cmasWarningInfo;
419 
420     /**
421      * The Service Category Program Data subparameter is used to enable and disable
422      * SMS broadcast service categories to display. If this subparameter is present,
423      * this field will contain a list of one or more
424      * {@link android.telephony.cdma.CdmaSmsCbProgramData} objects containing the
425      * operation(s) to perform.
426      */
427     public ArrayList<CdmaSmsCbProgramData> serviceCategoryProgramData;
428 
429     /**
430      * The Service Category Program Results subparameter informs the message center
431      * of the results of a Service Category Program Data request.
432      */
433     public ArrayList<CdmaSmsCbProgramResults> serviceCategoryProgramResults;
434 
435 
436     private static class CodingException extends Exception {
437         @UnsupportedAppUsage
CodingException(String s)438         public CodingException(String s) {
439             super(s);
440         }
441     }
442 
443     /**
444      * Returns the language indicator as a two-character ISO 639 string.
445      * @return a two character ISO 639 language code
446      */
getLanguage()447     public String getLanguage() {
448         return getLanguageCodeForValue(language);
449     }
450 
451     /**
452      * Converts a CDMA language indicator value to an ISO 639 two character language code.
453      * @param languageValue the CDMA language value to convert
454      * @return the two character ISO 639 language code for the specified value, or null if unknown
455      */
getLanguageCodeForValue(int languageValue)456     private static String getLanguageCodeForValue(int languageValue) {
457         switch (languageValue) {
458             case LANGUAGE_ENGLISH:
459                 return "en";
460 
461             case LANGUAGE_FRENCH:
462                 return "fr";
463 
464             case LANGUAGE_SPANISH:
465                 return "es";
466 
467             case LANGUAGE_JAPANESE:
468                 return "ja";
469 
470             case LANGUAGE_KOREAN:
471                 return "ko";
472 
473             case LANGUAGE_CHINESE:
474                 return "zh";
475 
476             case LANGUAGE_HEBREW:
477                 return "he";
478 
479             default:
480                 return null;
481         }
482     }
483 
484     @Override
toString()485     public String toString() {
486         StringBuilder builder = new StringBuilder();
487         builder.append("BearerData ");
488         builder.append("{ messageType=" + messageType);
489         builder.append(", messageId=" + messageId);
490         builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
491         builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
492         builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
493         builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
494         builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
495         builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
496         builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
497         builder.append(", msgCenterTimeStamp=" +
498                 ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset"));
499         builder.append(", validityPeriodAbsolute=" +
500                 ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset"));
501         builder.append(", validityPeriodRelative=" +
502                 ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset"));
503         builder.append(", deferredDeliveryTimeAbsolute=" +
504                 ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset"));
505         builder.append(", deferredDeliveryTimeRelative=" +
506                 ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset"));
507         builder.append(", userAckReq=" + userAckReq);
508         builder.append(", deliveryAckReq=" + deliveryAckReq);
509         builder.append(", readAckReq=" + readAckReq);
510         builder.append(", reportReq=" + reportReq);
511         builder.append(", numberOfMessages=" + numberOfMessages);
512         builder.append(", callbackNumber=" + Rlog.pii(LOG_TAG, callbackNumber));
513         builder.append(", depositIndex=" + depositIndex);
514         builder.append(", hasUserDataHeader=" + hasUserDataHeader);
515         builder.append(", userData=" + userData);
516         builder.append(" }");
517         return builder.toString();
518     }
519 
encodeMessageId(BearerData bData, BitwiseOutputStream outStream)520     private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream)
521         throws BitwiseOutputStream.AccessException
522     {
523         outStream.write(8, 3);
524         outStream.write(4, bData.messageType);
525         outStream.write(8, bData.messageId >> 8);
526         outStream.write(8, bData.messageId);
527         outStream.write(1, bData.hasUserDataHeader ? 1 : 0);
528         outStream.skip(3);
529     }
530 
531     @UnsupportedAppUsage
countAsciiSeptets(CharSequence msg, boolean force)532     private static int countAsciiSeptets(CharSequence msg, boolean force) {
533         int msgLen = msg.length();
534         if (force) return msgLen;
535         for (int i = 0; i < msgLen; i++) {
536             if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
537                 return -1;
538             }
539         }
540         return msgLen;
541     }
542 
543     /**
544      * Calculate the message text encoding length, fragmentation, and other details.
545      *
546      * @param msg message text
547      * @param force7BitEncoding ignore (but still count) illegal characters if true
548      * @param isEntireMsg indicates if this is entire msg or a segment in multipart msg
549      * @return septet count, or -1 on failure
550      */
calcTextEncodingDetails(CharSequence msg, boolean force7BitEncoding, boolean isEntireMsg)551     public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
552             boolean force7BitEncoding, boolean isEntireMsg) {
553         CharSequence newMsg = null;
554         Resources r = Resources.getSystem();
555         if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
556             newMsg = Sms7BitEncodingTranslator.translate(msg, true /* isCdmaFormat */);
557         }
558         if (TextUtils.isEmpty(newMsg)) {
559             newMsg = msg;
560         }
561 
562         TextEncodingDetails ted;
563         int septets = countAsciiSeptets(newMsg, force7BitEncoding);
564         if (septets != -1 && septets <= SmsConstants.MAX_USER_DATA_SEPTETS) {
565             ted = new TextEncodingDetails();
566             ted.msgCount = 1;
567             ted.codeUnitCount = septets;
568             ted.codeUnitsRemaining = SmsConstants.MAX_USER_DATA_SEPTETS - septets;
569             ted.codeUnitSize = SmsConstants.ENCODING_7BIT;
570         } else {
571             ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
572                     msg, force7BitEncoding);
573             if (ted.msgCount == 1 && ted.codeUnitSize == SmsConstants.ENCODING_7BIT &&
574                     isEntireMsg) {
575                 // We don't support single-segment EMS, so calculate for 16-bit
576                 // TODO: Consider supporting single-segment EMS
577                 return SmsMessageBase.calcUnicodeEncodingDetails(msg);
578             }
579         }
580         return ted;
581     }
582 
583     @UnsupportedAppUsage
encode7bitAscii(String msg, boolean force)584     private static byte[] encode7bitAscii(String msg, boolean force)
585         throws CodingException
586     {
587         try {
588             BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
589             int msgLen = msg.length();
590             for (int i = 0; i < msgLen; i++) {
591                 int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
592                 if (charCode == -1) {
593                     if (force) {
594                         outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
595                     } else {
596                         throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
597                     }
598                 } else {
599                     outStream.write(7, charCode);
600                 }
601             }
602             return outStream.toByteArray();
603         } catch (BitwiseOutputStream.AccessException ex) {
604             throw new CodingException("7bit ASCII encode failed: " + ex);
605         }
606     }
607 
encodeUtf16(String msg)608     private static byte[] encodeUtf16(String msg)
609         throws CodingException
610     {
611         try {
612             return msg.getBytes("utf-16be");
613         } catch (java.io.UnsupportedEncodingException ex) {
614             throw new CodingException("UTF-16 encode failed: " + ex);
615         }
616     }
617 
618     private static class Gsm7bitCodingResult {
619         int septets;
620         byte[] data;
621     }
622 
encode7bitGsm(String msg, int septetOffset, boolean force)623     private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force)
624         throws CodingException
625     {
626         try {
627             /*
628              * TODO(cleanup): It would be nice if GsmAlphabet provided
629              * an option to produce just the data without prepending
630              * the septet count, as this function is really just a
631              * wrapper to strip that off.  Not to mention that the
632              * septet count is generally known prior to invocation of
633              * the encoder.  Note that it cannot be derived from the
634              * resulting array length, since that cannot distinguish
635              * if the last contains either 1 or 8 valid bits.
636              *
637              * TODO(cleanup): The BitwiseXStreams could also be
638              * extended with byte-wise reversed endianness read/write
639              * routines to allow a corresponding implementation of
640              * stringToGsm7BitPacked, and potentially directly support
641              * access to the main bitwise stream from encode/decode.
642              */
643             byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
644             Gsm7bitCodingResult result = new Gsm7bitCodingResult();
645             result.data = new byte[fullData.length - 1];
646             System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
647             result.septets = fullData[0] & 0x00FF;
648             return result;
649         } catch (com.android.internal.telephony.EncodeException ex) {
650             throw new CodingException("7bit GSM encode failed: " + ex);
651         }
652     }
653 
encode7bitEms(UserData uData, byte[] udhData, boolean force)654     private static void encode7bitEms(UserData uData, byte[] udhData, boolean force)
655         throws CodingException
656     {
657         int udhBytes = udhData.length + 1;  // Add length octet.
658         int udhSeptets = ((udhBytes * 8) + 6) / 7;
659         Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
660         uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
661         uData.msgEncodingSet = true;
662         uData.numFields = gcr.septets;
663         uData.payload = gcr.data;
664         uData.payload[0] = (byte)udhData.length;
665         System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
666     }
667 
encode16bitEms(UserData uData, byte[] udhData)668     private static void encode16bitEms(UserData uData, byte[] udhData)
669         throws CodingException
670     {
671         byte[] payload = encodeUtf16(uData.payloadStr);
672         int udhBytes = udhData.length + 1;  // Add length octet.
673         int udhCodeUnits = (udhBytes + 1) / 2;
674         int payloadCodeUnits = payload.length / 2;
675         uData.msgEncoding = UserData.ENCODING_UNICODE_16;
676         uData.msgEncodingSet = true;
677         uData.numFields = udhCodeUnits + payloadCodeUnits;
678         uData.payload = new byte[uData.numFields * 2];
679         uData.payload[0] = (byte)udhData.length;
680         System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
681         System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
682     }
683 
encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force)684     private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force)
685             throws CodingException
686     {
687         try {
688             Rlog.d(LOG_TAG, "encode7bitAsciiEms");
689             int udhBytes = udhData.length + 1;  // Add length octet.
690             int udhSeptets = ((udhBytes * 8) + 6) / 7;
691             int paddingBits = (udhSeptets * 7) - (udhBytes * 8);
692             String msg = uData.payloadStr;
693             byte[] payload ;
694             int msgLen = msg.length();
695             BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen +
696                     (paddingBits > 0 ? 1 : 0));
697             outStream.write(paddingBits, 0);
698             for (int i = 0; i < msgLen; i++) {
699                 int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
700                 if (charCode == -1) {
701                     if (force) {
702                         outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
703                     } else {
704                         throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
705                     }
706                 } else {
707                     outStream.write(7, charCode);
708                 }
709             }
710             payload = outStream.toByteArray();
711             uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
712             uData.msgEncodingSet = true;
713             uData.numFields = udhSeptets + uData.payloadStr.length();
714             uData.payload = new byte[udhBytes + payload.length];
715             uData.payload[0] = (byte)udhData.length;
716             System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
717             System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
718         } catch (BitwiseOutputStream.AccessException ex) {
719             throw new CodingException("7bit ASCII encode failed: " + ex);
720         }
721     }
722 
encodeEmsUserDataPayload(UserData uData)723     private static void encodeEmsUserDataPayload(UserData uData)
724         throws CodingException
725     {
726         byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
727         if (uData.msgEncodingSet) {
728             if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
729                 encode7bitEms(uData, headerData, true);
730             } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
731                 encode16bitEms(uData, headerData);
732             } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
733                 encode7bitAsciiEms(uData, headerData, true);
734             } else {
735                 throw new CodingException("unsupported EMS user data encoding (" +
736                                           uData.msgEncoding + ")");
737             }
738         } else {
739             try {
740                 encode7bitEms(uData, headerData, false);
741             } catch (CodingException ex) {
742                 encode16bitEms(uData, headerData);
743             }
744         }
745     }
746 
encodeShiftJis(String msg)747     private static byte[] encodeShiftJis(String msg) throws CodingException {
748         try {
749             return msg.getBytes("Shift_JIS");
750         } catch (java.io.UnsupportedEncodingException ex) {
751             throw new CodingException("Shift-JIS encode failed: " + ex);
752         }
753     }
754 
encodeUserDataPayload(UserData uData)755     private static void encodeUserDataPayload(UserData uData)
756         throws CodingException
757     {
758         if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
759             Rlog.e(LOG_TAG, "user data with null payloadStr");
760             uData.payloadStr = "";
761         }
762 
763         if (uData.userDataHeader != null) {
764             encodeEmsUserDataPayload(uData);
765             return;
766         }
767 
768         if (uData.msgEncodingSet) {
769             if (uData.msgEncoding == UserData.ENCODING_OCTET) {
770                 if (uData.payload == null) {
771                     Rlog.e(LOG_TAG, "user data with octet encoding but null payload");
772                     uData.payload = new byte[0];
773                     uData.numFields = 0;
774                 } else {
775                     uData.numFields = uData.payload.length;
776                 }
777             } else {
778                 if (uData.payloadStr == null) {
779                     Rlog.e(LOG_TAG, "non-octet user data with null payloadStr");
780                     uData.payloadStr = "";
781                 }
782                 if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
783                     Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
784                     uData.payload = gcr.data;
785                     uData.numFields = gcr.septets;
786                 } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
787                     uData.payload = encode7bitAscii(uData.payloadStr, true);
788                     uData.numFields = uData.payloadStr.length();
789                 } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
790                     uData.payload = encodeUtf16(uData.payloadStr);
791                     uData.numFields = uData.payloadStr.length();
792                 } else if (uData.msgEncoding == UserData.ENCODING_SHIFT_JIS) {
793                     uData.payload = encodeShiftJis(uData.payloadStr);
794                     uData.numFields = uData.payload.length;
795                 } else {
796                     throw new CodingException("unsupported user data encoding (" +
797                                               uData.msgEncoding + ")");
798                 }
799             }
800         } else {
801             try {
802                 uData.payload = encode7bitAscii(uData.payloadStr, false);
803                 uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
804             } catch (CodingException ex) {
805                 uData.payload = encodeUtf16(uData.payloadStr);
806                 uData.msgEncoding = UserData.ENCODING_UNICODE_16;
807             }
808             uData.numFields = uData.payloadStr.length();
809             uData.msgEncodingSet = true;
810         }
811     }
812 
encodeUserData(BearerData bData, BitwiseOutputStream outStream)813     private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
814         throws BitwiseOutputStream.AccessException, CodingException
815     {
816         /*
817          * TODO(cleanup): Do we really need to set userData.payload as
818          * a side effect of encoding?  If not, we could avoid data
819          * copies by passing outStream directly.
820          */
821         encodeUserDataPayload(bData.userData);
822         bData.hasUserDataHeader = bData.userData.userDataHeader != null;
823 
824         if (bData.userData.payload.length > SmsConstants.MAX_USER_DATA_BYTES) {
825             throw new CodingException("encoded user data too large (" +
826                                       bData.userData.payload.length +
827                                       " > " + SmsConstants.MAX_USER_DATA_BYTES + " bytes)");
828         }
829 
830         if (bData.userData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
831             bData.userData.paddingBits =
832                     (bData.userData.payload.length * 8) - (bData.userData.numFields * 7);
833         } else {
834             bData.userData.paddingBits = 0;
835         }
836 
837         int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
838         int paramBits = dataBits + 13;
839         if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
840             (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
841             paramBits += 8;
842         }
843         int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
844         int paddingBits = (paramBytes * 8) - paramBits;
845         outStream.write(8, paramBytes);
846         outStream.write(5, bData.userData.msgEncoding);
847         if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
848             (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
849             outStream.write(8, bData.userData.msgType);
850         }
851         outStream.write(8, bData.userData.numFields);
852         outStream.writeByteArray(dataBits, bData.userData.payload);
853         if (paddingBits > 0) outStream.write(paddingBits, 0);
854     }
855 
encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)856     private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)
857         throws BitwiseOutputStream.AccessException
858     {
859         outStream.write(8, 1);
860         outStream.write(1, bData.userAckReq     ? 1 : 0);
861         outStream.write(1, bData.deliveryAckReq ? 1 : 0);
862         outStream.write(1, bData.readAckReq     ? 1 : 0);
863         outStream.write(1, bData.reportReq      ? 1 : 0);
864         outStream.write(4, 0);
865     }
866 
encodeDtmfSmsAddress(String address)867     private static byte[] encodeDtmfSmsAddress(String address) {
868         int digits = address.length();
869         int dataBits = digits * 4;
870         int dataBytes = (dataBits / 8);
871         dataBytes += (dataBits % 8) > 0 ? 1 : 0;
872         byte[] rawData = new byte[dataBytes];
873         for (int i = 0; i < digits; i++) {
874             char c = address.charAt(i);
875             int val = 0;
876             if ((c >= '1') && (c <= '9')) val = c - '0';
877             else if (c == '0') val = 10;
878             else if (c == '*') val = 11;
879             else if (c == '#') val = 12;
880             else return null;
881             rawData[i / 2] |= val << (4 - ((i % 2) * 4));
882         }
883         return rawData;
884     }
885 
886     /*
887      * TODO(cleanup): CdmaSmsAddress encoding should make use of
888      * CdmaSmsAddress.parse provided that DTMF encoding is unified,
889      * and the difference in 4-bit vs. 8-bit is resolved.
890      */
891 
encodeCdmaSmsAddress(CdmaSmsAddress addr)892     private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
893         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
894             try {
895                 addr.origBytes = addr.address.getBytes("US-ASCII");
896             } catch (java.io.UnsupportedEncodingException ex) {
897                 throw new CodingException("invalid SMS address, cannot convert to ASCII");
898             }
899         } else {
900             addr.origBytes = encodeDtmfSmsAddress(addr.address);
901         }
902     }
903 
encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)904     private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)
905         throws BitwiseOutputStream.AccessException, CodingException
906     {
907         CdmaSmsAddress addr = bData.callbackNumber;
908         encodeCdmaSmsAddress(addr);
909         int paramBits = 9;
910         int dataBits = 0;
911         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
912             paramBits += 7;
913             dataBits = addr.numberOfDigits * 8;
914         } else {
915             dataBits = addr.numberOfDigits * 4;
916         }
917         paramBits += dataBits;
918         int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
919         int paddingBits = (paramBytes * 8) - paramBits;
920         outStream.write(8, paramBytes);
921         outStream.write(1, addr.digitMode);
922         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
923             outStream.write(3, addr.ton);
924             outStream.write(4, addr.numberPlan);
925         }
926         outStream.write(8, addr.numberOfDigits);
927         outStream.writeByteArray(dataBits, addr.origBytes);
928         if (paddingBits > 0) outStream.write(paddingBits, 0);
929     }
930 
encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)931     private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)
932         throws BitwiseOutputStream.AccessException
933     {
934         outStream.write(8, 1);
935         outStream.write(2, bData.errorClass);
936         outStream.write(6, bData.messageStatus);
937     }
938 
encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)939     private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)
940         throws BitwiseOutputStream.AccessException
941     {
942         outStream.write(8, 1);
943         outStream.write(8, bData.numberOfMessages);
944     }
945 
encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)946     private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)
947         throws BitwiseOutputStream.AccessException
948     {
949         outStream.write(8, 1);
950         outStream.write(8, bData.validityPeriodRelative);
951     }
952 
encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)953     private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
954         throws BitwiseOutputStream.AccessException
955     {
956         outStream.write(8, 1);
957         outStream.write(2, bData.privacy);
958         outStream.skip(6);
959     }
960 
encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)961     private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)
962         throws BitwiseOutputStream.AccessException
963     {
964         outStream.write(8, 1);
965         outStream.write(8, bData.language);
966     }
967 
encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)968     private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)
969         throws BitwiseOutputStream.AccessException
970     {
971         outStream.write(8, 1);
972         outStream.write(2, bData.displayMode);
973         outStream.skip(6);
974     }
975 
encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)976     private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)
977         throws BitwiseOutputStream.AccessException
978     {
979         outStream.write(8, 1);
980         outStream.write(2, bData.priority);
981         outStream.skip(6);
982     }
983 
encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)984     private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)
985         throws BitwiseOutputStream.AccessException
986     {
987         outStream.write(8, 1);
988         outStream.write(2, bData.alert);
989         outStream.skip(6);
990     }
991 
encodeScpResults(BearerData bData, BitwiseOutputStream outStream)992     private static void encodeScpResults(BearerData bData, BitwiseOutputStream outStream)
993         throws BitwiseOutputStream.AccessException
994     {
995         ArrayList<CdmaSmsCbProgramResults> results = bData.serviceCategoryProgramResults;
996         outStream.write(8, (results.size() * 4));   // 4 octets per program result
997         for (CdmaSmsCbProgramResults result : results) {
998             int category = result.getCategory();
999             outStream.write(8, category >> 8);
1000             outStream.write(8, category);
1001             outStream.write(8, result.getLanguage());
1002             outStream.write(4, result.getCategoryResult());
1003             outStream.skip(4);
1004         }
1005     }
1006 
encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)1007     private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)
1008             throws BitwiseOutputStream.AccessException {
1009         outStream.write(8, 6);
1010         outStream.writeByteArray(8 * 6, bData.msgCenterTimeStamp.toByteArray());
1011     };
1012 
1013     /**
1014      * Create serialized representation for BearerData object.
1015      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
1016      *
1017      * @param bData an instance of BearerData.
1018      *
1019      * @return byte array of raw encoded SMS bearer data.
1020      */
1021     @UnsupportedAppUsage
encode(BearerData bData)1022     public static byte[] encode(BearerData bData) {
1023         bData.hasUserDataHeader = ((bData.userData != null) &&
1024                 (bData.userData.userDataHeader != null));
1025         try {
1026             BitwiseOutputStream outStream = new BitwiseOutputStream(200);
1027             outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
1028             encodeMessageId(bData, outStream);
1029             if (bData.userData != null) {
1030                 outStream.write(8, SUBPARAM_USER_DATA);
1031                 encodeUserData(bData, outStream);
1032             }
1033             if (bData.callbackNumber != null) {
1034                 outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
1035                 encodeCallbackNumber(bData, outStream);
1036             }
1037             if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
1038                 outStream.write(8, SUBPARAM_REPLY_OPTION);
1039                 encodeReplyOption(bData, outStream);
1040             }
1041             if (bData.numberOfMessages != 0) {
1042                 outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
1043                 encodeMsgCount(bData, outStream);
1044             }
1045             if (bData.validityPeriodRelativeSet) {
1046                 outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
1047                 encodeValidityPeriodRel(bData, outStream);
1048             }
1049             if (bData.privacyIndicatorSet) {
1050                 outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
1051                 encodePrivacyIndicator(bData, outStream);
1052             }
1053             if (bData.languageIndicatorSet) {
1054                 outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
1055                 encodeLanguageIndicator(bData, outStream);
1056             }
1057             if (bData.displayModeSet) {
1058                 outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
1059                 encodeDisplayMode(bData, outStream);
1060             }
1061             if (bData.priorityIndicatorSet) {
1062                 outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
1063                 encodePriorityIndicator(bData, outStream);
1064             }
1065             if (bData.alertIndicatorSet) {
1066                 outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
1067                 encodeMsgDeliveryAlert(bData, outStream);
1068             }
1069             if (bData.messageStatusSet) {
1070                 outStream.write(8, SUBPARAM_MESSAGE_STATUS);
1071                 encodeMsgStatus(bData, outStream);
1072             }
1073             if (bData.serviceCategoryProgramResults != null) {
1074                 outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
1075                 encodeScpResults(bData, outStream);
1076             }
1077             if (bData.msgCenterTimeStamp != null) {
1078                 outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
1079                 encodeMsgCenterTimeStamp(bData, outStream);
1080             }
1081             return outStream.toByteArray();
1082         } catch (BitwiseOutputStream.AccessException ex) {
1083             Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
1084         } catch (CodingException ex) {
1085             Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
1086         }
1087         return null;
1088    }
1089 
decodeMessageId(BearerData bData, BitwiseInputStream inStream)1090     private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
1091         throws BitwiseInputStream.AccessException {
1092         final int EXPECTED_PARAM_SIZE = 3 * 8;
1093         boolean decodeSuccess = false;
1094         int paramBits = inStream.read(8) * 8;
1095         if (paramBits >= EXPECTED_PARAM_SIZE) {
1096             paramBits -= EXPECTED_PARAM_SIZE;
1097             decodeSuccess = true;
1098             bData.messageType = inStream.read(4);
1099             bData.messageId = inStream.read(8) << 8;
1100             bData.messageId |= inStream.read(8);
1101             bData.hasUserDataHeader = (inStream.read(1) == 1);
1102             inStream.skip(3);
1103         }
1104         if ((!decodeSuccess) || (paramBits > 0)) {
1105             Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
1106                       (decodeSuccess ? "succeeded" : "failed") +
1107                       " (extra bits = " + paramBits + ")");
1108         }
1109         inStream.skip(paramBits);
1110         return decodeSuccess;
1111     }
1112 
decodeReserved( BearerData bData, BitwiseInputStream inStream, int subparamId)1113     private static boolean decodeReserved(
1114             BearerData bData, BitwiseInputStream inStream, int subparamId)
1115         throws BitwiseInputStream.AccessException, CodingException
1116     {
1117         boolean decodeSuccess = false;
1118         int subparamLen = inStream.read(8); // SUBPARAM_LEN
1119         int paramBits = subparamLen * 8;
1120         if (paramBits <= inStream.available()) {
1121             decodeSuccess = true;
1122             inStream.skip(paramBits);
1123         }
1124         Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode "
1125                 + (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")");
1126         if (!decodeSuccess) {
1127             throw new CodingException("RESERVED bearer data subparameter " + subparamId
1128                     + " had invalid SUBPARAM_LEN " + subparamLen);
1129         }
1130 
1131         return decodeSuccess;
1132     }
1133 
decodeUserData(BearerData bData, BitwiseInputStream inStream)1134     private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
1135         throws BitwiseInputStream.AccessException
1136     {
1137         int paramBits = inStream.read(8) * 8;
1138         bData.userData = new UserData();
1139         bData.userData.msgEncoding = inStream.read(5);
1140         bData.userData.msgEncodingSet = true;
1141         bData.userData.msgType = 0;
1142         int consumedBits = 5;
1143         if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
1144             (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
1145             bData.userData.msgType = inStream.read(8);
1146             consumedBits += 8;
1147         }
1148         bData.userData.numFields = inStream.read(8);
1149         consumedBits += 8;
1150         int dataBits = paramBits - consumedBits;
1151         bData.userData.payload = inStream.readByteArray(dataBits);
1152         return true;
1153     }
1154 
decodeUtf8(byte[] data, int offset, int numFields)1155     private static String decodeUtf8(byte[] data, int offset, int numFields)
1156         throws CodingException
1157     {
1158         return decodeCharset(data, offset, numFields, 1, "UTF-8");
1159     }
1160 
decodeUtf16(byte[] data, int offset, int numFields)1161     private static String decodeUtf16(byte[] data, int offset, int numFields)
1162         throws CodingException
1163     {
1164         // Subtract header and possible padding byte (at end) from num fields.
1165         int padding = offset % 2;
1166         numFields -= (offset + padding) / 2;
1167         return decodeCharset(data, offset, numFields, 2, "utf-16be");
1168     }
1169 
decodeCharset(byte[] data, int offset, int numFields, int width, String charset)1170     private static String decodeCharset(byte[] data, int offset, int numFields, int width,
1171             String charset) throws CodingException
1172     {
1173         if (numFields < 0 || (numFields * width + offset) > data.length) {
1174             // Try to decode the max number of characters in payload
1175             int padding = offset % width;
1176             int maxNumFields = (data.length - offset - padding) / width;
1177             if (maxNumFields < 0) {
1178                 throw new CodingException(charset + " decode failed: offset out of range");
1179             }
1180             Rlog.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
1181                     + numFields + " data.length = " + data.length + " maxNumFields = "
1182                     + maxNumFields);
1183             numFields = maxNumFields;
1184         }
1185         try {
1186             return new String(data, offset, numFields * width, charset);
1187         } catch (java.io.UnsupportedEncodingException ex) {
1188             throw new CodingException(charset + " decode failed: " + ex);
1189         }
1190     }
1191 
decode7bitAscii(byte[] data, int offset, int numFields)1192     private static String decode7bitAscii(byte[] data, int offset, int numFields)
1193         throws CodingException
1194     {
1195         try {
1196             int offsetBits = offset * 8;
1197             int offsetSeptets = (offsetBits + 6) / 7;
1198             numFields -= offsetSeptets;
1199 
1200             StringBuffer strBuf = new StringBuffer(numFields);
1201             BitwiseInputStream inStream = new BitwiseInputStream(data);
1202             int wantedBits = (offsetSeptets * 7) + (numFields * 7);
1203             if (inStream.available() < wantedBits) {
1204                 throw new CodingException("insufficient data (wanted " + wantedBits +
1205                                           " bits, but only have " + inStream.available() + ")");
1206             }
1207             inStream.skip(offsetSeptets * 7);
1208             for (int i = 0; i < numFields; i++) {
1209                 int charCode = inStream.read(7);
1210                 if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
1211                         (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
1212                     strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
1213                 } else if (charCode == UserData.ASCII_NL_INDEX) {
1214                     strBuf.append('\n');
1215                 } else if (charCode == UserData.ASCII_CR_INDEX) {
1216                     strBuf.append('\r');
1217                 } else {
1218                     /* For other charCodes, they are unprintable, and so simply use SPACE. */
1219                     strBuf.append(' ');
1220                 }
1221             }
1222             return strBuf.toString();
1223         } catch (BitwiseInputStream.AccessException ex) {
1224             throw new CodingException("7bit ASCII decode failed: " + ex);
1225         }
1226     }
1227 
decode7bitGsm(byte[] data, int offset, int numFields)1228     private static String decode7bitGsm(byte[] data, int offset, int numFields)
1229         throws CodingException
1230     {
1231         // Start reading from the next 7-bit aligned boundary after offset.
1232         int offsetBits = offset * 8;
1233         int offsetSeptets = (offsetBits + 6) / 7;
1234         numFields -= offsetSeptets;
1235         int paddingBits = (offsetSeptets * 7) - offsetBits;
1236         String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
1237                 0, 0);
1238         if (result == null) {
1239             throw new CodingException("7bit GSM decoding failed");
1240         }
1241         return result;
1242     }
1243 
decodeLatin(byte[] data, int offset, int numFields)1244     private static String decodeLatin(byte[] data, int offset, int numFields)
1245         throws CodingException
1246     {
1247         return decodeCharset(data, offset, numFields, 1, "ISO-8859-1");
1248     }
1249 
decodeShiftJis(byte[] data, int offset, int numFields)1250     private static String decodeShiftJis(byte[] data, int offset, int numFields)
1251         throws CodingException
1252     {
1253         return decodeCharset(data, offset, numFields, 1, "Shift_JIS");
1254     }
1255 
decodeGsmDcs(byte[] data, int offset, int numFields, int msgType)1256     private static String decodeGsmDcs(byte[] data, int offset, int numFields, int msgType)
1257             throws CodingException
1258     {
1259         if ((msgType & 0xC0) != 0) {
1260             throw new CodingException("unsupported coding group ("
1261                     + msgType + ")");
1262         }
1263 
1264         switch ((msgType >> 2) & 0x3) {
1265         case UserData.ENCODING_GSM_DCS_7BIT:
1266             return decode7bitGsm(data, offset, numFields);
1267         case UserData.ENCODING_GSM_DCS_8BIT:
1268             return decodeUtf8(data, offset, numFields);
1269         case UserData.ENCODING_GSM_DCS_16BIT:
1270             return decodeUtf16(data, offset, numFields);
1271         default:
1272             throw new CodingException("unsupported user msgType encoding ("
1273                     + msgType + ")");
1274         }
1275     }
1276 
1277     @UnsupportedAppUsage
decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)1278     private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
1279         throws CodingException
1280     {
1281         int offset = 0;
1282         if (hasUserDataHeader) {
1283             int udhLen = userData.payload[0] & 0x00FF;
1284             offset += udhLen + 1;
1285             byte[] headerData = new byte[udhLen];
1286             System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
1287             userData.userDataHeader = SmsHeader.fromByteArray(headerData);
1288         }
1289         switch (userData.msgEncoding) {
1290         case UserData.ENCODING_OCTET:
1291             /*
1292             *  Octet decoding depends on the carrier service.
1293             */
1294             boolean decodingtypeUTF8 = Resources.getSystem()
1295                     .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
1296 
1297             // Strip off any padding bytes, meaning any differences between the length of the
1298             // array and the target length specified by numFields.  This is to avoid any
1299             // confusion by code elsewhere that only considers the payload array length.
1300             byte[] payload = new byte[userData.numFields];
1301             int copyLen = userData.numFields < userData.payload.length
1302                     ? userData.numFields : userData.payload.length;
1303 
1304             System.arraycopy(userData.payload, 0, payload, 0, copyLen);
1305             userData.payload = payload;
1306 
1307             if (!decodingtypeUTF8) {
1308                 // There are many devices in the market that send 8bit text sms (latin encoded) as
1309                 // octet encoded.
1310                 userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1311             } else {
1312                 userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
1313             }
1314             break;
1315 
1316         case UserData.ENCODING_IA5:
1317         case UserData.ENCODING_7BIT_ASCII:
1318             userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
1319             break;
1320         case UserData.ENCODING_UNICODE_16:
1321             userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
1322             break;
1323         case UserData.ENCODING_GSM_7BIT_ALPHABET:
1324             userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
1325             break;
1326         case UserData.ENCODING_LATIN:
1327             userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1328             break;
1329         case UserData.ENCODING_SHIFT_JIS:
1330             userData.payloadStr = decodeShiftJis(userData.payload, offset, userData.numFields);
1331             break;
1332         case UserData.ENCODING_GSM_DCS:
1333             userData.payloadStr = decodeGsmDcs(userData.payload, offset,
1334                     userData.numFields, userData.msgType);
1335             break;
1336         default:
1337             throw new CodingException("unsupported user data encoding ("
1338                                       + userData.msgEncoding + ")");
1339         }
1340     }
1341 
1342     /**
1343      * IS-91 Voice Mail message decoding
1344      * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1345      * (For character encodings, see TIA/EIA/IS-91, Annex B)
1346      *
1347      * Protocol Summary: The user data payload may contain 3-14
1348      * characters.  The first two characters are parsed as a number
1349      * and indicate the number of voicemails.  The third character is
1350      * either a SPACE or '!' to indicate normal or urgent priority,
1351      * respectively.  Any following characters are treated as normal
1352      * text user data payload.
1353      *
1354      * Note that the characters encoding is 6-bit packed.
1355      */
1356     private static void decodeIs91VoicemailStatus(BearerData bData)
1357         throws BitwiseInputStream.AccessException, CodingException
1358     {
1359         BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1360         int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1361         int numFields = bData.userData.numFields;
1362         if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1363             throw new CodingException("IS-91 voicemail status decoding failed");
1364         }
1365         try {
1366             StringBuffer strbuf = new StringBuffer(dataLen);
1367             while (inStream.available() >= 6) {
1368                 strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1369             }
1370             String data = strbuf.toString();
1371             bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
1372             char prioCode = data.charAt(2);
1373             if (prioCode == ' ') {
1374                 bData.priority = PRIORITY_NORMAL;
1375             } else if (prioCode == '!') {
1376                 bData.priority = PRIORITY_URGENT;
1377             } else {
1378                 throw new CodingException("IS-91 voicemail status decoding failed: " +
1379                         "illegal priority setting (" + prioCode + ")");
1380             }
1381             bData.priorityIndicatorSet = true;
1382             bData.userData.payloadStr = data.substring(3, numFields - 3);
1383        } catch (java.lang.NumberFormatException ex) {
1384             throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1385         } catch (java.lang.IndexOutOfBoundsException ex) {
1386             throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1387         }
1388     }
1389 
1390     /**
1391      * IS-91 Short Message decoding
1392      * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1393      * (For character encodings, see TIA/EIA/IS-91, Annex B)
1394      *
1395      * Protocol Summary: The user data payload may contain 1-14
1396      * characters, which are treated as normal text user data payload.
1397      * Note that the characters encoding is 6-bit packed.
1398      */
1399     private static void decodeIs91ShortMessage(BearerData bData)
1400         throws BitwiseInputStream.AccessException, CodingException
1401     {
1402         BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1403         int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1404         int numFields = bData.userData.numFields;
1405         // dataLen may be > 14 characters due to octet padding
1406         if ((numFields > 14) || (dataLen < numFields)) {
1407             throw new CodingException("IS-91 short message decoding failed");
1408         }
1409         StringBuffer strbuf = new StringBuffer(dataLen);
1410         for (int i = 0; i < numFields; i++) {
1411             strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1412         }
1413         bData.userData.payloadStr = strbuf.toString();
1414     }
1415 
1416     /**
1417      * IS-91 CLI message (callback number) decoding
1418      * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1419      *
1420      * Protocol Summary: The data payload may contain 1-32 digits,
1421      * encoded using standard 4-bit DTMF, which are treated as a
1422      * callback number.
1423      */
decodeIs91Cli(BearerData bData)1424     private static void decodeIs91Cli(BearerData bData) throws CodingException {
1425         BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1426         int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
1427         int numFields = bData.userData.numFields;
1428         if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1429             throw new CodingException("IS-91 voicemail status decoding failed");
1430         }
1431         CdmaSmsAddress addr = new CdmaSmsAddress();
1432         addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
1433         addr.origBytes = bData.userData.payload;
1434         addr.numberOfDigits = (byte)numFields;
1435         decodeSmsAddress(addr);
1436         bData.callbackNumber = addr;
1437     }
1438 
decodeIs91(BearerData bData)1439     private static void decodeIs91(BearerData bData)
1440         throws BitwiseInputStream.AccessException, CodingException
1441     {
1442         switch (bData.userData.msgType) {
1443         case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
1444             decodeIs91VoicemailStatus(bData);
1445             break;
1446         case UserData.IS91_MSG_TYPE_CLI:
1447             decodeIs91Cli(bData);
1448             break;
1449         case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
1450         case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
1451             decodeIs91ShortMessage(bData);
1452             break;
1453         default:
1454             throw new CodingException("unsupported IS-91 message type (" +
1455                     bData.userData.msgType + ")");
1456         }
1457     }
1458 
decodeReplyOption(BearerData bData, BitwiseInputStream inStream)1459     private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
1460         throws BitwiseInputStream.AccessException {
1461         final int EXPECTED_PARAM_SIZE = 1 * 8;
1462         boolean decodeSuccess = false;
1463         int paramBits = inStream.read(8) * 8;
1464         if (paramBits >= EXPECTED_PARAM_SIZE) {
1465             paramBits -= EXPECTED_PARAM_SIZE;
1466             decodeSuccess = true;
1467             bData.userAckReq     = (inStream.read(1) == 1);
1468             bData.deliveryAckReq = (inStream.read(1) == 1);
1469             bData.readAckReq     = (inStream.read(1) == 1);
1470             bData.reportReq      = (inStream.read(1) == 1);
1471             inStream.skip(4);
1472         }
1473         if ((!decodeSuccess) || (paramBits > 0)) {
1474             Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
1475                       (decodeSuccess ? "succeeded" : "failed") +
1476                       " (extra bits = " + paramBits + ")");
1477         }
1478         inStream.skip(paramBits);
1479         return decodeSuccess;
1480     }
1481 
decodeMsgCount(BearerData bData, BitwiseInputStream inStream)1482     private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
1483         throws BitwiseInputStream.AccessException {
1484         final int EXPECTED_PARAM_SIZE = 1 * 8;
1485         boolean decodeSuccess = false;
1486         int paramBits = inStream.read(8) * 8;
1487         if (paramBits >= EXPECTED_PARAM_SIZE) {
1488             paramBits -= EXPECTED_PARAM_SIZE;
1489             decodeSuccess = true;
1490             bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
1491         }
1492         if ((!decodeSuccess) || (paramBits > 0)) {
1493             Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
1494                       (decodeSuccess ? "succeeded" : "failed") +
1495                       " (extra bits = " + paramBits + ")");
1496         }
1497         inStream.skip(paramBits);
1498         return decodeSuccess;
1499     }
1500 
decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)1501     private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
1502         throws BitwiseInputStream.AccessException {
1503         final int EXPECTED_PARAM_SIZE = 2 * 8;
1504         boolean decodeSuccess = false;
1505         int paramBits = inStream.read(8) * 8;
1506         if (paramBits >= EXPECTED_PARAM_SIZE) {
1507             paramBits -= EXPECTED_PARAM_SIZE;
1508             decodeSuccess = true;
1509             bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
1510         }
1511         if ((!decodeSuccess) || (paramBits > 0)) {
1512             Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
1513                       (decodeSuccess ? "succeeded" : "failed") +
1514                       " (extra bits = " + paramBits + ")");
1515         }
1516         inStream.skip(paramBits);
1517         return decodeSuccess;
1518     }
1519 
decodeDtmfSmsAddress(byte[] rawData, int numFields)1520     private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
1521         throws CodingException
1522     {
1523         /* DTMF 4-bit digit encoding, defined in at
1524          * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
1525         StringBuffer strBuf = new StringBuffer(numFields);
1526         for (int i = 0; i < numFields; i++) {
1527             int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
1528             if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
1529             else if (val == 10) strBuf.append('0');
1530             else if (val == 11) strBuf.append('*');
1531             else if (val == 12) strBuf.append('#');
1532             else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
1533         }
1534         return strBuf.toString();
1535     }
1536 
decodeSmsAddress(CdmaSmsAddress addr)1537     private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
1538         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1539             try {
1540                 /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
1541                  * just 7-bit ASCII encoding, with the MSB being zero. */
1542                 addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
1543             } catch (java.io.UnsupportedEncodingException ex) {
1544                 throw new CodingException("invalid SMS address ASCII code");
1545             }
1546         } else {
1547             addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
1548         }
1549     }
1550 
decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)1551     private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
1552         throws BitwiseInputStream.AccessException, CodingException
1553     {
1554         final int EXPECTED_PARAM_SIZE = 1 * 8; //at least
1555         int paramBits = inStream.read(8) * 8;
1556         if (paramBits < EXPECTED_PARAM_SIZE) {
1557             inStream.skip(paramBits);
1558             return false;
1559         }
1560         CdmaSmsAddress addr = new CdmaSmsAddress();
1561         addr.digitMode = inStream.read(1);
1562         byte fieldBits = 4;
1563         byte consumedBits = 1;
1564         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1565             addr.ton = inStream.read(3);
1566             addr.numberPlan = inStream.read(4);
1567             fieldBits = 8;
1568             consumedBits += 7;
1569         }
1570         addr.numberOfDigits = inStream.read(8);
1571         consumedBits += 8;
1572         int remainingBits = paramBits - consumedBits;
1573         int dataBits = addr.numberOfDigits * fieldBits;
1574         int paddingBits = remainingBits - dataBits;
1575         if (remainingBits < dataBits) {
1576             throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
1577                                       "remainingBits + " + remainingBits + ", dataBits + " +
1578                                       dataBits + ", paddingBits + " + paddingBits + ")");
1579         }
1580         addr.origBytes = inStream.readByteArray(dataBits);
1581         inStream.skip(paddingBits);
1582         decodeSmsAddress(addr);
1583         bData.callbackNumber = addr;
1584         return true;
1585     }
1586 
decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)1587     private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
1588         throws BitwiseInputStream.AccessException {
1589         final int EXPECTED_PARAM_SIZE = 1 * 8;
1590         boolean decodeSuccess = false;
1591         int paramBits = inStream.read(8) * 8;
1592         if (paramBits >= EXPECTED_PARAM_SIZE) {
1593             paramBits -= EXPECTED_PARAM_SIZE;
1594             decodeSuccess = true;
1595             bData.errorClass = inStream.read(2);
1596             bData.messageStatus = inStream.read(6);
1597         }
1598         if ((!decodeSuccess) || (paramBits > 0)) {
1599             Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
1600                       (decodeSuccess ? "succeeded" : "failed") +
1601                       " (extra bits = " + paramBits + ")");
1602         }
1603         inStream.skip(paramBits);
1604         bData.messageStatusSet = decodeSuccess;
1605         return decodeSuccess;
1606     }
1607 
decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)1608     private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
1609         throws BitwiseInputStream.AccessException {
1610         final int EXPECTED_PARAM_SIZE = 6 * 8;
1611         boolean decodeSuccess = false;
1612         int paramBits = inStream.read(8) * 8;
1613         if (paramBits >= EXPECTED_PARAM_SIZE) {
1614             paramBits -= EXPECTED_PARAM_SIZE;
1615             decodeSuccess = true;
1616             bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1617         }
1618         if ((!decodeSuccess) || (paramBits > 0)) {
1619             Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
1620                       (decodeSuccess ? "succeeded" : "failed") +
1621                       " (extra bits = " + paramBits + ")");
1622         }
1623         inStream.skip(paramBits);
1624         return decodeSuccess;
1625     }
1626 
decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)1627     private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
1628         throws BitwiseInputStream.AccessException {
1629         final int EXPECTED_PARAM_SIZE = 6 * 8;
1630         boolean decodeSuccess = false;
1631         int paramBits = inStream.read(8) * 8;
1632         if (paramBits >= EXPECTED_PARAM_SIZE) {
1633             paramBits -= EXPECTED_PARAM_SIZE;
1634             decodeSuccess = true;
1635             bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1636         }
1637         if ((!decodeSuccess) || (paramBits > 0)) {
1638             Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
1639                       (decodeSuccess ? "succeeded" : "failed") +
1640                       " (extra bits = " + paramBits + ")");
1641         }
1642         inStream.skip(paramBits);
1643         return decodeSuccess;
1644     }
1645 
decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)1646     private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
1647         throws BitwiseInputStream.AccessException {
1648         final int EXPECTED_PARAM_SIZE = 6 * 8;
1649         boolean decodeSuccess = false;
1650         int paramBits = inStream.read(8) * 8;
1651         if (paramBits >= EXPECTED_PARAM_SIZE) {
1652             paramBits -= EXPECTED_PARAM_SIZE;
1653             decodeSuccess = true;
1654             bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
1655                     inStream.readByteArray(6 * 8));
1656         }
1657         if ((!decodeSuccess) || (paramBits > 0)) {
1658             Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
1659                       (decodeSuccess ? "succeeded" : "failed") +
1660                       " (extra bits = " + paramBits + ")");
1661         }
1662         inStream.skip(paramBits);
1663         return decodeSuccess;
1664     }
1665 
decodeValidityRel(BearerData bData, BitwiseInputStream inStream)1666     private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
1667         throws BitwiseInputStream.AccessException {
1668         final int EXPECTED_PARAM_SIZE = 1 * 8;
1669         boolean decodeSuccess = false;
1670         int paramBits = inStream.read(8) * 8;
1671         if (paramBits >= EXPECTED_PARAM_SIZE) {
1672             paramBits -= EXPECTED_PARAM_SIZE;
1673             decodeSuccess = true;
1674             bData.deferredDeliveryTimeRelative = inStream.read(8);
1675         }
1676         if ((!decodeSuccess) || (paramBits > 0)) {
1677             Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
1678                       (decodeSuccess ? "succeeded" : "failed") +
1679                       " (extra bits = " + paramBits + ")");
1680         }
1681         inStream.skip(paramBits);
1682         bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
1683         return decodeSuccess;
1684     }
1685 
decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)1686     private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
1687         throws BitwiseInputStream.AccessException {
1688         final int EXPECTED_PARAM_SIZE = 1 * 8;
1689         boolean decodeSuccess = false;
1690         int paramBits = inStream.read(8) * 8;
1691         if (paramBits >= EXPECTED_PARAM_SIZE) {
1692             paramBits -= EXPECTED_PARAM_SIZE;
1693             decodeSuccess = true;
1694             bData.validityPeriodRelative = inStream.read(8);
1695         }
1696         if ((!decodeSuccess) || (paramBits > 0)) {
1697             Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
1698                       (decodeSuccess ? "succeeded" : "failed") +
1699                       " (extra bits = " + paramBits + ")");
1700         }
1701         inStream.skip(paramBits);
1702         bData.validityPeriodRelativeSet = decodeSuccess;
1703         return decodeSuccess;
1704     }
1705 
decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)1706     private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
1707         throws BitwiseInputStream.AccessException {
1708         final int EXPECTED_PARAM_SIZE = 1 * 8;
1709         boolean decodeSuccess = false;
1710         int paramBits = inStream.read(8) * 8;
1711         if (paramBits >= EXPECTED_PARAM_SIZE) {
1712             paramBits -= EXPECTED_PARAM_SIZE;
1713             decodeSuccess = true;
1714             bData.privacy = inStream.read(2);
1715             inStream.skip(6);
1716         }
1717         if ((!decodeSuccess) || (paramBits > 0)) {
1718             Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
1719                       (decodeSuccess ? "succeeded" : "failed") +
1720                       " (extra bits = " + paramBits + ")");
1721         }
1722         inStream.skip(paramBits);
1723         bData.privacyIndicatorSet = decodeSuccess;
1724         return decodeSuccess;
1725     }
1726 
decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)1727     private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
1728         throws BitwiseInputStream.AccessException {
1729         final int EXPECTED_PARAM_SIZE = 1 * 8;
1730         boolean decodeSuccess = false;
1731         int paramBits = inStream.read(8) * 8;
1732         if (paramBits >= EXPECTED_PARAM_SIZE) {
1733             paramBits -= EXPECTED_PARAM_SIZE;
1734             decodeSuccess = true;
1735             bData.language = inStream.read(8);
1736         }
1737         if ((!decodeSuccess) || (paramBits > 0)) {
1738             Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
1739                       (decodeSuccess ? "succeeded" : "failed") +
1740                       " (extra bits = " + paramBits + ")");
1741         }
1742         inStream.skip(paramBits);
1743         bData.languageIndicatorSet = decodeSuccess;
1744         return decodeSuccess;
1745     }
1746 
decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)1747     private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
1748         throws BitwiseInputStream.AccessException {
1749         final int EXPECTED_PARAM_SIZE = 1 * 8;
1750         boolean decodeSuccess = false;
1751         int paramBits = inStream.read(8) * 8;
1752         if (paramBits >= EXPECTED_PARAM_SIZE) {
1753             paramBits -= EXPECTED_PARAM_SIZE;
1754             decodeSuccess = true;
1755             bData.displayMode = inStream.read(2);
1756             inStream.skip(6);
1757         }
1758         if ((!decodeSuccess) || (paramBits > 0)) {
1759             Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
1760                       (decodeSuccess ? "succeeded" : "failed") +
1761                       " (extra bits = " + paramBits + ")");
1762         }
1763         inStream.skip(paramBits);
1764         bData.displayModeSet = decodeSuccess;
1765         return decodeSuccess;
1766     }
1767 
decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)1768     private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
1769         throws BitwiseInputStream.AccessException {
1770         final int EXPECTED_PARAM_SIZE = 1 * 8;
1771         boolean decodeSuccess = false;
1772         int paramBits = inStream.read(8) * 8;
1773         if (paramBits >= EXPECTED_PARAM_SIZE) {
1774             paramBits -= EXPECTED_PARAM_SIZE;
1775             decodeSuccess = true;
1776             bData.priority = inStream.read(2);
1777             inStream.skip(6);
1778         }
1779         if ((!decodeSuccess) || (paramBits > 0)) {
1780             Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
1781                       (decodeSuccess ? "succeeded" : "failed") +
1782                       " (extra bits = " + paramBits + ")");
1783         }
1784         inStream.skip(paramBits);
1785         bData.priorityIndicatorSet = decodeSuccess;
1786         return decodeSuccess;
1787     }
1788 
decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)1789     private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
1790         throws BitwiseInputStream.AccessException {
1791         final int EXPECTED_PARAM_SIZE = 1 * 8;
1792         boolean decodeSuccess = false;
1793         int paramBits = inStream.read(8) * 8;
1794         if (paramBits >= EXPECTED_PARAM_SIZE) {
1795             paramBits -= EXPECTED_PARAM_SIZE;
1796             decodeSuccess = true;
1797             bData.alert = inStream.read(2);
1798             inStream.skip(6);
1799         }
1800         if ((!decodeSuccess) || (paramBits > 0)) {
1801             Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
1802                       (decodeSuccess ? "succeeded" : "failed") +
1803                       " (extra bits = " + paramBits + ")");
1804         }
1805         inStream.skip(paramBits);
1806         bData.alertIndicatorSet = decodeSuccess;
1807         return decodeSuccess;
1808     }
1809 
decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)1810     private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
1811         throws BitwiseInputStream.AccessException {
1812         final int EXPECTED_PARAM_SIZE = 1 * 8;
1813         boolean decodeSuccess = false;
1814         int paramBits = inStream.read(8) * 8;
1815         if (paramBits >= EXPECTED_PARAM_SIZE) {
1816             paramBits -= EXPECTED_PARAM_SIZE;
1817             decodeSuccess = true;
1818             bData.userResponseCode = inStream.read(8);
1819         }
1820         if ((!decodeSuccess) || (paramBits > 0)) {
1821             Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
1822                       (decodeSuccess ? "succeeded" : "failed") +
1823                       " (extra bits = " + paramBits + ")");
1824         }
1825         inStream.skip(paramBits);
1826         bData.userResponseCodeSet = decodeSuccess;
1827         return decodeSuccess;
1828     }
1829 
decodeServiceCategoryProgramData(BearerData bData, BitwiseInputStream inStream)1830     private static boolean decodeServiceCategoryProgramData(BearerData bData,
1831             BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException
1832     {
1833         if (inStream.available() < 13) {
1834             throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
1835                     + inStream.available() + " bits available");
1836         }
1837 
1838         int paramBits = inStream.read(8) * 8;
1839         int msgEncoding = inStream.read(5);
1840         paramBits -= 5;
1841 
1842         if (inStream.available() < paramBits) {
1843             throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
1844                     + inStream.available() + " bits available (" + paramBits + " bits expected)");
1845         }
1846 
1847         ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();
1848 
1849         final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
1850         boolean decodeSuccess = false;
1851         while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
1852             int operation = inStream.read(4);
1853             int category = (inStream.read(8) << 8) | inStream.read(8);
1854             int language = inStream.read(8);
1855             int maxMessages = inStream.read(8);
1856             int alertOption = inStream.read(4);
1857             int numFields = inStream.read(8);
1858             paramBits -= CATEGORY_FIELD_MIN_SIZE;
1859 
1860             int textBits = getBitsForNumFields(msgEncoding, numFields);
1861             if (paramBits < textBits) {
1862                 throw new CodingException("category name is " + textBits + " bits in length,"
1863                         + " but there are only " + paramBits + " bits available");
1864             }
1865 
1866             UserData userData = new UserData();
1867             userData.msgEncoding = msgEncoding;
1868             userData.msgEncodingSet = true;
1869             userData.numFields = numFields;
1870             userData.payload = inStream.readByteArray(textBits);
1871             paramBits -= textBits;
1872 
1873             decodeUserDataPayload(userData, false);
1874             String categoryName = userData.payloadStr;
1875             CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
1876                     language, maxMessages, alertOption, categoryName);
1877             programDataList.add(programData);
1878 
1879             decodeSuccess = true;
1880         }
1881 
1882         if ((!decodeSuccess) || (paramBits > 0)) {
1883             Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
1884                       (decodeSuccess ? "succeeded" : "failed") +
1885                       " (extra bits = " + paramBits + ')');
1886         }
1887 
1888         inStream.skip(paramBits);
1889         bData.serviceCategoryProgramData = programDataList;
1890         return decodeSuccess;
1891     }
1892 
serviceCategoryToCmasMessageClass(int serviceCategory)1893     private static int serviceCategoryToCmasMessageClass(int serviceCategory) {
1894         switch (serviceCategory) {
1895             case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
1896                 return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
1897 
1898             case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
1899                 return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
1900 
1901             case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
1902                 return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
1903 
1904             case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
1905                 return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
1906 
1907             case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
1908                 return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
1909 
1910             default:
1911                 return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
1912         }
1913     }
1914 
1915     /**
1916      * Calculates the number of bits to read for the specified number of encoded characters.
1917      * @param msgEncoding the message encoding to use
1918      * @param numFields the number of characters to read. For Shift-JIS and Korean encodings,
1919      *  this is the number of bytes to read.
1920      * @return the number of bits to read from the stream
1921      * @throws CodingException if the specified encoding is not supported
1922      */
1923     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getBitsForNumFields(int msgEncoding, int numFields)1924     private static int getBitsForNumFields(int msgEncoding, int numFields)
1925             throws CodingException {
1926         switch (msgEncoding) {
1927             case UserData.ENCODING_OCTET:
1928             case UserData.ENCODING_SHIFT_JIS:
1929             case UserData.ENCODING_KOREAN:
1930             case UserData.ENCODING_LATIN:
1931             case UserData.ENCODING_LATIN_HEBREW:
1932                 return numFields * 8;
1933 
1934             case UserData.ENCODING_IA5:
1935             case UserData.ENCODING_7BIT_ASCII:
1936             case UserData.ENCODING_GSM_7BIT_ALPHABET:
1937                 return numFields * 7;
1938 
1939             case UserData.ENCODING_UNICODE_16:
1940                 return numFields * 16;
1941 
1942             default:
1943                 throw new CodingException("unsupported message encoding (" + msgEncoding + ')');
1944         }
1945     }
1946 
1947     /**
1948      * CMAS message decoding.
1949      * (See TIA-1149-0-1, CMAS over CDMA)
1950      *
1951      * @param serviceCategory is the service category from the SMS envelope
1952      */
decodeCmasUserData(BearerData bData, int serviceCategory)1953     private static void decodeCmasUserData(BearerData bData, int serviceCategory)
1954             throws BitwiseInputStream.AccessException, CodingException {
1955         BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1956         if (inStream.available() < 8) {
1957             throw new CodingException("emergency CB with no CMAE_protocol_version");
1958         }
1959         int protocolVersion = inStream.read(8);
1960         if (protocolVersion != 0) {
1961             throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
1962         }
1963 
1964         int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
1965         int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
1966         int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
1967         int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
1968         int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
1969         int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
1970 
1971         while (inStream.available() >= 16) {
1972             int recordType = inStream.read(8);
1973             int recordLen = inStream.read(8);
1974             switch (recordType) {
1975                 case 0:     // Type 0 elements (Alert text)
1976                     UserData alertUserData = new UserData();
1977                     alertUserData.msgEncoding = inStream.read(5);
1978                     alertUserData.msgEncodingSet = true;
1979                     alertUserData.msgType = 0;
1980 
1981                     int numFields;                          // number of chars to decode
1982                     switch (alertUserData.msgEncoding) {
1983                         case UserData.ENCODING_OCTET:
1984                         case UserData.ENCODING_LATIN:
1985                             numFields = recordLen - 1;      // subtract 1 byte for encoding
1986                             break;
1987 
1988                         case UserData.ENCODING_IA5:
1989                         case UserData.ENCODING_7BIT_ASCII:
1990                         case UserData.ENCODING_GSM_7BIT_ALPHABET:
1991                             numFields = ((recordLen * 8) - 5) / 7;  // subtract 5 bits for encoding
1992                             break;
1993 
1994                         case UserData.ENCODING_UNICODE_16:
1995                             numFields = (recordLen - 1) / 2;
1996                             break;
1997 
1998                         default:
1999                             numFields = 0;      // unsupported encoding
2000                     }
2001 
2002                     alertUserData.numFields = numFields;
2003                     alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
2004                     decodeUserDataPayload(alertUserData, false);
2005                     bData.userData = alertUserData;
2006                     break;
2007 
2008                 case 1:     // Type 1 elements
2009                     category = inStream.read(8);
2010                     responseType = inStream.read(8);
2011                     severity = inStream.read(4);
2012                     urgency = inStream.read(4);
2013                     certainty = inStream.read(4);
2014                     inStream.skip(recordLen * 8 - 28);
2015                     break;
2016 
2017                 default:
2018                     Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
2019                     inStream.skip(recordLen * 8);
2020                     break;
2021             }
2022         }
2023 
2024         bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
2025                 urgency, certainty);
2026     }
2027 
2028     /**
2029      * Create BearerData object from serialized representation.
2030      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
2031      *
2032      * @param smsData byte array of raw encoded SMS bearer data.
2033      * @return an instance of BearerData.
2034      */
decode(byte[] smsData)2035     public static BearerData decode(byte[] smsData) {
2036         return decode(smsData, 0);
2037     }
2038 
isCmasAlertCategory(int category)2039     private static boolean isCmasAlertCategory(int category) {
2040         return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
2041                 && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
2042     }
2043 
2044     /**
2045      * Create BearerData object from serialized representation.
2046      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
2047      *
2048      * @param smsData byte array of raw encoded SMS bearer data.
2049      * @param serviceCategory the envelope service category (for CMAS alert handling)
2050      * @return an instance of BearerData.
2051      */
decode(byte[] smsData, int serviceCategory)2052     public static BearerData decode(byte[] smsData, int serviceCategory) {
2053         try {
2054             BitwiseInputStream inStream = new BitwiseInputStream(smsData);
2055             BearerData bData = new BearerData();
2056             int foundSubparamMask = 0;
2057             while (inStream.available() > 0) {
2058                 int subparamId = inStream.read(8);
2059                 int subparamIdBit = 1 << subparamId;
2060                 // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8)
2061                 // as 32th bit is the max bit in int.
2062                 // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers:
2063                 // last defined subparam ID is 23 (00010111 = 0x17 = 23).
2064                 // Only do duplicate subparam ID check if subparam is within defined value as
2065                 // reserved subparams are just skipped.
2066                 if ((foundSubparamMask & subparamIdBit) != 0 &&
2067                         (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
2068                         subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
2069                     throw new CodingException("illegal duplicate subparameter (" +
2070                                               subparamId + ")");
2071                 }
2072                 boolean decodeSuccess;
2073                 switch (subparamId) {
2074                 case SUBPARAM_MESSAGE_IDENTIFIER:
2075                     decodeSuccess = decodeMessageId(bData, inStream);
2076                     break;
2077                 case SUBPARAM_USER_DATA:
2078                     decodeSuccess = decodeUserData(bData, inStream);
2079                     break;
2080                 case SUBPARAM_USER_RESPONSE_CODE:
2081                     decodeSuccess = decodeUserResponseCode(bData, inStream);
2082                     break;
2083                 case SUBPARAM_REPLY_OPTION:
2084                     decodeSuccess = decodeReplyOption(bData, inStream);
2085                     break;
2086                 case SUBPARAM_NUMBER_OF_MESSAGES:
2087                     decodeSuccess = decodeMsgCount(bData, inStream);
2088                     break;
2089                 case SUBPARAM_CALLBACK_NUMBER:
2090                     decodeSuccess = decodeCallbackNumber(bData, inStream);
2091                     break;
2092                 case SUBPARAM_MESSAGE_STATUS:
2093                     decodeSuccess = decodeMsgStatus(bData, inStream);
2094                     break;
2095                 case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
2096                     decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
2097                     break;
2098                 case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
2099                     decodeSuccess = decodeValidityAbs(bData, inStream);
2100                     break;
2101                 case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
2102                     decodeSuccess = decodeValidityRel(bData, inStream);
2103                     break;
2104                 case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
2105                     decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
2106                     break;
2107                 case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
2108                     decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
2109                     break;
2110                 case SUBPARAM_PRIVACY_INDICATOR:
2111                     decodeSuccess = decodePrivacyIndicator(bData, inStream);
2112                     break;
2113                 case SUBPARAM_LANGUAGE_INDICATOR:
2114                     decodeSuccess = decodeLanguageIndicator(bData, inStream);
2115                     break;
2116                 case SUBPARAM_MESSAGE_DISPLAY_MODE:
2117                     decodeSuccess = decodeDisplayMode(bData, inStream);
2118                     break;
2119                 case SUBPARAM_PRIORITY_INDICATOR:
2120                     decodeSuccess = decodePriorityIndicator(bData, inStream);
2121                     break;
2122                 case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
2123                     decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
2124                     break;
2125                 case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
2126                     decodeSuccess = decodeDepositIndex(bData, inStream);
2127                     break;
2128                 case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
2129                     decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
2130                     break;
2131                 default:
2132                     decodeSuccess = decodeReserved(bData, inStream, subparamId);
2133                 }
2134                 if (decodeSuccess &&
2135                         (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
2136                         subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
2137                     foundSubparamMask |= subparamIdBit;
2138                 }
2139             }
2140             if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
2141                 throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
2142             }
2143             if (bData.userData != null) {
2144                 if (isCmasAlertCategory(serviceCategory)) {
2145                     decodeCmasUserData(bData, serviceCategory);
2146                 } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
2147                     if ((foundSubparamMask ^
2148                              (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
2149                              (1 << SUBPARAM_USER_DATA))
2150                             != 0) {
2151                         Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
2152                               foundSubparamMask + ")");
2153                     }
2154                     decodeIs91(bData);
2155                 } else {
2156                     decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
2157                 }
2158             }
2159             return bData;
2160         } catch (BitwiseInputStream.AccessException ex) {
2161             Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
2162         } catch (CodingException ex) {
2163             Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
2164         }
2165         return null;
2166     }
2167 }
2168