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