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