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
17package com.android.internal.telephony.cdma.sms;
18
19import static android.telephony.SmsMessage.ENCODING_16BIT;
20import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
21import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
22
23import android.util.Log;
24
25import android.telephony.SmsMessage;
26
27import android.text.format.Time;
28
29import com.android.internal.telephony.IccUtils;
30import com.android.internal.telephony.GsmAlphabet;
31import com.android.internal.telephony.SmsHeader;
32import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
33
34import com.android.internal.util.BitwiseInputStream;
35import com.android.internal.util.BitwiseOutputStream;
36
37import android.content.res.Resources;
38
39
40
41/**
42 * An object to encode and decode CDMA SMS bearer data.
43 */
44public final class BearerData {
45    private final static String LOG_TAG = "SMS";
46
47    /**
48     * Bearer Data Subparameter Indentifiers
49     * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
50     * NOTE: Commented subparameter types are not implemented.
51     */
52    private final static byte SUBPARAM_MESSAGE_IDENTIFIER               = 0x00;
53    private final static byte SUBPARAM_USER_DATA                        = 0x01;
54    private final static byte SUBPARAM_USER_REPONSE_CODE                = 0x02;
55    private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP        = 0x03;
56    private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
57    private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
58    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
59    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
60    private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
61    private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
62    private final static byte SUBPARAM_REPLY_OPTION                     = 0x0A;
63    private final static byte SUBPARAM_NUMBER_OF_MESSAGES               = 0x0B;
64    private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY        = 0x0C;
65    private final static byte SUBPARAM_LANGUAGE_INDICATOR               = 0x0D;
66    private final static byte SUBPARAM_CALLBACK_NUMBER                  = 0x0E;
67    private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
68    //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA      = 0x10;
69    private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
70    //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
71    //private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
72    private final static byte SUBPARAM_MESSAGE_STATUS                   = 0x14;
73    //private final static byte SUBPARAM_TP_FAILURE_CAUSE                 = 0x15;
74    //private final static byte SUBPARAM_ENHANCED_VMN                     = 0x16;
75    //private final static byte SUBPARAM_ENHANCED_VMN_ACK                 = 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
233        public TimeStamp() {
234            super(Time.TIMEZONE_UTC);
235        }
236
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
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    private static class CodingException extends Exception {
342        public CodingException(String s) {
343            super(s);
344        }
345    }
346
347    @Override
348    public String toString() {
349        StringBuilder builder = new StringBuilder();
350        builder.append("BearerData ");
351        builder.append("{ messageType=" + messageType);
352        builder.append(", messageId=" + (int)messageId);
353        builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
354        builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
355        builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
356        builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
357        builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
358        builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
359        builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
360        builder.append(", msgCenterTimeStamp=" +
361                ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset"));
362        builder.append(", validityPeriodAbsolute=" +
363                ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset"));
364        builder.append(", validityPeriodRelative=" +
365                ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset"));
366        builder.append(", deferredDeliveryTimeAbsolute=" +
367                ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset"));
368        builder.append(", deferredDeliveryTimeRelative=" +
369                ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset"));
370        builder.append(", userAckReq=" + userAckReq);
371        builder.append(", deliveryAckReq=" + deliveryAckReq);
372        builder.append(", readAckReq=" + readAckReq);
373        builder.append(", reportReq=" + reportReq);
374        builder.append(", numberOfMessages=" + numberOfMessages);
375        builder.append(", callbackNumber=" + callbackNumber);
376        builder.append(", depositIndex=" + depositIndex);
377        builder.append(", hasUserDataHeader=" + hasUserDataHeader);
378        builder.append(", userData=" + userData);
379        builder.append(" }");
380        return builder.toString();
381    }
382
383    private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream)
384        throws BitwiseOutputStream.AccessException
385    {
386        outStream.write(8, 3);
387        outStream.write(4, bData.messageType);
388        outStream.write(8, bData.messageId >> 8);
389        outStream.write(8, bData.messageId);
390        outStream.write(1, bData.hasUserDataHeader ? 1 : 0);
391        outStream.skip(3);
392    }
393
394    private static int countAsciiSeptets(CharSequence msg, boolean force) {
395        int msgLen = msg.length();
396        if (force) return msgLen;
397        for (int i = 0; i < msgLen; i++) {
398            if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
399                return -1;
400            }
401        }
402        return msgLen;
403    }
404
405    /**
406     * Calculate the message text encoding length, fragmentation, and other details.
407     *
408     * @param msg message text
409     * @param force7BitEncoding ignore (but still count) illegal characters if true
410     * @return septet count, or -1 on failure
411     */
412    public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
413            boolean force7BitEncoding) {
414        TextEncodingDetails ted;
415        int septets = countAsciiSeptets(msg, force7BitEncoding);
416        if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) {
417            ted = new TextEncodingDetails();
418            ted.msgCount = 1;
419            ted.codeUnitCount = septets;
420            ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets;
421            ted.codeUnitSize = SmsMessage.ENCODING_7BIT;
422        } else {
423            ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
424                    msg, force7BitEncoding);
425            if (ted.msgCount == 1 && ted.codeUnitSize == SmsMessage.ENCODING_7BIT) {
426                // We don't support single-segment EMS, so calculate for 16-bit
427                // TODO: Consider supporting single-segment EMS
428                ted.codeUnitCount = msg.length();
429                int octets = ted.codeUnitCount * 2;
430                if (octets > MAX_USER_DATA_BYTES) {
431                    ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
432                            MAX_USER_DATA_BYTES_WITH_HEADER;
433                    ted.codeUnitsRemaining = ((ted.msgCount *
434                            MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
435                } else {
436                    ted.msgCount = 1;
437                    ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
438                }
439                ted.codeUnitSize = ENCODING_16BIT;
440            }
441        }
442        return ted;
443    }
444
445    private static byte[] encode7bitAscii(String msg, boolean force)
446        throws CodingException
447    {
448        try {
449            BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
450            int msgLen = msg.length();
451            for (int i = 0; i < msgLen; i++) {
452                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
453                if (charCode == -1) {
454                    if (force) {
455                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
456                    } else {
457                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
458                    }
459                } else {
460                    outStream.write(7, charCode);
461                }
462            }
463            return outStream.toByteArray();
464        } catch (BitwiseOutputStream.AccessException ex) {
465            throw new CodingException("7bit ASCII encode failed: " + ex);
466        }
467    }
468
469    private static byte[] encodeUtf16(String msg)
470        throws CodingException
471    {
472        try {
473            return msg.getBytes("utf-16be");
474        } catch (java.io.UnsupportedEncodingException ex) {
475            throw new CodingException("UTF-16 encode failed: " + ex);
476        }
477    }
478
479    private static class Gsm7bitCodingResult {
480        int septets;
481        byte[] data;
482    }
483
484    private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force)
485        throws CodingException
486    {
487        try {
488            /*
489             * TODO(cleanup): It would be nice if GsmAlphabet provided
490             * an option to produce just the data without prepending
491             * the septet count, as this function is really just a
492             * wrapper to strip that off.  Not to mention that the
493             * septet count is generally known prior to invocation of
494             * the encoder.  Note that it cannot be derived from the
495             * resulting array length, since that cannot distinguish
496             * if the last contains either 1 or 8 valid bits.
497             *
498             * TODO(cleanup): The BitwiseXStreams could also be
499             * extended with byte-wise reversed endianness read/write
500             * routines to allow a corresponding implementation of
501             * stringToGsm7BitPacked, and potentially directly support
502             * access to the main bitwise stream from encode/decode.
503             */
504            byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
505            Gsm7bitCodingResult result = new Gsm7bitCodingResult();
506            result.data = new byte[fullData.length - 1];
507            System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
508            result.septets = fullData[0] & 0x00FF;
509            return result;
510        } catch (com.android.internal.telephony.EncodeException ex) {
511            throw new CodingException("7bit GSM encode failed: " + ex);
512        }
513    }
514
515    private static void encode7bitEms(UserData uData, byte[] udhData, boolean force)
516        throws CodingException
517    {
518        int udhBytes = udhData.length + 1;  // Add length octet.
519        int udhSeptets = ((udhBytes * 8) + 6) / 7;
520        Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
521        uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
522        uData.msgEncodingSet = true;
523        uData.numFields = gcr.septets;
524        uData.payload = gcr.data;
525        uData.payload[0] = (byte)udhData.length;
526        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
527    }
528
529    private static void encode16bitEms(UserData uData, byte[] udhData)
530        throws CodingException
531    {
532        byte[] payload = encodeUtf16(uData.payloadStr);
533        int udhBytes = udhData.length + 1;  // Add length octet.
534        int udhCodeUnits = (udhBytes + 1) / 2;
535        int udhPadding = udhBytes % 2;
536        int payloadCodeUnits = payload.length / 2;
537        uData.msgEncoding = UserData.ENCODING_UNICODE_16;
538        uData.msgEncodingSet = true;
539        uData.numFields = udhCodeUnits + payloadCodeUnits;
540        uData.payload = new byte[uData.numFields * 2];
541        uData.payload[0] = (byte)udhData.length;
542        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
543        System.arraycopy(payload, 0, uData.payload, udhBytes + udhPadding, payload.length);
544    }
545
546    private static void encodeEmsUserDataPayload(UserData uData)
547        throws CodingException
548    {
549        byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
550        if (uData.msgEncodingSet) {
551            if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
552                encode7bitEms(uData, headerData, true);
553            } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
554                encode16bitEms(uData, headerData);
555            } else {
556                throw new CodingException("unsupported EMS user data encoding (" +
557                                          uData.msgEncoding + ")");
558            }
559        } else {
560            try {
561                encode7bitEms(uData, headerData, false);
562            } catch (CodingException ex) {
563                encode16bitEms(uData, headerData);
564            }
565        }
566    }
567
568    private static void encodeUserDataPayload(UserData uData)
569        throws CodingException
570    {
571        if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
572            Log.e(LOG_TAG, "user data with null payloadStr");
573            uData.payloadStr = "";
574        }
575
576        if (uData.userDataHeader != null) {
577            encodeEmsUserDataPayload(uData);
578            return;
579        }
580
581        if (uData.msgEncodingSet) {
582            if (uData.msgEncoding == UserData.ENCODING_OCTET) {
583                if (uData.payload == null) {
584                    Log.e(LOG_TAG, "user data with octet encoding but null payload");
585                    uData.payload = new byte[0];
586                    uData.numFields = 0;
587                } else {
588                    uData.payload = uData.payload;
589                    uData.numFields = uData.payload.length;
590                }
591            } else {
592                if (uData.payloadStr == null) {
593                    Log.e(LOG_TAG, "non-octet user data with null payloadStr");
594                    uData.payloadStr = "";
595                }
596                if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
597                    Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
598                    uData.payload = gcr.data;
599                    uData.numFields = gcr.septets;
600                } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
601                    uData.payload = encode7bitAscii(uData.payloadStr, true);
602                    uData.numFields = uData.payloadStr.length();
603                } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
604                    uData.payload = encodeUtf16(uData.payloadStr);
605                    uData.numFields = uData.payloadStr.length();
606                } else {
607                    throw new CodingException("unsupported user data encoding (" +
608                                              uData.msgEncoding + ")");
609                }
610            }
611        } else {
612            try {
613                uData.payload = encode7bitAscii(uData.payloadStr, false);
614                uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
615            } catch (CodingException ex) {
616                uData.payload = encodeUtf16(uData.payloadStr);
617                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
618            }
619            uData.numFields = uData.payloadStr.length();
620            uData.msgEncodingSet = true;
621        }
622    }
623
624    private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
625        throws BitwiseOutputStream.AccessException, CodingException
626    {
627        /*
628         * TODO(cleanup): Do we really need to set userData.payload as
629         * a side effect of encoding?  If not, we could avoid data
630         * copies by passing outStream directly.
631         */
632        encodeUserDataPayload(bData.userData);
633        bData.hasUserDataHeader = bData.userData.userDataHeader != null;
634
635        if (bData.userData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) {
636            throw new CodingException("encoded user data too large (" +
637                                      bData.userData.payload.length +
638                                      " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
639        }
640
641        /*
642         * TODO(cleanup): figure out what the right answer is WRT paddingBits field
643         *
644         *   userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
645         *   userData.paddingBits = 0; // XXX this seems better, but why?
646         *
647         */
648        int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
649        int paramBits = dataBits + 13;
650        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
651            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
652            paramBits += 8;
653        }
654        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
655        int paddingBits = (paramBytes * 8) - paramBits;
656        outStream.write(8, paramBytes);
657        outStream.write(5, bData.userData.msgEncoding);
658        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
659            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
660            outStream.write(8, bData.userData.msgType);
661        }
662        outStream.write(8, bData.userData.numFields);
663        outStream.writeByteArray(dataBits, bData.userData.payload);
664        if (paddingBits > 0) outStream.write(paddingBits, 0);
665    }
666
667    private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)
668        throws BitwiseOutputStream.AccessException
669    {
670        outStream.write(8, 1);
671        outStream.write(1, bData.userAckReq     ? 1 : 0);
672        outStream.write(1, bData.deliveryAckReq ? 1 : 0);
673        outStream.write(1, bData.readAckReq     ? 1 : 0);
674        outStream.write(1, bData.reportReq      ? 1 : 0);
675        outStream.write(4, 0);
676    }
677
678    private static byte[] encodeDtmfSmsAddress(String address) {
679        int digits = address.length();
680        int dataBits = digits * 4;
681        int dataBytes = (dataBits / 8);
682        dataBytes += (dataBits % 8) > 0 ? 1 : 0;
683        byte[] rawData = new byte[dataBytes];
684        for (int i = 0; i < digits; i++) {
685            char c = address.charAt(i);
686            int val = 0;
687            if ((c >= '1') && (c <= '9')) val = c - '0';
688            else if (c == '0') val = 10;
689            else if (c == '*') val = 11;
690            else if (c == '#') val = 12;
691            else return null;
692            rawData[i / 2] |= val << (4 - ((i % 2) * 4));
693        }
694        return rawData;
695    }
696
697    /*
698     * TODO(cleanup): CdmaSmsAddress encoding should make use of
699     * CdmaSmsAddress.parse provided that DTMF encoding is unified,
700     * and the difference in 4bit vs 8bit is resolved.
701     */
702
703    private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
704        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
705            try {
706                addr.origBytes = addr.address.getBytes("US-ASCII");
707            } catch (java.io.UnsupportedEncodingException ex) {
708                throw new CodingException("invalid SMS address, cannot convert to ASCII");
709            }
710        } else {
711            addr.origBytes = encodeDtmfSmsAddress(addr.address);
712        }
713    }
714
715    private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)
716        throws BitwiseOutputStream.AccessException, CodingException
717    {
718        CdmaSmsAddress addr = bData.callbackNumber;
719        encodeCdmaSmsAddress(addr);
720        int paramBits = 9;
721        int dataBits = 0;
722        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
723            paramBits += 7;
724            dataBits = addr.numberOfDigits * 8;
725        } else {
726            dataBits = addr.numberOfDigits * 4;
727        }
728        paramBits += dataBits;
729        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
730        int paddingBits = (paramBytes * 8) - paramBits;
731        outStream.write(8, paramBytes);
732        outStream.write(1, addr.digitMode);
733        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
734            outStream.write(3, addr.ton);
735            outStream.write(4, addr.numberPlan);
736        }
737        outStream.write(8, addr.numberOfDigits);
738        outStream.writeByteArray(dataBits, addr.origBytes);
739        if (paddingBits > 0) outStream.write(paddingBits, 0);
740    }
741
742    private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)
743        throws BitwiseOutputStream.AccessException
744    {
745        outStream.write(8, 1);
746        outStream.write(2, bData.errorClass);
747        outStream.write(6, bData.messageStatus);
748    }
749
750    private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)
751        throws BitwiseOutputStream.AccessException
752    {
753        outStream.write(8, 1);
754        outStream.write(8, bData.numberOfMessages);
755    }
756
757    private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)
758        throws BitwiseOutputStream.AccessException
759    {
760        outStream.write(8, 1);
761        outStream.write(8, bData.validityPeriodRelative);
762    }
763
764    private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
765        throws BitwiseOutputStream.AccessException
766    {
767        outStream.write(8, 1);
768        outStream.write(2, bData.privacy);
769        outStream.skip(6);
770    }
771
772    private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)
773        throws BitwiseOutputStream.AccessException
774    {
775        outStream.write(8, 1);
776        outStream.write(8, bData.language);
777    }
778
779    private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)
780        throws BitwiseOutputStream.AccessException
781    {
782        outStream.write(8, 1);
783        outStream.write(2, bData.displayMode);
784        outStream.skip(6);
785    }
786
787    private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)
788        throws BitwiseOutputStream.AccessException
789    {
790        outStream.write(8, 1);
791        outStream.write(2, bData.priority);
792        outStream.skip(6);
793    }
794
795    private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)
796        throws BitwiseOutputStream.AccessException
797    {
798        outStream.write(8, 1);
799        outStream.write(2, bData.alert);
800        outStream.skip(6);
801    }
802
803    /**
804     * Create serialized representation for BearerData object.
805     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
806     *
807     * @param bData an instance of BearerData.
808     * @return byte array of raw encoded SMS bearer data.
809     */
810    public static byte[] encode(BearerData bData) {
811        bData.hasUserDataHeader = ((bData.userData != null) &&
812                (bData.userData.userDataHeader != null));
813        try {
814            BitwiseOutputStream outStream = new BitwiseOutputStream(200);
815            outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
816            encodeMessageId(bData, outStream);
817            if (bData.userData != null) {
818                outStream.write(8, SUBPARAM_USER_DATA);
819                encodeUserData(bData, outStream);
820            }
821            if (bData.callbackNumber != null) {
822                outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
823                encodeCallbackNumber(bData, outStream);
824            }
825            if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
826                outStream.write(8, SUBPARAM_REPLY_OPTION);
827                encodeReplyOption(bData, outStream);
828            }
829            if (bData.numberOfMessages != 0) {
830                outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
831                encodeMsgCount(bData, outStream);
832            }
833            if (bData.validityPeriodRelativeSet) {
834                outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
835                encodeValidityPeriodRel(bData, outStream);
836            }
837            if (bData.privacyIndicatorSet) {
838                outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
839                encodePrivacyIndicator(bData, outStream);
840            }
841            if (bData.languageIndicatorSet) {
842                outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
843                encodeLanguageIndicator(bData, outStream);
844            }
845            if (bData.displayModeSet) {
846                outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
847                encodeDisplayMode(bData, outStream);
848            }
849            if (bData.priorityIndicatorSet) {
850                outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
851                encodePriorityIndicator(bData, outStream);
852            }
853            if (bData.alertIndicatorSet) {
854                outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
855                encodeMsgDeliveryAlert(bData, outStream);
856            }
857            if (bData.messageStatusSet) {
858                outStream.write(8, SUBPARAM_MESSAGE_STATUS);
859                encodeMsgStatus(bData, outStream);
860            }
861            return outStream.toByteArray();
862        } catch (BitwiseOutputStream.AccessException ex) {
863            Log.e(LOG_TAG, "BearerData encode failed: " + ex);
864        } catch (CodingException ex) {
865            Log.e(LOG_TAG, "BearerData encode failed: " + ex);
866        }
867        return null;
868   }
869
870    private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
871        throws BitwiseInputStream.AccessException, CodingException
872    {
873        final int EXPECTED_PARAM_SIZE = 3 * 8;
874        boolean decodeSuccess = false;
875        int paramBits = inStream.read(8) * 8;
876        if (paramBits >= EXPECTED_PARAM_SIZE) {
877            paramBits -= EXPECTED_PARAM_SIZE;
878            decodeSuccess = true;
879            bData.messageType = inStream.read(4);
880            bData.messageId = inStream.read(8) << 8;
881            bData.messageId |= inStream.read(8);
882            bData.hasUserDataHeader = (inStream.read(1) == 1);
883            inStream.skip(3);
884        }
885        if ((! decodeSuccess) || (paramBits > 0)) {
886            Log.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
887                      (decodeSuccess ? "succeeded" : "failed") +
888                      " (extra bits = " + paramBits + ")");
889        }
890        inStream.skip(paramBits);
891        return decodeSuccess;
892    }
893
894    private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
895        throws BitwiseInputStream.AccessException
896    {
897        int paramBits = inStream.read(8) * 8;
898        bData.userData = new UserData();
899        bData.userData.msgEncoding = inStream.read(5);
900        bData.userData.msgEncodingSet = true;
901        bData.userData.msgType = 0;
902        int consumedBits = 5;
903        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
904            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
905            bData.userData.msgType = inStream.read(8);
906            consumedBits += 8;
907        }
908        bData.userData.numFields = inStream.read(8);
909        consumedBits += 8;
910        int dataBits = paramBits - consumedBits;
911        bData.userData.payload = inStream.readByteArray(dataBits);
912        return true;
913    }
914
915    private static String decodeUtf8(byte[] data, int offset, int numFields)
916        throws CodingException
917    {
918        try {
919            return new String(data, offset, numFields, "UTF-8");
920        } catch (java.io.UnsupportedEncodingException ex) {
921            throw new CodingException("UTF-8 decode failed: " + ex);
922        }
923    }
924
925    private static String decodeUtf16(byte[] data, int offset, int numFields)
926        throws CodingException
927    {
928        // Start reading from the next 16-bit aligned boundary after offset.
929        int padding = offset % 2;
930        numFields -= (offset + padding) / 2;
931        try {
932            return new String(data, offset, numFields * 2, "utf-16be");
933        } catch (java.io.UnsupportedEncodingException ex) {
934            throw new CodingException("UTF-16 decode failed: " + ex);
935        }
936    }
937
938    private static String decode7bitAscii(byte[] data, int offset, int numFields)
939        throws CodingException
940    {
941        try {
942            offset *= 8;
943            StringBuffer strBuf = new StringBuffer(numFields);
944            BitwiseInputStream inStream = new BitwiseInputStream(data);
945            int wantedBits = (offset * 8) + (numFields * 7);
946            if (inStream.available() < wantedBits) {
947                throw new CodingException("insufficient data (wanted " + wantedBits +
948                                          " bits, but only have " + inStream.available() + ")");
949            }
950            inStream.skip(offset);
951            for (int i = 0; i < numFields; i++) {
952                int charCode = inStream.read(7);
953                if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
954                        (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
955                    strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
956                } else if (charCode == UserData.ASCII_NL_INDEX) {
957                    strBuf.append('\n');
958                } else if (charCode == UserData.ASCII_CR_INDEX) {
959                    strBuf.append('\r');
960                } else {
961                    /* For other charCodes, they are unprintable, and so simply use SPACE. */
962                    strBuf.append(' ');
963                }
964            }
965            return strBuf.toString();
966        } catch (BitwiseInputStream.AccessException ex) {
967            throw new CodingException("7bit ASCII decode failed: " + ex);
968        }
969    }
970
971    private static String decode7bitGsm(byte[] data, int offset, int numFields)
972        throws CodingException
973    {
974        // Start reading from the next 7-bit aligned boundary after offset.
975        int offsetBits = offset * 8;
976        int offsetSeptets = (offsetBits + 6) / 7;
977        numFields -= offsetSeptets;
978        int paddingBits = (offsetSeptets * 7) - offsetBits;
979        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
980                0, 0);
981        if (result == null) {
982            throw new CodingException("7bit GSM decoding failed");
983        }
984        return result;
985    }
986
987    private static String decodeLatin(byte[] data, int offset, int numFields)
988        throws CodingException
989    {
990        try {
991            return new String(data, offset, numFields - offset, "ISO-8859-1");
992        } catch (java.io.UnsupportedEncodingException ex) {
993            throw new CodingException("ISO-8859-1 decode failed: " + ex);
994        }
995    }
996
997    private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
998        throws CodingException
999    {
1000        int offset = 0;
1001        if (hasUserDataHeader) {
1002            int udhLen = userData.payload[0] & 0x00FF;
1003            offset += udhLen + 1;
1004            byte[] headerData = new byte[udhLen];
1005            System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
1006            userData.userDataHeader = SmsHeader.fromByteArray(headerData);
1007        }
1008        switch (userData.msgEncoding) {
1009        case UserData.ENCODING_OCTET:
1010            /*
1011            *  Octet decoding depends on the carrier service.
1012            */
1013            boolean decodingtypeUTF8 = Resources.getSystem()
1014                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
1015
1016            // Strip off any padding bytes, meaning any differences between the length of the
1017            // array and the target length specified by numFields.  This is to avoid any
1018            // confusion by code elsewhere that only considers the payload array length.
1019            byte[] payload = new byte[userData.numFields];
1020            int copyLen = userData.numFields < userData.payload.length
1021                    ? userData.numFields : userData.payload.length;
1022
1023            System.arraycopy(userData.payload, 0, payload, 0, copyLen);
1024            userData.payload = payload;
1025
1026            if (!decodingtypeUTF8) {
1027                // There are many devices in the market that send 8bit text sms (latin encoded) as
1028                // octet encoded.
1029                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1030            } else {
1031                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
1032            }
1033            break;
1034        case UserData.ENCODING_IA5:
1035        case UserData.ENCODING_7BIT_ASCII:
1036            userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
1037            break;
1038        case UserData.ENCODING_UNICODE_16:
1039            userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
1040            break;
1041        case UserData.ENCODING_GSM_7BIT_ALPHABET:
1042            userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
1043            break;
1044        case UserData.ENCODING_LATIN:
1045            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1046            break;
1047        default:
1048            throw new CodingException("unsupported user data encoding ("
1049                                      + userData.msgEncoding + ")");
1050        }
1051    }
1052
1053    /**
1054     * IS-91 Voice Mail message decoding
1055     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1056     * (For character encodings, see TIA/EIA/IS-91, Annex B)
1057     *
1058     * Protocol Summary: The user data payload may contain 3-14
1059     * characters.  The first two characters are parsed as a number
1060     * and indicate the number of voicemails.  The third character is
1061     * either a SPACE or '!' to indicate normal or urgent priority,
1062     * respectively.  Any following characters are treated as normal
1063     * text user data payload.
1064     *
1065     * Note that the characters encoding is 6-bit packed.
1066     */
1067    private static void decodeIs91VoicemailStatus(BearerData bData)
1068        throws BitwiseInputStream.AccessException, CodingException
1069    {
1070        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1071        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1072        int numFields = bData.userData.numFields;
1073        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1074            throw new CodingException("IS-91 voicemail status decoding failed");
1075        }
1076        try {
1077            StringBuffer strbuf = new StringBuffer(dataLen);
1078            while (inStream.available() >= 6) {
1079                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1080            }
1081            String data = strbuf.toString();
1082            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
1083            char prioCode = data.charAt(2);
1084            if (prioCode == ' ') {
1085                bData.priority = PRIORITY_NORMAL;
1086            } else if (prioCode == '!') {
1087                bData.priority = PRIORITY_URGENT;
1088            } else {
1089                throw new CodingException("IS-91 voicemail status decoding failed: " +
1090                        "illegal priority setting (" + prioCode + ")");
1091            }
1092            bData.priorityIndicatorSet = true;
1093            bData.userData.payloadStr = data.substring(3, numFields - 3);
1094       } catch (java.lang.NumberFormatException ex) {
1095            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1096        } catch (java.lang.IndexOutOfBoundsException ex) {
1097            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1098        }
1099    }
1100
1101    /**
1102     * IS-91 Short Message decoding
1103     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1104     * (For character encodings, see TIA/EIA/IS-91, Annex B)
1105     *
1106     * Protocol Summary: The user data payload may contain 1-14
1107     * characters, which are treated as normal text user data payload.
1108     * Note that the characters encoding is 6-bit packed.
1109     */
1110    private static void decodeIs91ShortMessage(BearerData bData)
1111        throws BitwiseInputStream.AccessException, CodingException
1112    {
1113        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1114        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1115        int numFields = bData.userData.numFields;
1116        if ((dataLen > 14) || (dataLen < numFields)) {
1117            throw new CodingException("IS-91 voicemail status decoding failed");
1118        }
1119        StringBuffer strbuf = new StringBuffer(dataLen);
1120        for (int i = 0; i < numFields; i++) {
1121            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1122        }
1123        bData.userData.payloadStr = strbuf.toString();
1124    }
1125
1126    /**
1127     * IS-91 CLI message (callback number) decoding
1128     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1129     *
1130     * Protocol Summary: The data payload may contain 1-32 digits,
1131     * encoded using standard 4-bit DTMF, which are treated as a
1132     * callback number.
1133     */
1134    private static void decodeIs91Cli(BearerData bData) throws CodingException {
1135        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1136        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
1137        int numFields = bData.userData.numFields;
1138        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1139            throw new CodingException("IS-91 voicemail status decoding failed");
1140        }
1141        CdmaSmsAddress addr = new CdmaSmsAddress();
1142        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
1143        addr.origBytes = bData.userData.payload;
1144        addr.numberOfDigits = (byte)numFields;
1145        decodeSmsAddress(addr);
1146        bData.callbackNumber = addr;
1147    }
1148
1149    private static void decodeIs91(BearerData bData)
1150        throws BitwiseInputStream.AccessException, CodingException
1151    {
1152        switch (bData.userData.msgType) {
1153        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
1154            decodeIs91VoicemailStatus(bData);
1155            break;
1156        case UserData.IS91_MSG_TYPE_CLI:
1157            decodeIs91Cli(bData);
1158            break;
1159        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
1160        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
1161            decodeIs91ShortMessage(bData);
1162            break;
1163        default:
1164            throw new CodingException("unsupported IS-91 message type (" +
1165                    bData.userData.msgType + ")");
1166        }
1167    }
1168
1169    private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
1170        throws BitwiseInputStream.AccessException, CodingException
1171    {
1172        final int EXPECTED_PARAM_SIZE = 1 * 8;
1173        boolean decodeSuccess = false;
1174        int paramBits = inStream.read(8) * 8;
1175        if (paramBits >= EXPECTED_PARAM_SIZE) {
1176            paramBits -= EXPECTED_PARAM_SIZE;
1177            decodeSuccess = true;
1178            bData.userAckReq     = (inStream.read(1) == 1);
1179            bData.deliveryAckReq = (inStream.read(1) == 1);
1180            bData.readAckReq     = (inStream.read(1) == 1);
1181            bData.reportReq      = (inStream.read(1) == 1);
1182            inStream.skip(4);
1183        }
1184        if ((! decodeSuccess) || (paramBits > 0)) {
1185            Log.d(LOG_TAG, "REPLY_OPTION decode " +
1186                      (decodeSuccess ? "succeeded" : "failed") +
1187                      " (extra bits = " + paramBits + ")");
1188        }
1189        inStream.skip(paramBits);
1190        return decodeSuccess;
1191    }
1192
1193    private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
1194        throws BitwiseInputStream.AccessException, CodingException
1195    {
1196        final int EXPECTED_PARAM_SIZE = 1 * 8;
1197        boolean decodeSuccess = false;
1198        int paramBits = inStream.read(8) * 8;
1199        if (paramBits >= EXPECTED_PARAM_SIZE) {
1200            paramBits -= EXPECTED_PARAM_SIZE;
1201            decodeSuccess = true;
1202            bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
1203        }
1204        if ((! decodeSuccess) || (paramBits > 0)) {
1205            Log.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
1206                      (decodeSuccess ? "succeeded" : "failed") +
1207                      " (extra bits = " + paramBits + ")");
1208        }
1209        inStream.skip(paramBits);
1210        return decodeSuccess;
1211    }
1212
1213    private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
1214        throws BitwiseInputStream.AccessException, CodingException
1215    {
1216        final int EXPECTED_PARAM_SIZE = 2 * 8;
1217        boolean decodeSuccess = false;
1218        int paramBits = inStream.read(8) * 8;
1219        if (paramBits >= EXPECTED_PARAM_SIZE) {
1220            paramBits -= EXPECTED_PARAM_SIZE;
1221            decodeSuccess = true;
1222            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
1223        }
1224        if ((! decodeSuccess) || (paramBits > 0)) {
1225            Log.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
1226                      (decodeSuccess ? "succeeded" : "failed") +
1227                      " (extra bits = " + paramBits + ")");
1228        }
1229        inStream.skip(paramBits);
1230        return decodeSuccess;
1231    }
1232
1233    private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
1234        throws CodingException
1235    {
1236        /* DTMF 4-bit digit encoding, defined in at
1237         * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
1238        StringBuffer strBuf = new StringBuffer(numFields);
1239        for (int i = 0; i < numFields; i++) {
1240            int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
1241            if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
1242            else if (val == 10) strBuf.append('0');
1243            else if (val == 11) strBuf.append('*');
1244            else if (val == 12) strBuf.append('#');
1245            else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
1246        }
1247        return strBuf.toString();
1248    }
1249
1250    private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
1251        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1252            try {
1253                /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
1254                 * just 7-bit ASCII encoding, with the MSB being zero. */
1255                addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
1256            } catch (java.io.UnsupportedEncodingException ex) {
1257                throw new CodingException("invalid SMS address ASCII code");
1258            }
1259        } else {
1260            addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
1261        }
1262    }
1263
1264    private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
1265        throws BitwiseInputStream.AccessException, CodingException
1266    {
1267        int paramBits = inStream.read(8) * 8;
1268        CdmaSmsAddress addr = new CdmaSmsAddress();
1269        addr.digitMode = inStream.read(1);
1270        byte fieldBits = 4;
1271        byte consumedBits = 1;
1272        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1273            addr.ton = inStream.read(3);
1274            addr.numberPlan = inStream.read(4);
1275            fieldBits = 8;
1276            consumedBits += 7;
1277        }
1278        addr.numberOfDigits = inStream.read(8);
1279        consumedBits += 8;
1280        int remainingBits = paramBits - consumedBits;
1281        int dataBits = addr.numberOfDigits * fieldBits;
1282        int paddingBits = remainingBits - dataBits;
1283        if (remainingBits < dataBits) {
1284            throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
1285                                      "remainingBits + " + remainingBits + ", dataBits + " +
1286                                      dataBits + ", paddingBits + " + paddingBits + ")");
1287        }
1288        addr.origBytes = inStream.readByteArray(dataBits);
1289        inStream.skip(paddingBits);
1290        decodeSmsAddress(addr);
1291        bData.callbackNumber = addr;
1292        return true;
1293    }
1294
1295    private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
1296        throws BitwiseInputStream.AccessException, CodingException
1297    {
1298        final int EXPECTED_PARAM_SIZE = 1 * 8;
1299        boolean decodeSuccess = false;
1300        int paramBits = inStream.read(8) * 8;
1301        if (paramBits >= EXPECTED_PARAM_SIZE) {
1302            paramBits -= EXPECTED_PARAM_SIZE;
1303            decodeSuccess = true;
1304            bData.errorClass = inStream.read(2);
1305            bData.messageStatus = inStream.read(6);
1306        }
1307        if ((! decodeSuccess) || (paramBits > 0)) {
1308            Log.d(LOG_TAG, "MESSAGE_STATUS decode " +
1309                      (decodeSuccess ? "succeeded" : "failed") +
1310                      " (extra bits = " + paramBits + ")");
1311        }
1312        inStream.skip(paramBits);
1313        bData.messageStatusSet = decodeSuccess;
1314        return decodeSuccess;
1315    }
1316
1317    private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
1318        throws BitwiseInputStream.AccessException, CodingException
1319    {
1320        final int EXPECTED_PARAM_SIZE = 6 * 8;
1321        boolean decodeSuccess = false;
1322        int paramBits = inStream.read(8) * 8;
1323        if (paramBits >= EXPECTED_PARAM_SIZE) {
1324            paramBits -= EXPECTED_PARAM_SIZE;
1325            decodeSuccess = true;
1326            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1327        }
1328        if ((! decodeSuccess) || (paramBits > 0)) {
1329            Log.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
1330                      (decodeSuccess ? "succeeded" : "failed") +
1331                      " (extra bits = " + paramBits + ")");
1332        }
1333        inStream.skip(paramBits);
1334        return decodeSuccess;
1335    }
1336
1337    private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
1338        throws BitwiseInputStream.AccessException, CodingException
1339    {
1340        final int EXPECTED_PARAM_SIZE = 6 * 8;
1341        boolean decodeSuccess = false;
1342        int paramBits = inStream.read(8) * 8;
1343        if (paramBits >= EXPECTED_PARAM_SIZE) {
1344            paramBits -= EXPECTED_PARAM_SIZE;
1345            decodeSuccess = true;
1346            bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1347        }
1348        if ((! decodeSuccess) || (paramBits > 0)) {
1349            Log.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
1350                      (decodeSuccess ? "succeeded" : "failed") +
1351                      " (extra bits = " + paramBits + ")");
1352        }
1353        inStream.skip(paramBits);
1354        return decodeSuccess;
1355    }
1356
1357    private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
1358        throws BitwiseInputStream.AccessException, CodingException
1359    {
1360        final int EXPECTED_PARAM_SIZE = 6 * 8;
1361        boolean decodeSuccess = false;
1362        int paramBits = inStream.read(8) * 8;
1363        if (paramBits >= EXPECTED_PARAM_SIZE) {
1364            paramBits -= EXPECTED_PARAM_SIZE;
1365            decodeSuccess = true;
1366            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
1367                    inStream.readByteArray(6 * 8));
1368        }
1369        if ((! decodeSuccess) || (paramBits > 0)) {
1370            Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
1371                      (decodeSuccess ? "succeeded" : "failed") +
1372                      " (extra bits = " + paramBits + ")");
1373        }
1374        inStream.skip(paramBits);
1375        return decodeSuccess;
1376    }
1377
1378    private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
1379        throws BitwiseInputStream.AccessException, CodingException
1380    {
1381        final int EXPECTED_PARAM_SIZE = 1 * 8;
1382        boolean decodeSuccess = false;
1383        int paramBits = inStream.read(8) * 8;
1384        if (paramBits >= EXPECTED_PARAM_SIZE) {
1385            paramBits -= EXPECTED_PARAM_SIZE;
1386            decodeSuccess = true;
1387            bData.deferredDeliveryTimeRelative = inStream.read(8);
1388        }
1389        if ((! decodeSuccess) || (paramBits > 0)) {
1390            Log.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
1391                      (decodeSuccess ? "succeeded" : "failed") +
1392                      " (extra bits = " + paramBits + ")");
1393        }
1394        inStream.skip(paramBits);
1395        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
1396        return decodeSuccess;
1397    }
1398
1399    private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
1400        throws BitwiseInputStream.AccessException, CodingException
1401    {
1402        final int EXPECTED_PARAM_SIZE = 1 * 8;
1403        boolean decodeSuccess = false;
1404        int paramBits = inStream.read(8) * 8;
1405        if (paramBits >= EXPECTED_PARAM_SIZE) {
1406            paramBits -= EXPECTED_PARAM_SIZE;
1407            decodeSuccess = true;
1408            bData.validityPeriodRelative = inStream.read(8);
1409        }
1410        if ((! decodeSuccess) || (paramBits > 0)) {
1411            Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
1412                      (decodeSuccess ? "succeeded" : "failed") +
1413                      " (extra bits = " + paramBits + ")");
1414        }
1415        inStream.skip(paramBits);
1416        bData.validityPeriodRelativeSet = decodeSuccess;
1417        return decodeSuccess;
1418    }
1419
1420    private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
1421        throws BitwiseInputStream.AccessException, CodingException
1422    {
1423        final int EXPECTED_PARAM_SIZE = 1 * 8;
1424        boolean decodeSuccess = false;
1425        int paramBits = inStream.read(8) * 8;
1426        if (paramBits >= EXPECTED_PARAM_SIZE) {
1427            paramBits -= EXPECTED_PARAM_SIZE;
1428            decodeSuccess = true;
1429            bData.privacy = inStream.read(2);
1430            inStream.skip(6);
1431        }
1432        if ((! decodeSuccess) || (paramBits > 0)) {
1433            Log.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
1434                      (decodeSuccess ? "succeeded" : "failed") +
1435                      " (extra bits = " + paramBits + ")");
1436        }
1437        inStream.skip(paramBits);
1438        bData.privacyIndicatorSet = decodeSuccess;
1439        return decodeSuccess;
1440    }
1441
1442    private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
1443        throws BitwiseInputStream.AccessException, CodingException
1444    {
1445        final int EXPECTED_PARAM_SIZE = 1 * 8;
1446        boolean decodeSuccess = false;
1447        int paramBits = inStream.read(8) * 8;
1448        if (paramBits >= EXPECTED_PARAM_SIZE) {
1449            paramBits -= EXPECTED_PARAM_SIZE;
1450            decodeSuccess = true;
1451            bData.language = inStream.read(8);
1452        }
1453        if ((! decodeSuccess) || (paramBits > 0)) {
1454            Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
1455                      (decodeSuccess ? "succeeded" : "failed") +
1456                      " (extra bits = " + paramBits + ")");
1457        }
1458        inStream.skip(paramBits);
1459        bData.languageIndicatorSet = decodeSuccess;
1460        return decodeSuccess;
1461    }
1462
1463    private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
1464        throws BitwiseInputStream.AccessException, CodingException
1465    {
1466        final int EXPECTED_PARAM_SIZE = 1 * 8;
1467        boolean decodeSuccess = false;
1468        int paramBits = inStream.read(8) * 8;
1469        if (paramBits >= EXPECTED_PARAM_SIZE) {
1470            paramBits -= EXPECTED_PARAM_SIZE;
1471            decodeSuccess = true;
1472            bData.displayMode = inStream.read(2);
1473            inStream.skip(6);
1474        }
1475        if ((! decodeSuccess) || (paramBits > 0)) {
1476            Log.d(LOG_TAG, "DISPLAY_MODE decode " +
1477                      (decodeSuccess ? "succeeded" : "failed") +
1478                      " (extra bits = " + paramBits + ")");
1479        }
1480        inStream.skip(paramBits);
1481        bData.displayModeSet = decodeSuccess;
1482        return decodeSuccess;
1483    }
1484
1485    private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
1486        throws BitwiseInputStream.AccessException, CodingException
1487    {
1488        final int EXPECTED_PARAM_SIZE = 1 * 8;
1489        boolean decodeSuccess = false;
1490        int paramBits = inStream.read(8) * 8;
1491        if (paramBits >= EXPECTED_PARAM_SIZE) {
1492            paramBits -= EXPECTED_PARAM_SIZE;
1493            decodeSuccess = true;
1494            bData.priority = inStream.read(2);
1495            inStream.skip(6);
1496        }
1497        if ((! decodeSuccess) || (paramBits > 0)) {
1498            Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
1499                      (decodeSuccess ? "succeeded" : "failed") +
1500                      " (extra bits = " + paramBits + ")");
1501        }
1502        inStream.skip(paramBits);
1503        bData.priorityIndicatorSet = decodeSuccess;
1504        return decodeSuccess;
1505    }
1506
1507    private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
1508        throws BitwiseInputStream.AccessException, CodingException
1509    {
1510        final int EXPECTED_PARAM_SIZE = 1 * 8;
1511        boolean decodeSuccess = false;
1512        int paramBits = inStream.read(8) * 8;
1513        if (paramBits >= EXPECTED_PARAM_SIZE) {
1514            paramBits -= EXPECTED_PARAM_SIZE;
1515            decodeSuccess = true;
1516            bData.alert = inStream.read(2);
1517            inStream.skip(6);
1518        }
1519        if ((! decodeSuccess) || (paramBits > 0)) {
1520            Log.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
1521                      (decodeSuccess ? "succeeded" : "failed") +
1522                      " (extra bits = " + paramBits + ")");
1523        }
1524        inStream.skip(paramBits);
1525        bData.alertIndicatorSet = decodeSuccess;
1526        return decodeSuccess;
1527    }
1528
1529    private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
1530        throws BitwiseInputStream.AccessException, CodingException
1531    {
1532        final int EXPECTED_PARAM_SIZE = 1 * 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.userResponseCode = inStream.read(8);
1539        }
1540        if ((! decodeSuccess) || (paramBits > 0)) {
1541            Log.d(LOG_TAG, "USER_REPONSE_CODE decode " +
1542                      (decodeSuccess ? "succeeded" : "failed") +
1543                      " (extra bits = " + paramBits + ")");
1544        }
1545        inStream.skip(paramBits);
1546        bData.userResponseCodeSet = decodeSuccess;
1547        return decodeSuccess;
1548    }
1549
1550    /**
1551     * Create BearerData object from serialized representation.
1552     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
1553     *
1554     * @param smsData byte array of raw encoded SMS bearer data.
1555     *
1556     * @return an instance of BearerData.
1557     */
1558    public static BearerData decode(byte[] smsData) {
1559        try {
1560            BitwiseInputStream inStream = new BitwiseInputStream(smsData);
1561            BearerData bData = new BearerData();
1562            int foundSubparamMask = 0;
1563            while (inStream.available() > 0) {
1564                boolean decodeSuccess = false;
1565                int subparamId = inStream.read(8);
1566                int subparamIdBit = 1 << subparamId;
1567                if ((foundSubparamMask & subparamIdBit) != 0) {
1568                    throw new CodingException("illegal duplicate subparameter (" +
1569                                              subparamId + ")");
1570                }
1571                switch (subparamId) {
1572                case SUBPARAM_MESSAGE_IDENTIFIER:
1573                    decodeSuccess = decodeMessageId(bData, inStream);
1574                    break;
1575                case SUBPARAM_USER_DATA:
1576                    decodeSuccess = decodeUserData(bData, inStream);
1577                    break;
1578                case SUBPARAM_USER_REPONSE_CODE:
1579                    decodeSuccess = decodeUserResponseCode(bData, inStream);
1580                    break;
1581                case SUBPARAM_REPLY_OPTION:
1582                    decodeSuccess = decodeReplyOption(bData, inStream);
1583                    break;
1584                case SUBPARAM_NUMBER_OF_MESSAGES:
1585                    decodeSuccess = decodeMsgCount(bData, inStream);
1586                    break;
1587                case SUBPARAM_CALLBACK_NUMBER:
1588                    decodeSuccess = decodeCallbackNumber(bData, inStream);
1589                    break;
1590                case SUBPARAM_MESSAGE_STATUS:
1591                    decodeSuccess = decodeMsgStatus(bData, inStream);
1592                    break;
1593                case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
1594                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
1595                    break;
1596                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
1597                    decodeSuccess = decodeValidityAbs(bData, inStream);
1598                    break;
1599                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
1600                    decodeSuccess = decodeValidityRel(bData, inStream);
1601                    break;
1602                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
1603                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
1604                    break;
1605                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
1606                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
1607                    break;
1608                case SUBPARAM_PRIVACY_INDICATOR:
1609                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
1610                    break;
1611                case SUBPARAM_LANGUAGE_INDICATOR:
1612                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
1613                    break;
1614                case SUBPARAM_MESSAGE_DISPLAY_MODE:
1615                    decodeSuccess = decodeDisplayMode(bData, inStream);
1616                    break;
1617                case SUBPARAM_PRIORITY_INDICATOR:
1618                    decodeSuccess = decodePriorityIndicator(bData, inStream);
1619                    break;
1620                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
1621                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
1622                    break;
1623                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
1624                    decodeSuccess = decodeDepositIndex(bData, inStream);
1625                    break;
1626                default:
1627                    throw new CodingException("unsupported bearer data subparameter ("
1628                                              + subparamId + ")");
1629                }
1630                if (decodeSuccess) foundSubparamMask |= subparamIdBit;
1631            }
1632            if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
1633                throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
1634            }
1635            if (bData.userData != null) {
1636                if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
1637                    if ((foundSubparamMask ^
1638                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
1639                             (1 << SUBPARAM_USER_DATA))
1640                            != 0) {
1641                        Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
1642                              foundSubparamMask + ")");
1643                    }
1644                    decodeIs91(bData);
1645                } else {
1646                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
1647                }
1648            }
1649            return bData;
1650        } catch (BitwiseInputStream.AccessException ex) {
1651            Log.e(LOG_TAG, "BearerData decode failed: " + ex);
1652        } catch (CodingException ex) {
1653            Log.e(LOG_TAG, "BearerData decode failed: " + ex);
1654        }
1655        return null;
1656    }
1657}
1658