BearerData.java revision 4980bf4aff8d49ac4e05444a6ef40ea1536f1afb
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 android.content.res.Resources;
20import android.telephony.SmsCbCmasInfo;
21import android.telephony.cdma.CdmaSmsCbProgramData;
22import android.telephony.cdma.CdmaSmsCbProgramResults;
23import android.text.format.Time;
24import android.telephony.Rlog;
25
26import com.android.internal.telephony.GsmAlphabet;
27import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
28import com.android.internal.telephony.SmsConstants;
29import com.android.internal.telephony.SmsHeader;
30import com.android.internal.telephony.SmsMessageBase;
31import com.android.internal.telephony.uicc.IccUtils;
32import com.android.internal.util.BitwiseInputStream;
33import com.android.internal.util.BitwiseOutputStream;
34
35import java.util.ArrayList;
36import java.util.TimeZone;
37
38/**
39 * An object to encode and decode CDMA SMS bearer data.
40 */
41public 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
233        public TimeStamp() {
234            super(TimeZone.getDefault().getID());   // 3GPP2 timestamps use the local timezone
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    /**
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 {
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     */
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     */
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
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
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
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     */
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
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
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
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
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
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
599    private static void encodeEmsUserDataPayload(UserData uData)
600        throws CodingException
601    {
602        byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
603        if (uData.msgEncodingSet) {
604            if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
605                encode7bitEms(uData, headerData, true);
606            } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
607                encode16bitEms(uData, headerData);
608            } else {
609                throw new CodingException("unsupported EMS user data encoding (" +
610                                          uData.msgEncoding + ")");
611            }
612        } else {
613            try {
614                encode7bitEms(uData, headerData, false);
615            } catch (CodingException ex) {
616                encode16bitEms(uData, headerData);
617            }
618        }
619    }
620
621    private static byte[] encodeShiftJis(String msg) throws CodingException {
622        try {
623            return msg.getBytes("Shift_JIS");
624        } catch (java.io.UnsupportedEncodingException ex) {
625            throw new CodingException("Shift-JIS encode failed: " + ex);
626        }
627    }
628
629    private static void encodeUserDataPayload(UserData uData)
630        throws CodingException
631    {
632        if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
633            Rlog.e(LOG_TAG, "user data with null payloadStr");
634            uData.payloadStr = "";
635        }
636
637        if (uData.userDataHeader != null) {
638            encodeEmsUserDataPayload(uData);
639            return;
640        }
641
642        if (uData.msgEncodingSet) {
643            if (uData.msgEncoding == UserData.ENCODING_OCTET) {
644                if (uData.payload == null) {
645                    Rlog.e(LOG_TAG, "user data with octet encoding but null payload");
646                    uData.payload = new byte[0];
647                    uData.numFields = 0;
648                } else {
649                    uData.numFields = uData.payload.length;
650                }
651            } else {
652                if (uData.payloadStr == null) {
653                    Rlog.e(LOG_TAG, "non-octet user data with null payloadStr");
654                    uData.payloadStr = "";
655                }
656                if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
657                    Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
658                    uData.payload = gcr.data;
659                    uData.numFields = gcr.septets;
660                } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
661                    uData.payload = encode7bitAscii(uData.payloadStr, true);
662                    uData.numFields = uData.payloadStr.length();
663                } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
664                    uData.payload = encodeUtf16(uData.payloadStr);
665                    uData.numFields = uData.payloadStr.length();
666                } else if (uData.msgEncoding == UserData.ENCODING_SHIFT_JIS) {
667                    uData.payload = encodeShiftJis(uData.payloadStr);
668                    uData.numFields = uData.payload.length;
669                } else {
670                    throw new CodingException("unsupported user data encoding (" +
671                                              uData.msgEncoding + ")");
672                }
673            }
674        } else {
675            try {
676                uData.payload = encode7bitAscii(uData.payloadStr, false);
677                uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
678            } catch (CodingException ex) {
679                uData.payload = encodeUtf16(uData.payloadStr);
680                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
681            }
682            uData.numFields = uData.payloadStr.length();
683            uData.msgEncodingSet = true;
684        }
685    }
686
687    private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
688        throws BitwiseOutputStream.AccessException, CodingException
689    {
690        /*
691         * TODO(cleanup): Do we really need to set userData.payload as
692         * a side effect of encoding?  If not, we could avoid data
693         * copies by passing outStream directly.
694         */
695        encodeUserDataPayload(bData.userData);
696        bData.hasUserDataHeader = bData.userData.userDataHeader != null;
697
698        if (bData.userData.payload.length > SmsConstants.MAX_USER_DATA_BYTES) {
699            throw new CodingException("encoded user data too large (" +
700                                      bData.userData.payload.length +
701                                      " > " + SmsConstants.MAX_USER_DATA_BYTES + " bytes)");
702        }
703
704        /*
705         * TODO(cleanup): figure out what the right answer is WRT paddingBits field
706         *
707         *   userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
708         *   userData.paddingBits = 0; // XXX this seems better, but why?
709         *
710         */
711        int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
712        int paramBits = dataBits + 13;
713        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
714            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
715            paramBits += 8;
716        }
717        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
718        int paddingBits = (paramBytes * 8) - paramBits;
719        outStream.write(8, paramBytes);
720        outStream.write(5, bData.userData.msgEncoding);
721        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
722            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
723            outStream.write(8, bData.userData.msgType);
724        }
725        outStream.write(8, bData.userData.numFields);
726        outStream.writeByteArray(dataBits, bData.userData.payload);
727        if (paddingBits > 0) outStream.write(paddingBits, 0);
728    }
729
730    private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)
731        throws BitwiseOutputStream.AccessException
732    {
733        outStream.write(8, 1);
734        outStream.write(1, bData.userAckReq     ? 1 : 0);
735        outStream.write(1, bData.deliveryAckReq ? 1 : 0);
736        outStream.write(1, bData.readAckReq     ? 1 : 0);
737        outStream.write(1, bData.reportReq      ? 1 : 0);
738        outStream.write(4, 0);
739    }
740
741    private static byte[] encodeDtmfSmsAddress(String address) {
742        int digits = address.length();
743        int dataBits = digits * 4;
744        int dataBytes = (dataBits / 8);
745        dataBytes += (dataBits % 8) > 0 ? 1 : 0;
746        byte[] rawData = new byte[dataBytes];
747        for (int i = 0; i < digits; i++) {
748            char c = address.charAt(i);
749            int val = 0;
750            if ((c >= '1') && (c <= '9')) val = c - '0';
751            else if (c == '0') val = 10;
752            else if (c == '*') val = 11;
753            else if (c == '#') val = 12;
754            else return null;
755            rawData[i / 2] |= val << (4 - ((i % 2) * 4));
756        }
757        return rawData;
758    }
759
760    /*
761     * TODO(cleanup): CdmaSmsAddress encoding should make use of
762     * CdmaSmsAddress.parse provided that DTMF encoding is unified,
763     * and the difference in 4-bit vs. 8-bit is resolved.
764     */
765
766    private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
767        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
768            try {
769                addr.origBytes = addr.address.getBytes("US-ASCII");
770            } catch (java.io.UnsupportedEncodingException ex) {
771                throw new CodingException("invalid SMS address, cannot convert to ASCII");
772            }
773        } else {
774            addr.origBytes = encodeDtmfSmsAddress(addr.address);
775        }
776    }
777
778    private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)
779        throws BitwiseOutputStream.AccessException, CodingException
780    {
781        CdmaSmsAddress addr = bData.callbackNumber;
782        encodeCdmaSmsAddress(addr);
783        int paramBits = 9;
784        int dataBits = 0;
785        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
786            paramBits += 7;
787            dataBits = addr.numberOfDigits * 8;
788        } else {
789            dataBits = addr.numberOfDigits * 4;
790        }
791        paramBits += dataBits;
792        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
793        int paddingBits = (paramBytes * 8) - paramBits;
794        outStream.write(8, paramBytes);
795        outStream.write(1, addr.digitMode);
796        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
797            outStream.write(3, addr.ton);
798            outStream.write(4, addr.numberPlan);
799        }
800        outStream.write(8, addr.numberOfDigits);
801        outStream.writeByteArray(dataBits, addr.origBytes);
802        if (paddingBits > 0) outStream.write(paddingBits, 0);
803    }
804
805    private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)
806        throws BitwiseOutputStream.AccessException
807    {
808        outStream.write(8, 1);
809        outStream.write(2, bData.errorClass);
810        outStream.write(6, bData.messageStatus);
811    }
812
813    private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)
814        throws BitwiseOutputStream.AccessException
815    {
816        outStream.write(8, 1);
817        outStream.write(8, bData.numberOfMessages);
818    }
819
820    private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)
821        throws BitwiseOutputStream.AccessException
822    {
823        outStream.write(8, 1);
824        outStream.write(8, bData.validityPeriodRelative);
825    }
826
827    private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
828        throws BitwiseOutputStream.AccessException
829    {
830        outStream.write(8, 1);
831        outStream.write(2, bData.privacy);
832        outStream.skip(6);
833    }
834
835    private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)
836        throws BitwiseOutputStream.AccessException
837    {
838        outStream.write(8, 1);
839        outStream.write(8, bData.language);
840    }
841
842    private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)
843        throws BitwiseOutputStream.AccessException
844    {
845        outStream.write(8, 1);
846        outStream.write(2, bData.displayMode);
847        outStream.skip(6);
848    }
849
850    private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)
851        throws BitwiseOutputStream.AccessException
852    {
853        outStream.write(8, 1);
854        outStream.write(2, bData.priority);
855        outStream.skip(6);
856    }
857
858    private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)
859        throws BitwiseOutputStream.AccessException
860    {
861        outStream.write(8, 1);
862        outStream.write(2, bData.alert);
863        outStream.skip(6);
864    }
865
866    private static void encodeScpResults(BearerData bData, BitwiseOutputStream outStream)
867        throws BitwiseOutputStream.AccessException
868    {
869        ArrayList<CdmaSmsCbProgramResults> results = bData.serviceCategoryProgramResults;
870        outStream.write(8, (results.size() * 4));   // 4 octets per program result
871        for (CdmaSmsCbProgramResults result : results) {
872            int category = result.getCategory();
873            outStream.write(8, category >> 8);
874            outStream.write(8, category);
875            outStream.write(8, result.getLanguage());
876            outStream.write(4, result.getCategoryResult());
877            outStream.skip(4);
878        }
879    }
880
881    /**
882     * Create serialized representation for BearerData object.
883     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
884     *
885     * @param bData an instance of BearerData.
886     *
887     * @return byte array of raw encoded SMS bearer data.
888     */
889    public static byte[] encode(BearerData bData) {
890        bData.hasUserDataHeader = ((bData.userData != null) &&
891                (bData.userData.userDataHeader != null));
892        try {
893            BitwiseOutputStream outStream = new BitwiseOutputStream(200);
894            outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
895            encodeMessageId(bData, outStream);
896            if (bData.userData != null) {
897                outStream.write(8, SUBPARAM_USER_DATA);
898                encodeUserData(bData, outStream);
899            }
900            if (bData.callbackNumber != null) {
901                outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
902                encodeCallbackNumber(bData, outStream);
903            }
904            if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
905                outStream.write(8, SUBPARAM_REPLY_OPTION);
906                encodeReplyOption(bData, outStream);
907            }
908            if (bData.numberOfMessages != 0) {
909                outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
910                encodeMsgCount(bData, outStream);
911            }
912            if (bData.validityPeriodRelativeSet) {
913                outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
914                encodeValidityPeriodRel(bData, outStream);
915            }
916            if (bData.privacyIndicatorSet) {
917                outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
918                encodePrivacyIndicator(bData, outStream);
919            }
920            if (bData.languageIndicatorSet) {
921                outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
922                encodeLanguageIndicator(bData, outStream);
923            }
924            if (bData.displayModeSet) {
925                outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
926                encodeDisplayMode(bData, outStream);
927            }
928            if (bData.priorityIndicatorSet) {
929                outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
930                encodePriorityIndicator(bData, outStream);
931            }
932            if (bData.alertIndicatorSet) {
933                outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
934                encodeMsgDeliveryAlert(bData, outStream);
935            }
936            if (bData.messageStatusSet) {
937                outStream.write(8, SUBPARAM_MESSAGE_STATUS);
938                encodeMsgStatus(bData, outStream);
939            }
940            if (bData.serviceCategoryProgramResults != null) {
941                outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
942                encodeScpResults(bData, outStream);
943            }
944            return outStream.toByteArray();
945        } catch (BitwiseOutputStream.AccessException ex) {
946            Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
947        } catch (CodingException ex) {
948            Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
949        }
950        return null;
951   }
952
953    private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
954        throws BitwiseInputStream.AccessException {
955        final int EXPECTED_PARAM_SIZE = 3 * 8;
956        boolean decodeSuccess = false;
957        int paramBits = inStream.read(8) * 8;
958        if (paramBits >= EXPECTED_PARAM_SIZE) {
959            paramBits -= EXPECTED_PARAM_SIZE;
960            decodeSuccess = true;
961            bData.messageType = inStream.read(4);
962            bData.messageId = inStream.read(8) << 8;
963            bData.messageId |= inStream.read(8);
964            bData.hasUserDataHeader = (inStream.read(1) == 1);
965            inStream.skip(3);
966        }
967        if ((! decodeSuccess) || (paramBits > 0)) {
968            Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
969                      (decodeSuccess ? "succeeded" : "failed") +
970                      " (extra bits = " + paramBits + ")");
971        }
972        inStream.skip(paramBits);
973        return decodeSuccess;
974    }
975
976    private static boolean decodeReserved(
977            BearerData bData, BitwiseInputStream inStream, int subparamId)
978        throws BitwiseInputStream.AccessException, CodingException
979    {
980        boolean decodeSuccess = false;
981        int subparamLen = inStream.read(8); // SUBPARAM_LEN
982        int paramBits = subparamLen * 8;
983        if (paramBits <= inStream.available()) {
984            decodeSuccess = true;
985            inStream.skip(paramBits);
986        }
987        Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode "
988                + (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")");
989        if (!decodeSuccess) {
990            throw new CodingException("RESERVED bearer data subparameter " + subparamId
991                    + " had invalid SUBPARAM_LEN " + subparamLen);
992        }
993
994        return decodeSuccess;
995    }
996
997    private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
998        throws BitwiseInputStream.AccessException
999    {
1000        int paramBits = inStream.read(8) * 8;
1001        bData.userData = new UserData();
1002        bData.userData.msgEncoding = inStream.read(5);
1003        bData.userData.msgEncodingSet = true;
1004        bData.userData.msgType = 0;
1005        int consumedBits = 5;
1006        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
1007            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
1008            bData.userData.msgType = inStream.read(8);
1009            consumedBits += 8;
1010        }
1011        bData.userData.numFields = inStream.read(8);
1012        consumedBits += 8;
1013        int dataBits = paramBits - consumedBits;
1014        bData.userData.payload = inStream.readByteArray(dataBits);
1015        return true;
1016    }
1017
1018    private static String decodeUtf8(byte[] data, int offset, int numFields)
1019        throws CodingException
1020    {
1021        return decodeCharset(data, offset, numFields, 1, "UTF-8");
1022    }
1023
1024    private static String decodeUtf16(byte[] data, int offset, int numFields)
1025        throws CodingException
1026    {
1027        // Subtract header and possible padding byte (at end) from num fields.
1028        int padding = offset % 2;
1029        numFields -= (offset + padding) / 2;
1030        return decodeCharset(data, offset, numFields, 2, "utf-16be");
1031    }
1032
1033    private static String decodeCharset(byte[] data, int offset, int numFields, int width,
1034            String charset) throws CodingException
1035    {
1036        if (numFields < 0 || (numFields * width + offset) > data.length) {
1037            // Try to decode the max number of characters in payload
1038            int padding = offset % width;
1039            int maxNumFields = (data.length - offset - padding) / width;
1040            if (maxNumFields < 0) {
1041                throw new CodingException(charset + " decode failed: offset out of range");
1042            }
1043            Rlog.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
1044                    + numFields + " data.length = " + data.length + " maxNumFields = "
1045                    + maxNumFields);
1046            numFields = maxNumFields;
1047        }
1048        try {
1049            return new String(data, offset, numFields * width, charset);
1050        } catch (java.io.UnsupportedEncodingException ex) {
1051            throw new CodingException(charset + " decode failed: " + ex);
1052        }
1053    }
1054
1055    private static String decode7bitAscii(byte[] data, int offset, int numFields)
1056        throws CodingException
1057    {
1058        try {
1059            offset *= 8;
1060            StringBuffer strBuf = new StringBuffer(numFields);
1061            BitwiseInputStream inStream = new BitwiseInputStream(data);
1062            int wantedBits = (offset * 8) + (numFields * 7);
1063            if (inStream.available() < wantedBits) {
1064                throw new CodingException("insufficient data (wanted " + wantedBits +
1065                                          " bits, but only have " + inStream.available() + ")");
1066            }
1067            inStream.skip(offset);
1068            for (int i = 0; i < numFields; i++) {
1069                int charCode = inStream.read(7);
1070                if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
1071                        (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
1072                    strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
1073                } else if (charCode == UserData.ASCII_NL_INDEX) {
1074                    strBuf.append('\n');
1075                } else if (charCode == UserData.ASCII_CR_INDEX) {
1076                    strBuf.append('\r');
1077                } else {
1078                    /* For other charCodes, they are unprintable, and so simply use SPACE. */
1079                    strBuf.append(' ');
1080                }
1081            }
1082            return strBuf.toString();
1083        } catch (BitwiseInputStream.AccessException ex) {
1084            throw new CodingException("7bit ASCII decode failed: " + ex);
1085        }
1086    }
1087
1088    private static String decode7bitGsm(byte[] data, int offset, int numFields)
1089        throws CodingException
1090    {
1091        // Start reading from the next 7-bit aligned boundary after offset.
1092        int offsetBits = offset * 8;
1093        int offsetSeptets = (offsetBits + 6) / 7;
1094        numFields -= offsetSeptets;
1095        int paddingBits = (offsetSeptets * 7) - offsetBits;
1096        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
1097                0, 0);
1098        if (result == null) {
1099            throw new CodingException("7bit GSM decoding failed");
1100        }
1101        return result;
1102    }
1103
1104    private static String decodeLatin(byte[] data, int offset, int numFields)
1105        throws CodingException
1106    {
1107        return decodeCharset(data, offset, numFields, 1, "ISO-8859-1");
1108    }
1109
1110    private static String decodeShiftJis(byte[] data, int offset, int numFields)
1111        throws CodingException
1112    {
1113        return decodeCharset(data, offset, numFields, 1, "Shift_JIS");
1114    }
1115
1116    private static String decodeGsmDcs(byte[] data, int offset, int numFields, int msgType)
1117            throws CodingException
1118    {
1119        if ((msgType & 0xC0) != 0) {
1120            throw new CodingException("unsupported coding group ("
1121                    + msgType + ")");
1122        }
1123
1124        switch ((msgType >> 2) & 0x3) {
1125        case UserData.ENCODING_GSM_DCS_7BIT:
1126            return decode7bitGsm(data, offset, numFields);
1127        case UserData.ENCODING_GSM_DCS_8BIT:
1128            return decodeUtf8(data, offset, numFields);
1129        case UserData.ENCODING_GSM_DCS_16BIT:
1130            return decodeUtf16(data, offset, numFields);
1131        default:
1132            throw new CodingException("unsupported user msgType encoding ("
1133                    + msgType + ")");
1134        }
1135    }
1136
1137    private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
1138        throws CodingException
1139    {
1140        int offset = 0;
1141        if (hasUserDataHeader) {
1142            int udhLen = userData.payload[0] & 0x00FF;
1143            offset += udhLen + 1;
1144            byte[] headerData = new byte[udhLen];
1145            System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
1146            userData.userDataHeader = SmsHeader.fromByteArray(headerData);
1147        }
1148        switch (userData.msgEncoding) {
1149        case UserData.ENCODING_OCTET:
1150            /*
1151            *  Octet decoding depends on the carrier service.
1152            */
1153            boolean decodingtypeUTF8 = Resources.getSystem()
1154                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
1155
1156            // Strip off any padding bytes, meaning any differences between the length of the
1157            // array and the target length specified by numFields.  This is to avoid any
1158            // confusion by code elsewhere that only considers the payload array length.
1159            byte[] payload = new byte[userData.numFields];
1160            int copyLen = userData.numFields < userData.payload.length
1161                    ? userData.numFields : userData.payload.length;
1162
1163            System.arraycopy(userData.payload, 0, payload, 0, copyLen);
1164            userData.payload = payload;
1165
1166            if (!decodingtypeUTF8) {
1167                // There are many devices in the market that send 8bit text sms (latin encoded) as
1168                // octet encoded.
1169                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1170            } else {
1171                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
1172            }
1173            break;
1174
1175        case UserData.ENCODING_IA5:
1176        case UserData.ENCODING_7BIT_ASCII:
1177            userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
1178            break;
1179        case UserData.ENCODING_UNICODE_16:
1180            userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
1181            break;
1182        case UserData.ENCODING_GSM_7BIT_ALPHABET:
1183            userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
1184            break;
1185        case UserData.ENCODING_LATIN:
1186            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1187            break;
1188        case UserData.ENCODING_SHIFT_JIS:
1189            userData.payloadStr = decodeShiftJis(userData.payload, offset, userData.numFields);
1190            break;
1191        case UserData.ENCODING_GSM_DCS:
1192            userData.payloadStr = decodeGsmDcs(userData.payload, offset,
1193                    userData.numFields, userData.msgType);
1194            break;
1195        default:
1196            throw new CodingException("unsupported user data encoding ("
1197                                      + userData.msgEncoding + ")");
1198        }
1199    }
1200
1201    /**
1202     * IS-91 Voice Mail message decoding
1203     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1204     * (For character encodings, see TIA/EIA/IS-91, Annex B)
1205     *
1206     * Protocol Summary: The user data payload may contain 3-14
1207     * characters.  The first two characters are parsed as a number
1208     * and indicate the number of voicemails.  The third character is
1209     * either a SPACE or '!' to indicate normal or urgent priority,
1210     * respectively.  Any following characters are treated as normal
1211     * text user data payload.
1212     *
1213     * Note that the characters encoding is 6-bit packed.
1214     */
1215    private static void decodeIs91VoicemailStatus(BearerData bData)
1216        throws BitwiseInputStream.AccessException, CodingException
1217    {
1218        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1219        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1220        int numFields = bData.userData.numFields;
1221        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1222            throw new CodingException("IS-91 voicemail status decoding failed");
1223        }
1224        try {
1225            StringBuffer strbuf = new StringBuffer(dataLen);
1226            while (inStream.available() >= 6) {
1227                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1228            }
1229            String data = strbuf.toString();
1230            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
1231            char prioCode = data.charAt(2);
1232            if (prioCode == ' ') {
1233                bData.priority = PRIORITY_NORMAL;
1234            } else if (prioCode == '!') {
1235                bData.priority = PRIORITY_URGENT;
1236            } else {
1237                throw new CodingException("IS-91 voicemail status decoding failed: " +
1238                        "illegal priority setting (" + prioCode + ")");
1239            }
1240            bData.priorityIndicatorSet = true;
1241            bData.userData.payloadStr = data.substring(3, numFields - 3);
1242       } catch (java.lang.NumberFormatException ex) {
1243            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1244        } catch (java.lang.IndexOutOfBoundsException ex) {
1245            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1246        }
1247    }
1248
1249    /**
1250     * IS-91 Short Message decoding
1251     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1252     * (For character encodings, see TIA/EIA/IS-91, Annex B)
1253     *
1254     * Protocol Summary: The user data payload may contain 1-14
1255     * characters, which are treated as normal text user data payload.
1256     * Note that the characters encoding is 6-bit packed.
1257     */
1258    private static void decodeIs91ShortMessage(BearerData bData)
1259        throws BitwiseInputStream.AccessException, CodingException
1260    {
1261        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1262        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1263        int numFields = bData.userData.numFields;
1264        // dataLen may be > 14 characters due to octet padding
1265        if ((numFields > 14) || (dataLen < numFields)) {
1266            throw new CodingException("IS-91 short message decoding failed");
1267        }
1268        StringBuffer strbuf = new StringBuffer(dataLen);
1269        for (int i = 0; i < numFields; i++) {
1270            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1271        }
1272        bData.userData.payloadStr = strbuf.toString();
1273    }
1274
1275    /**
1276     * IS-91 CLI message (callback number) decoding
1277     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1278     *
1279     * Protocol Summary: The data payload may contain 1-32 digits,
1280     * encoded using standard 4-bit DTMF, which are treated as a
1281     * callback number.
1282     */
1283    private static void decodeIs91Cli(BearerData bData) throws CodingException {
1284        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1285        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
1286        int numFields = bData.userData.numFields;
1287        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1288            throw new CodingException("IS-91 voicemail status decoding failed");
1289        }
1290        CdmaSmsAddress addr = new CdmaSmsAddress();
1291        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
1292        addr.origBytes = bData.userData.payload;
1293        addr.numberOfDigits = (byte)numFields;
1294        decodeSmsAddress(addr);
1295        bData.callbackNumber = addr;
1296    }
1297
1298    private static void decodeIs91(BearerData bData)
1299        throws BitwiseInputStream.AccessException, CodingException
1300    {
1301        switch (bData.userData.msgType) {
1302        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
1303            decodeIs91VoicemailStatus(bData);
1304            break;
1305        case UserData.IS91_MSG_TYPE_CLI:
1306            decodeIs91Cli(bData);
1307            break;
1308        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
1309        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
1310            decodeIs91ShortMessage(bData);
1311            break;
1312        default:
1313            throw new CodingException("unsupported IS-91 message type (" +
1314                    bData.userData.msgType + ")");
1315        }
1316    }
1317
1318    private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
1319        throws BitwiseInputStream.AccessException {
1320        final int EXPECTED_PARAM_SIZE = 1 * 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.userAckReq     = (inStream.read(1) == 1);
1327            bData.deliveryAckReq = (inStream.read(1) == 1);
1328            bData.readAckReq     = (inStream.read(1) == 1);
1329            bData.reportReq      = (inStream.read(1) == 1);
1330            inStream.skip(4);
1331        }
1332        if ((! decodeSuccess) || (paramBits > 0)) {
1333            Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
1334                      (decodeSuccess ? "succeeded" : "failed") +
1335                      " (extra bits = " + paramBits + ")");
1336        }
1337        inStream.skip(paramBits);
1338        return decodeSuccess;
1339    }
1340
1341    private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
1342        throws BitwiseInputStream.AccessException {
1343        final int EXPECTED_PARAM_SIZE = 1 * 8;
1344        boolean decodeSuccess = false;
1345        int paramBits = inStream.read(8) * 8;
1346        if (paramBits >= EXPECTED_PARAM_SIZE) {
1347            paramBits -= EXPECTED_PARAM_SIZE;
1348            decodeSuccess = true;
1349            bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
1350        }
1351        if ((! decodeSuccess) || (paramBits > 0)) {
1352            Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
1353                      (decodeSuccess ? "succeeded" : "failed") +
1354                      " (extra bits = " + paramBits + ")");
1355        }
1356        inStream.skip(paramBits);
1357        return decodeSuccess;
1358    }
1359
1360    private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
1361        throws BitwiseInputStream.AccessException {
1362        final int EXPECTED_PARAM_SIZE = 2 * 8;
1363        boolean decodeSuccess = false;
1364        int paramBits = inStream.read(8) * 8;
1365        if (paramBits >= EXPECTED_PARAM_SIZE) {
1366            paramBits -= EXPECTED_PARAM_SIZE;
1367            decodeSuccess = true;
1368            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
1369        }
1370        if ((! decodeSuccess) || (paramBits > 0)) {
1371            Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
1372                      (decodeSuccess ? "succeeded" : "failed") +
1373                      " (extra bits = " + paramBits + ")");
1374        }
1375        inStream.skip(paramBits);
1376        return decodeSuccess;
1377    }
1378
1379    private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
1380        throws CodingException
1381    {
1382        /* DTMF 4-bit digit encoding, defined in at
1383         * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
1384        StringBuffer strBuf = new StringBuffer(numFields);
1385        for (int i = 0; i < numFields; i++) {
1386            int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
1387            if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
1388            else if (val == 10) strBuf.append('0');
1389            else if (val == 11) strBuf.append('*');
1390            else if (val == 12) strBuf.append('#');
1391            else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
1392        }
1393        return strBuf.toString();
1394    }
1395
1396    private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
1397        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1398            try {
1399                /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
1400                 * just 7-bit ASCII encoding, with the MSB being zero. */
1401                addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
1402            } catch (java.io.UnsupportedEncodingException ex) {
1403                throw new CodingException("invalid SMS address ASCII code");
1404            }
1405        } else {
1406            addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
1407        }
1408    }
1409
1410    private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
1411        throws BitwiseInputStream.AccessException, CodingException
1412    {
1413        final int EXPECTED_PARAM_SIZE = 1 * 8; //at least
1414        int paramBits = inStream.read(8) * 8;
1415        if (paramBits < EXPECTED_PARAM_SIZE) {
1416            inStream.skip(paramBits);
1417            return false;
1418        }
1419        CdmaSmsAddress addr = new CdmaSmsAddress();
1420        addr.digitMode = inStream.read(1);
1421        byte fieldBits = 4;
1422        byte consumedBits = 1;
1423        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1424            addr.ton = inStream.read(3);
1425            addr.numberPlan = inStream.read(4);
1426            fieldBits = 8;
1427            consumedBits += 7;
1428        }
1429        addr.numberOfDigits = inStream.read(8);
1430        consumedBits += 8;
1431        int remainingBits = paramBits - consumedBits;
1432        int dataBits = addr.numberOfDigits * fieldBits;
1433        int paddingBits = remainingBits - dataBits;
1434        if (remainingBits < dataBits) {
1435            throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
1436                                      "remainingBits + " + remainingBits + ", dataBits + " +
1437                                      dataBits + ", paddingBits + " + paddingBits + ")");
1438        }
1439        addr.origBytes = inStream.readByteArray(dataBits);
1440        inStream.skip(paddingBits);
1441        decodeSmsAddress(addr);
1442        bData.callbackNumber = addr;
1443        return true;
1444    }
1445
1446    private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
1447        throws BitwiseInputStream.AccessException {
1448        final int EXPECTED_PARAM_SIZE = 1 * 8;
1449        boolean decodeSuccess = false;
1450        int paramBits = inStream.read(8) * 8;
1451        if (paramBits >= EXPECTED_PARAM_SIZE) {
1452            paramBits -= EXPECTED_PARAM_SIZE;
1453            decodeSuccess = true;
1454            bData.errorClass = inStream.read(2);
1455            bData.messageStatus = inStream.read(6);
1456        }
1457        if ((! decodeSuccess) || (paramBits > 0)) {
1458            Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
1459                      (decodeSuccess ? "succeeded" : "failed") +
1460                      " (extra bits = " + paramBits + ")");
1461        }
1462        inStream.skip(paramBits);
1463        bData.messageStatusSet = decodeSuccess;
1464        return decodeSuccess;
1465    }
1466
1467    private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
1468        throws BitwiseInputStream.AccessException {
1469        final int EXPECTED_PARAM_SIZE = 6 * 8;
1470        boolean decodeSuccess = false;
1471        int paramBits = inStream.read(8) * 8;
1472        if (paramBits >= EXPECTED_PARAM_SIZE) {
1473            paramBits -= EXPECTED_PARAM_SIZE;
1474            decodeSuccess = true;
1475            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1476        }
1477        if ((! decodeSuccess) || (paramBits > 0)) {
1478            Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
1479                      (decodeSuccess ? "succeeded" : "failed") +
1480                      " (extra bits = " + paramBits + ")");
1481        }
1482        inStream.skip(paramBits);
1483        return decodeSuccess;
1484    }
1485
1486    private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
1487        throws BitwiseInputStream.AccessException {
1488        final int EXPECTED_PARAM_SIZE = 6 * 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.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1495        }
1496        if ((! decodeSuccess) || (paramBits > 0)) {
1497            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
1498                      (decodeSuccess ? "succeeded" : "failed") +
1499                      " (extra bits = " + paramBits + ")");
1500        }
1501        inStream.skip(paramBits);
1502        return decodeSuccess;
1503    }
1504
1505    private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
1506        throws BitwiseInputStream.AccessException {
1507        final int EXPECTED_PARAM_SIZE = 6 * 8;
1508        boolean decodeSuccess = false;
1509        int paramBits = inStream.read(8) * 8;
1510        if (paramBits >= EXPECTED_PARAM_SIZE) {
1511            paramBits -= EXPECTED_PARAM_SIZE;
1512            decodeSuccess = true;
1513            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
1514                    inStream.readByteArray(6 * 8));
1515        }
1516        if ((! decodeSuccess) || (paramBits > 0)) {
1517            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
1518                      (decodeSuccess ? "succeeded" : "failed") +
1519                      " (extra bits = " + paramBits + ")");
1520        }
1521        inStream.skip(paramBits);
1522        return decodeSuccess;
1523    }
1524
1525    private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
1526        throws BitwiseInputStream.AccessException {
1527        final int EXPECTED_PARAM_SIZE = 1 * 8;
1528        boolean decodeSuccess = false;
1529        int paramBits = inStream.read(8) * 8;
1530        if (paramBits >= EXPECTED_PARAM_SIZE) {
1531            paramBits -= EXPECTED_PARAM_SIZE;
1532            decodeSuccess = true;
1533            bData.deferredDeliveryTimeRelative = inStream.read(8);
1534        }
1535        if ((! decodeSuccess) || (paramBits > 0)) {
1536            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
1537                      (decodeSuccess ? "succeeded" : "failed") +
1538                      " (extra bits = " + paramBits + ")");
1539        }
1540        inStream.skip(paramBits);
1541        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
1542        return decodeSuccess;
1543    }
1544
1545    private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
1546        throws BitwiseInputStream.AccessException {
1547        final int EXPECTED_PARAM_SIZE = 1 * 8;
1548        boolean decodeSuccess = false;
1549        int paramBits = inStream.read(8) * 8;
1550        if (paramBits >= EXPECTED_PARAM_SIZE) {
1551            paramBits -= EXPECTED_PARAM_SIZE;
1552            decodeSuccess = true;
1553            bData.validityPeriodRelative = inStream.read(8);
1554        }
1555        if ((! decodeSuccess) || (paramBits > 0)) {
1556            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
1557                      (decodeSuccess ? "succeeded" : "failed") +
1558                      " (extra bits = " + paramBits + ")");
1559        }
1560        inStream.skip(paramBits);
1561        bData.validityPeriodRelativeSet = decodeSuccess;
1562        return decodeSuccess;
1563    }
1564
1565    private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
1566        throws BitwiseInputStream.AccessException {
1567        final int EXPECTED_PARAM_SIZE = 1 * 8;
1568        boolean decodeSuccess = false;
1569        int paramBits = inStream.read(8) * 8;
1570        if (paramBits >= EXPECTED_PARAM_SIZE) {
1571            paramBits -= EXPECTED_PARAM_SIZE;
1572            decodeSuccess = true;
1573            bData.privacy = inStream.read(2);
1574            inStream.skip(6);
1575        }
1576        if ((! decodeSuccess) || (paramBits > 0)) {
1577            Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
1578                      (decodeSuccess ? "succeeded" : "failed") +
1579                      " (extra bits = " + paramBits + ")");
1580        }
1581        inStream.skip(paramBits);
1582        bData.privacyIndicatorSet = decodeSuccess;
1583        return decodeSuccess;
1584    }
1585
1586    private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
1587        throws BitwiseInputStream.AccessException {
1588        final int EXPECTED_PARAM_SIZE = 1 * 8;
1589        boolean decodeSuccess = false;
1590        int paramBits = inStream.read(8) * 8;
1591        if (paramBits >= EXPECTED_PARAM_SIZE) {
1592            paramBits -= EXPECTED_PARAM_SIZE;
1593            decodeSuccess = true;
1594            bData.language = inStream.read(8);
1595        }
1596        if ((! decodeSuccess) || (paramBits > 0)) {
1597            Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
1598                      (decodeSuccess ? "succeeded" : "failed") +
1599                      " (extra bits = " + paramBits + ")");
1600        }
1601        inStream.skip(paramBits);
1602        bData.languageIndicatorSet = decodeSuccess;
1603        return decodeSuccess;
1604    }
1605
1606    private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
1607        throws BitwiseInputStream.AccessException {
1608        final int EXPECTED_PARAM_SIZE = 1 * 8;
1609        boolean decodeSuccess = false;
1610        int paramBits = inStream.read(8) * 8;
1611        if (paramBits >= EXPECTED_PARAM_SIZE) {
1612            paramBits -= EXPECTED_PARAM_SIZE;
1613            decodeSuccess = true;
1614            bData.displayMode = inStream.read(2);
1615            inStream.skip(6);
1616        }
1617        if ((! decodeSuccess) || (paramBits > 0)) {
1618            Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
1619                      (decodeSuccess ? "succeeded" : "failed") +
1620                      " (extra bits = " + paramBits + ")");
1621        }
1622        inStream.skip(paramBits);
1623        bData.displayModeSet = decodeSuccess;
1624        return decodeSuccess;
1625    }
1626
1627    private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
1628        throws BitwiseInputStream.AccessException {
1629        final int EXPECTED_PARAM_SIZE = 1 * 8;
1630        boolean decodeSuccess = false;
1631        int paramBits = inStream.read(8) * 8;
1632        if (paramBits >= EXPECTED_PARAM_SIZE) {
1633            paramBits -= EXPECTED_PARAM_SIZE;
1634            decodeSuccess = true;
1635            bData.priority = inStream.read(2);
1636            inStream.skip(6);
1637        }
1638        if ((! decodeSuccess) || (paramBits > 0)) {
1639            Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
1640                      (decodeSuccess ? "succeeded" : "failed") +
1641                      " (extra bits = " + paramBits + ")");
1642        }
1643        inStream.skip(paramBits);
1644        bData.priorityIndicatorSet = decodeSuccess;
1645        return decodeSuccess;
1646    }
1647
1648    private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
1649        throws BitwiseInputStream.AccessException {
1650        final int EXPECTED_PARAM_SIZE = 1 * 8;
1651        boolean decodeSuccess = false;
1652        int paramBits = inStream.read(8) * 8;
1653        if (paramBits >= EXPECTED_PARAM_SIZE) {
1654            paramBits -= EXPECTED_PARAM_SIZE;
1655            decodeSuccess = true;
1656            bData.alert = inStream.read(2);
1657            inStream.skip(6);
1658        }
1659        if ((! decodeSuccess) || (paramBits > 0)) {
1660            Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
1661                      (decodeSuccess ? "succeeded" : "failed") +
1662                      " (extra bits = " + paramBits + ")");
1663        }
1664        inStream.skip(paramBits);
1665        bData.alertIndicatorSet = decodeSuccess;
1666        return decodeSuccess;
1667    }
1668
1669    private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
1670        throws BitwiseInputStream.AccessException {
1671        final int EXPECTED_PARAM_SIZE = 1 * 8;
1672        boolean decodeSuccess = false;
1673        int paramBits = inStream.read(8) * 8;
1674        if (paramBits >= EXPECTED_PARAM_SIZE) {
1675            paramBits -= EXPECTED_PARAM_SIZE;
1676            decodeSuccess = true;
1677            bData.userResponseCode = inStream.read(8);
1678        }
1679        if ((! decodeSuccess) || (paramBits > 0)) {
1680            Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
1681                      (decodeSuccess ? "succeeded" : "failed") +
1682                      " (extra bits = " + paramBits + ")");
1683        }
1684        inStream.skip(paramBits);
1685        bData.userResponseCodeSet = decodeSuccess;
1686        return decodeSuccess;
1687    }
1688
1689    private static boolean decodeServiceCategoryProgramData(BearerData bData,
1690            BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException
1691    {
1692        if (inStream.available() < 13) {
1693            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
1694                    + inStream.available() + " bits available");
1695        }
1696
1697        int paramBits = inStream.read(8) * 8;
1698        int msgEncoding = inStream.read(5);
1699        paramBits -= 5;
1700
1701        if (inStream.available() < paramBits) {
1702            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
1703                    + inStream.available() + " bits available (" + paramBits + " bits expected)");
1704        }
1705
1706        ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();
1707
1708        final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
1709        boolean decodeSuccess = false;
1710        while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
1711            int operation = inStream.read(4);
1712            int category = (inStream.read(8) << 8) | inStream.read(8);
1713            int language = inStream.read(8);
1714            int maxMessages = inStream.read(8);
1715            int alertOption = inStream.read(4);
1716            int numFields = inStream.read(8);
1717            paramBits -= CATEGORY_FIELD_MIN_SIZE;
1718
1719            int textBits = getBitsForNumFields(msgEncoding, numFields);
1720            if (paramBits < textBits) {
1721                throw new CodingException("category name is " + textBits + " bits in length,"
1722                        + " but there are only " + paramBits + " bits available");
1723            }
1724
1725            UserData userData = new UserData();
1726            userData.msgEncoding = msgEncoding;
1727            userData.msgEncodingSet = true;
1728            userData.numFields = numFields;
1729            userData.payload = inStream.readByteArray(textBits);
1730            paramBits -= textBits;
1731
1732            decodeUserDataPayload(userData, false);
1733            String categoryName = userData.payloadStr;
1734            CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
1735                    language, maxMessages, alertOption, categoryName);
1736            programDataList.add(programData);
1737
1738            decodeSuccess = true;
1739        }
1740
1741        if ((! decodeSuccess) || (paramBits > 0)) {
1742            Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
1743                      (decodeSuccess ? "succeeded" : "failed") +
1744                      " (extra bits = " + paramBits + ')');
1745        }
1746
1747        inStream.skip(paramBits);
1748        bData.serviceCategoryProgramData = programDataList;
1749        return decodeSuccess;
1750    }
1751
1752    private static int serviceCategoryToCmasMessageClass(int serviceCategory) {
1753        switch (serviceCategory) {
1754            case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
1755                return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
1756
1757            case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
1758                return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
1759
1760            case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
1761                return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
1762
1763            case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
1764                return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
1765
1766            case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
1767                return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
1768
1769            default:
1770                return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
1771        }
1772    }
1773
1774    /**
1775     * Calculates the number of bits to read for the specified number of encoded characters.
1776     * @param msgEncoding the message encoding to use
1777     * @param numFields the number of characters to read. For Shift-JIS and Korean encodings,
1778     *  this is the number of bytes to read.
1779     * @return the number of bits to read from the stream
1780     * @throws CodingException if the specified encoding is not supported
1781     */
1782    private static int getBitsForNumFields(int msgEncoding, int numFields)
1783            throws CodingException {
1784        switch (msgEncoding) {
1785            case UserData.ENCODING_OCTET:
1786            case UserData.ENCODING_SHIFT_JIS:
1787            case UserData.ENCODING_KOREAN:
1788            case UserData.ENCODING_LATIN:
1789            case UserData.ENCODING_LATIN_HEBREW:
1790                return numFields * 8;
1791
1792            case UserData.ENCODING_IA5:
1793            case UserData.ENCODING_7BIT_ASCII:
1794            case UserData.ENCODING_GSM_7BIT_ALPHABET:
1795                return numFields * 7;
1796
1797            case UserData.ENCODING_UNICODE_16:
1798                return numFields * 16;
1799
1800            default:
1801                throw new CodingException("unsupported message encoding (" + msgEncoding + ')');
1802        }
1803    }
1804
1805    /**
1806     * CMAS message decoding.
1807     * (See TIA-1149-0-1, CMAS over CDMA)
1808     *
1809     * @param serviceCategory is the service category from the SMS envelope
1810     */
1811    private static void decodeCmasUserData(BearerData bData, int serviceCategory)
1812            throws BitwiseInputStream.AccessException, CodingException {
1813        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1814        if (inStream.available() < 8) {
1815            throw new CodingException("emergency CB with no CMAE_protocol_version");
1816        }
1817        int protocolVersion = inStream.read(8);
1818        if (protocolVersion != 0) {
1819            throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
1820        }
1821
1822        int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
1823        int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
1824        int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
1825        int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
1826        int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
1827        int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
1828
1829        while (inStream.available() >= 16) {
1830            int recordType = inStream.read(8);
1831            int recordLen = inStream.read(8);
1832            switch (recordType) {
1833                case 0:     // Type 0 elements (Alert text)
1834                    UserData alertUserData = new UserData();
1835                    alertUserData.msgEncoding = inStream.read(5);
1836                    alertUserData.msgEncodingSet = true;
1837                    alertUserData.msgType = 0;
1838
1839                    int numFields;                          // number of chars to decode
1840                    switch (alertUserData.msgEncoding) {
1841                        case UserData.ENCODING_OCTET:
1842                        case UserData.ENCODING_LATIN:
1843                            numFields = recordLen - 1;      // subtract 1 byte for encoding
1844                            break;
1845
1846                        case UserData.ENCODING_IA5:
1847                        case UserData.ENCODING_7BIT_ASCII:
1848                        case UserData.ENCODING_GSM_7BIT_ALPHABET:
1849                            numFields = ((recordLen * 8) - 5) / 7;  // subtract 5 bits for encoding
1850                            break;
1851
1852                        case UserData.ENCODING_UNICODE_16:
1853                            numFields = (recordLen - 1) / 2;
1854                            break;
1855
1856                        default:
1857                            numFields = 0;      // unsupported encoding
1858                    }
1859
1860                    alertUserData.numFields = numFields;
1861                    alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
1862                    decodeUserDataPayload(alertUserData, false);
1863                    bData.userData = alertUserData;
1864                    break;
1865
1866                case 1:     // Type 1 elements
1867                    category = inStream.read(8);
1868                    responseType = inStream.read(8);
1869                    severity = inStream.read(4);
1870                    urgency = inStream.read(4);
1871                    certainty = inStream.read(4);
1872                    inStream.skip(recordLen * 8 - 28);
1873                    break;
1874
1875                default:
1876                    Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
1877                    inStream.skip(recordLen * 8);
1878                    break;
1879            }
1880        }
1881
1882        bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
1883                urgency, certainty);
1884    }
1885
1886    /**
1887     * Create BearerData object from serialized representation.
1888     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
1889     *
1890     * @param smsData byte array of raw encoded SMS bearer data.
1891     * @return an instance of BearerData.
1892     */
1893    public static BearerData decode(byte[] smsData) {
1894        return decode(smsData, 0);
1895    }
1896
1897    private static boolean isCmasAlertCategory(int category) {
1898        return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
1899                && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
1900    }
1901
1902    /**
1903     * Create BearerData object from serialized representation.
1904     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
1905     *
1906     * @param smsData byte array of raw encoded SMS bearer data.
1907     * @param serviceCategory the envelope service category (for CMAS alert handling)
1908     * @return an instance of BearerData.
1909     */
1910    public static BearerData decode(byte[] smsData, int serviceCategory) {
1911        try {
1912            BitwiseInputStream inStream = new BitwiseInputStream(smsData);
1913            BearerData bData = new BearerData();
1914            int foundSubparamMask = 0;
1915            while (inStream.available() > 0) {
1916                int subparamId = inStream.read(8);
1917                int subparamIdBit = 1 << subparamId;
1918                // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8)
1919                // as 32th bit is the max bit in int.
1920                // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers:
1921                // last defined subparam ID is 23 (00010111 = 0x17 = 23).
1922                // Only do duplicate subparam ID check if subparam is within defined value as
1923                // reserved subparams are just skipped.
1924                if ((foundSubparamMask & subparamIdBit) != 0 &&
1925                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
1926                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
1927                    throw new CodingException("illegal duplicate subparameter (" +
1928                                              subparamId + ")");
1929                }
1930                boolean decodeSuccess;
1931                switch (subparamId) {
1932                case SUBPARAM_MESSAGE_IDENTIFIER:
1933                    decodeSuccess = decodeMessageId(bData, inStream);
1934                    break;
1935                case SUBPARAM_USER_DATA:
1936                    decodeSuccess = decodeUserData(bData, inStream);
1937                    break;
1938                case SUBPARAM_USER_RESPONSE_CODE:
1939                    decodeSuccess = decodeUserResponseCode(bData, inStream);
1940                    break;
1941                case SUBPARAM_REPLY_OPTION:
1942                    decodeSuccess = decodeReplyOption(bData, inStream);
1943                    break;
1944                case SUBPARAM_NUMBER_OF_MESSAGES:
1945                    decodeSuccess = decodeMsgCount(bData, inStream);
1946                    break;
1947                case SUBPARAM_CALLBACK_NUMBER:
1948                    decodeSuccess = decodeCallbackNumber(bData, inStream);
1949                    break;
1950                case SUBPARAM_MESSAGE_STATUS:
1951                    decodeSuccess = decodeMsgStatus(bData, inStream);
1952                    break;
1953                case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
1954                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
1955                    break;
1956                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
1957                    decodeSuccess = decodeValidityAbs(bData, inStream);
1958                    break;
1959                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
1960                    decodeSuccess = decodeValidityRel(bData, inStream);
1961                    break;
1962                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
1963                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
1964                    break;
1965                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
1966                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
1967                    break;
1968                case SUBPARAM_PRIVACY_INDICATOR:
1969                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
1970                    break;
1971                case SUBPARAM_LANGUAGE_INDICATOR:
1972                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
1973                    break;
1974                case SUBPARAM_MESSAGE_DISPLAY_MODE:
1975                    decodeSuccess = decodeDisplayMode(bData, inStream);
1976                    break;
1977                case SUBPARAM_PRIORITY_INDICATOR:
1978                    decodeSuccess = decodePriorityIndicator(bData, inStream);
1979                    break;
1980                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
1981                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
1982                    break;
1983                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
1984                    decodeSuccess = decodeDepositIndex(bData, inStream);
1985                    break;
1986                case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
1987                    decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
1988                    break;
1989                default:
1990                    decodeSuccess = decodeReserved(bData, inStream, subparamId);
1991                }
1992                if (decodeSuccess &&
1993                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
1994                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
1995                    foundSubparamMask |= subparamIdBit;
1996                }
1997            }
1998            if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
1999                throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
2000            }
2001            if (bData.userData != null) {
2002                if (isCmasAlertCategory(serviceCategory)) {
2003                    decodeCmasUserData(bData, serviceCategory);
2004                } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
2005                    if ((foundSubparamMask ^
2006                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
2007                             (1 << SUBPARAM_USER_DATA))
2008                            != 0) {
2009                        Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
2010                              foundSubparamMask + ")");
2011                    }
2012                    decodeIs91(bData);
2013                } else {
2014                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
2015                }
2016            }
2017            return bData;
2018        } catch (BitwiseInputStream.AccessException ex) {
2019            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
2020        } catch (CodingException ex) {
2021            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
2022        }
2023        return null;
2024    }
2025}
2026