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=" + 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 void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
1117        throws CodingException
1118    {
1119        int offset = 0;
1120        if (hasUserDataHeader) {
1121            int udhLen = userData.payload[0] & 0x00FF;
1122            offset += udhLen + 1;
1123            byte[] headerData = new byte[udhLen];
1124            System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
1125            userData.userDataHeader = SmsHeader.fromByteArray(headerData);
1126        }
1127        switch (userData.msgEncoding) {
1128        case UserData.ENCODING_OCTET:
1129            /*
1130            *  Octet decoding depends on the carrier service.
1131            */
1132            boolean decodingtypeUTF8 = Resources.getSystem()
1133                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
1134
1135            // Strip off any padding bytes, meaning any differences between the length of the
1136            // array and the target length specified by numFields.  This is to avoid any
1137            // confusion by code elsewhere that only considers the payload array length.
1138            byte[] payload = new byte[userData.numFields];
1139            int copyLen = userData.numFields < userData.payload.length
1140                    ? userData.numFields : userData.payload.length;
1141
1142            System.arraycopy(userData.payload, 0, payload, 0, copyLen);
1143            userData.payload = payload;
1144
1145            if (!decodingtypeUTF8) {
1146                // There are many devices in the market that send 8bit text sms (latin encoded) as
1147                // octet encoded.
1148                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1149            } else {
1150                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
1151            }
1152            break;
1153
1154        case UserData.ENCODING_IA5:
1155        case UserData.ENCODING_7BIT_ASCII:
1156            userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
1157            break;
1158        case UserData.ENCODING_UNICODE_16:
1159            userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
1160            break;
1161        case UserData.ENCODING_GSM_7BIT_ALPHABET:
1162            userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
1163            break;
1164        case UserData.ENCODING_LATIN:
1165            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
1166            break;
1167        case UserData.ENCODING_SHIFT_JIS:
1168            userData.payloadStr = decodeShiftJis(userData.payload, offset, userData.numFields);
1169            break;
1170        default:
1171            throw new CodingException("unsupported user data encoding ("
1172                                      + userData.msgEncoding + ")");
1173        }
1174    }
1175
1176    /**
1177     * IS-91 Voice Mail message decoding
1178     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1179     * (For character encodings, see TIA/EIA/IS-91, Annex B)
1180     *
1181     * Protocol Summary: The user data payload may contain 3-14
1182     * characters.  The first two characters are parsed as a number
1183     * and indicate the number of voicemails.  The third character is
1184     * either a SPACE or '!' to indicate normal or urgent priority,
1185     * respectively.  Any following characters are treated as normal
1186     * text user data payload.
1187     *
1188     * Note that the characters encoding is 6-bit packed.
1189     */
1190    private static void decodeIs91VoicemailStatus(BearerData bData)
1191        throws BitwiseInputStream.AccessException, CodingException
1192    {
1193        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1194        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1195        int numFields = bData.userData.numFields;
1196        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1197            throw new CodingException("IS-91 voicemail status decoding failed");
1198        }
1199        try {
1200            StringBuffer strbuf = new StringBuffer(dataLen);
1201            while (inStream.available() >= 6) {
1202                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1203            }
1204            String data = strbuf.toString();
1205            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
1206            char prioCode = data.charAt(2);
1207            if (prioCode == ' ') {
1208                bData.priority = PRIORITY_NORMAL;
1209            } else if (prioCode == '!') {
1210                bData.priority = PRIORITY_URGENT;
1211            } else {
1212                throw new CodingException("IS-91 voicemail status decoding failed: " +
1213                        "illegal priority setting (" + prioCode + ")");
1214            }
1215            bData.priorityIndicatorSet = true;
1216            bData.userData.payloadStr = data.substring(3, numFields - 3);
1217       } catch (java.lang.NumberFormatException ex) {
1218            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1219        } catch (java.lang.IndexOutOfBoundsException ex) {
1220            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
1221        }
1222    }
1223
1224    /**
1225     * IS-91 Short Message decoding
1226     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1227     * (For character encodings, see TIA/EIA/IS-91, Annex B)
1228     *
1229     * Protocol Summary: The user data payload may contain 1-14
1230     * characters, which are treated as normal text user data payload.
1231     * Note that the characters encoding is 6-bit packed.
1232     */
1233    private static void decodeIs91ShortMessage(BearerData bData)
1234        throws BitwiseInputStream.AccessException, CodingException
1235    {
1236        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1237        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
1238        int numFields = bData.userData.numFields;
1239        // dataLen may be > 14 characters due to octet padding
1240        if ((numFields > 14) || (dataLen < numFields)) {
1241            throw new CodingException("IS-91 short message decoding failed");
1242        }
1243        StringBuffer strbuf = new StringBuffer(dataLen);
1244        for (int i = 0; i < numFields; i++) {
1245            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
1246        }
1247        bData.userData.payloadStr = strbuf.toString();
1248    }
1249
1250    /**
1251     * IS-91 CLI message (callback number) decoding
1252     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
1253     *
1254     * Protocol Summary: The data payload may contain 1-32 digits,
1255     * encoded using standard 4-bit DTMF, which are treated as a
1256     * callback number.
1257     */
1258    private static void decodeIs91Cli(BearerData bData) throws CodingException {
1259        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1260        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
1261        int numFields = bData.userData.numFields;
1262        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
1263            throw new CodingException("IS-91 voicemail status decoding failed");
1264        }
1265        CdmaSmsAddress addr = new CdmaSmsAddress();
1266        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
1267        addr.origBytes = bData.userData.payload;
1268        addr.numberOfDigits = (byte)numFields;
1269        decodeSmsAddress(addr);
1270        bData.callbackNumber = addr;
1271    }
1272
1273    private static void decodeIs91(BearerData bData)
1274        throws BitwiseInputStream.AccessException, CodingException
1275    {
1276        switch (bData.userData.msgType) {
1277        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
1278            decodeIs91VoicemailStatus(bData);
1279            break;
1280        case UserData.IS91_MSG_TYPE_CLI:
1281            decodeIs91Cli(bData);
1282            break;
1283        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
1284        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
1285            decodeIs91ShortMessage(bData);
1286            break;
1287        default:
1288            throw new CodingException("unsupported IS-91 message type (" +
1289                    bData.userData.msgType + ")");
1290        }
1291    }
1292
1293    private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
1294        throws BitwiseInputStream.AccessException {
1295        final int EXPECTED_PARAM_SIZE = 1 * 8;
1296        boolean decodeSuccess = false;
1297        int paramBits = inStream.read(8) * 8;
1298        if (paramBits >= EXPECTED_PARAM_SIZE) {
1299            paramBits -= EXPECTED_PARAM_SIZE;
1300            decodeSuccess = true;
1301            bData.userAckReq     = (inStream.read(1) == 1);
1302            bData.deliveryAckReq = (inStream.read(1) == 1);
1303            bData.readAckReq     = (inStream.read(1) == 1);
1304            bData.reportReq      = (inStream.read(1) == 1);
1305            inStream.skip(4);
1306        }
1307        if ((! decodeSuccess) || (paramBits > 0)) {
1308            Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
1309                      (decodeSuccess ? "succeeded" : "failed") +
1310                      " (extra bits = " + paramBits + ")");
1311        }
1312        inStream.skip(paramBits);
1313        return decodeSuccess;
1314    }
1315
1316    private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
1317        throws BitwiseInputStream.AccessException {
1318        final int EXPECTED_PARAM_SIZE = 1 * 8;
1319        boolean decodeSuccess = false;
1320        int paramBits = inStream.read(8) * 8;
1321        if (paramBits >= EXPECTED_PARAM_SIZE) {
1322            paramBits -= EXPECTED_PARAM_SIZE;
1323            decodeSuccess = true;
1324            bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
1325        }
1326        if ((! decodeSuccess) || (paramBits > 0)) {
1327            Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
1328                      (decodeSuccess ? "succeeded" : "failed") +
1329                      " (extra bits = " + paramBits + ")");
1330        }
1331        inStream.skip(paramBits);
1332        return decodeSuccess;
1333    }
1334
1335    private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
1336        throws BitwiseInputStream.AccessException {
1337        final int EXPECTED_PARAM_SIZE = 2 * 8;
1338        boolean decodeSuccess = false;
1339        int paramBits = inStream.read(8) * 8;
1340        if (paramBits >= EXPECTED_PARAM_SIZE) {
1341            paramBits -= EXPECTED_PARAM_SIZE;
1342            decodeSuccess = true;
1343            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
1344        }
1345        if ((! decodeSuccess) || (paramBits > 0)) {
1346            Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
1347                      (decodeSuccess ? "succeeded" : "failed") +
1348                      " (extra bits = " + paramBits + ")");
1349        }
1350        inStream.skip(paramBits);
1351        return decodeSuccess;
1352    }
1353
1354    private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
1355        throws CodingException
1356    {
1357        /* DTMF 4-bit digit encoding, defined in at
1358         * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
1359        StringBuffer strBuf = new StringBuffer(numFields);
1360        for (int i = 0; i < numFields; i++) {
1361            int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
1362            if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
1363            else if (val == 10) strBuf.append('0');
1364            else if (val == 11) strBuf.append('*');
1365            else if (val == 12) strBuf.append('#');
1366            else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
1367        }
1368        return strBuf.toString();
1369    }
1370
1371    private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
1372        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1373            try {
1374                /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
1375                 * just 7-bit ASCII encoding, with the MSB being zero. */
1376                addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
1377            } catch (java.io.UnsupportedEncodingException ex) {
1378                throw new CodingException("invalid SMS address ASCII code");
1379            }
1380        } else {
1381            addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
1382        }
1383    }
1384
1385    private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
1386        throws BitwiseInputStream.AccessException, CodingException
1387    {
1388        final int EXPECTED_PARAM_SIZE = 1 * 8; //at least
1389        int paramBits = inStream.read(8) * 8;
1390        if (paramBits < EXPECTED_PARAM_SIZE) {
1391            inStream.skip(paramBits);
1392            return false;
1393        }
1394        CdmaSmsAddress addr = new CdmaSmsAddress();
1395        addr.digitMode = inStream.read(1);
1396        byte fieldBits = 4;
1397        byte consumedBits = 1;
1398        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
1399            addr.ton = inStream.read(3);
1400            addr.numberPlan = inStream.read(4);
1401            fieldBits = 8;
1402            consumedBits += 7;
1403        }
1404        addr.numberOfDigits = inStream.read(8);
1405        consumedBits += 8;
1406        int remainingBits = paramBits - consumedBits;
1407        int dataBits = addr.numberOfDigits * fieldBits;
1408        int paddingBits = remainingBits - dataBits;
1409        if (remainingBits < dataBits) {
1410            throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
1411                                      "remainingBits + " + remainingBits + ", dataBits + " +
1412                                      dataBits + ", paddingBits + " + paddingBits + ")");
1413        }
1414        addr.origBytes = inStream.readByteArray(dataBits);
1415        inStream.skip(paddingBits);
1416        decodeSmsAddress(addr);
1417        bData.callbackNumber = addr;
1418        return true;
1419    }
1420
1421    private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
1422        throws BitwiseInputStream.AccessException {
1423        final int EXPECTED_PARAM_SIZE = 1 * 8;
1424        boolean decodeSuccess = false;
1425        int paramBits = inStream.read(8) * 8;
1426        if (paramBits >= EXPECTED_PARAM_SIZE) {
1427            paramBits -= EXPECTED_PARAM_SIZE;
1428            decodeSuccess = true;
1429            bData.errorClass = inStream.read(2);
1430            bData.messageStatus = inStream.read(6);
1431        }
1432        if ((! decodeSuccess) || (paramBits > 0)) {
1433            Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
1434                      (decodeSuccess ? "succeeded" : "failed") +
1435                      " (extra bits = " + paramBits + ")");
1436        }
1437        inStream.skip(paramBits);
1438        bData.messageStatusSet = decodeSuccess;
1439        return decodeSuccess;
1440    }
1441
1442    private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
1443        throws BitwiseInputStream.AccessException {
1444        final int EXPECTED_PARAM_SIZE = 6 * 8;
1445        boolean decodeSuccess = false;
1446        int paramBits = inStream.read(8) * 8;
1447        if (paramBits >= EXPECTED_PARAM_SIZE) {
1448            paramBits -= EXPECTED_PARAM_SIZE;
1449            decodeSuccess = true;
1450            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1451        }
1452        if ((! decodeSuccess) || (paramBits > 0)) {
1453            Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
1454                      (decodeSuccess ? "succeeded" : "failed") +
1455                      " (extra bits = " + paramBits + ")");
1456        }
1457        inStream.skip(paramBits);
1458        return decodeSuccess;
1459    }
1460
1461    private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
1462        throws BitwiseInputStream.AccessException {
1463        final int EXPECTED_PARAM_SIZE = 6 * 8;
1464        boolean decodeSuccess = false;
1465        int paramBits = inStream.read(8) * 8;
1466        if (paramBits >= EXPECTED_PARAM_SIZE) {
1467            paramBits -= EXPECTED_PARAM_SIZE;
1468            decodeSuccess = true;
1469            bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
1470        }
1471        if ((! decodeSuccess) || (paramBits > 0)) {
1472            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
1473                      (decodeSuccess ? "succeeded" : "failed") +
1474                      " (extra bits = " + paramBits + ")");
1475        }
1476        inStream.skip(paramBits);
1477        return decodeSuccess;
1478    }
1479
1480    private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
1481        throws BitwiseInputStream.AccessException {
1482        final int EXPECTED_PARAM_SIZE = 6 * 8;
1483        boolean decodeSuccess = false;
1484        int paramBits = inStream.read(8) * 8;
1485        if (paramBits >= EXPECTED_PARAM_SIZE) {
1486            paramBits -= EXPECTED_PARAM_SIZE;
1487            decodeSuccess = true;
1488            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
1489                    inStream.readByteArray(6 * 8));
1490        }
1491        if ((! decodeSuccess) || (paramBits > 0)) {
1492            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
1493                      (decodeSuccess ? "succeeded" : "failed") +
1494                      " (extra bits = " + paramBits + ")");
1495        }
1496        inStream.skip(paramBits);
1497        return decodeSuccess;
1498    }
1499
1500    private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
1501        throws BitwiseInputStream.AccessException {
1502        final int EXPECTED_PARAM_SIZE = 1 * 8;
1503        boolean decodeSuccess = false;
1504        int paramBits = inStream.read(8) * 8;
1505        if (paramBits >= EXPECTED_PARAM_SIZE) {
1506            paramBits -= EXPECTED_PARAM_SIZE;
1507            decodeSuccess = true;
1508            bData.deferredDeliveryTimeRelative = inStream.read(8);
1509        }
1510        if ((! decodeSuccess) || (paramBits > 0)) {
1511            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
1512                      (decodeSuccess ? "succeeded" : "failed") +
1513                      " (extra bits = " + paramBits + ")");
1514        }
1515        inStream.skip(paramBits);
1516        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
1517        return decodeSuccess;
1518    }
1519
1520    private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
1521        throws BitwiseInputStream.AccessException {
1522        final int EXPECTED_PARAM_SIZE = 1 * 8;
1523        boolean decodeSuccess = false;
1524        int paramBits = inStream.read(8) * 8;
1525        if (paramBits >= EXPECTED_PARAM_SIZE) {
1526            paramBits -= EXPECTED_PARAM_SIZE;
1527            decodeSuccess = true;
1528            bData.validityPeriodRelative = inStream.read(8);
1529        }
1530        if ((! decodeSuccess) || (paramBits > 0)) {
1531            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
1532                      (decodeSuccess ? "succeeded" : "failed") +
1533                      " (extra bits = " + paramBits + ")");
1534        }
1535        inStream.skip(paramBits);
1536        bData.validityPeriodRelativeSet = decodeSuccess;
1537        return decodeSuccess;
1538    }
1539
1540    private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
1541        throws BitwiseInputStream.AccessException {
1542        final int EXPECTED_PARAM_SIZE = 1 * 8;
1543        boolean decodeSuccess = false;
1544        int paramBits = inStream.read(8) * 8;
1545        if (paramBits >= EXPECTED_PARAM_SIZE) {
1546            paramBits -= EXPECTED_PARAM_SIZE;
1547            decodeSuccess = true;
1548            bData.privacy = inStream.read(2);
1549            inStream.skip(6);
1550        }
1551        if ((! decodeSuccess) || (paramBits > 0)) {
1552            Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
1553                      (decodeSuccess ? "succeeded" : "failed") +
1554                      " (extra bits = " + paramBits + ")");
1555        }
1556        inStream.skip(paramBits);
1557        bData.privacyIndicatorSet = decodeSuccess;
1558        return decodeSuccess;
1559    }
1560
1561    private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
1562        throws BitwiseInputStream.AccessException {
1563        final int EXPECTED_PARAM_SIZE = 1 * 8;
1564        boolean decodeSuccess = false;
1565        int paramBits = inStream.read(8) * 8;
1566        if (paramBits >= EXPECTED_PARAM_SIZE) {
1567            paramBits -= EXPECTED_PARAM_SIZE;
1568            decodeSuccess = true;
1569            bData.language = inStream.read(8);
1570        }
1571        if ((! decodeSuccess) || (paramBits > 0)) {
1572            Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
1573                      (decodeSuccess ? "succeeded" : "failed") +
1574                      " (extra bits = " + paramBits + ")");
1575        }
1576        inStream.skip(paramBits);
1577        bData.languageIndicatorSet = decodeSuccess;
1578        return decodeSuccess;
1579    }
1580
1581    private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
1582        throws BitwiseInputStream.AccessException {
1583        final int EXPECTED_PARAM_SIZE = 1 * 8;
1584        boolean decodeSuccess = false;
1585        int paramBits = inStream.read(8) * 8;
1586        if (paramBits >= EXPECTED_PARAM_SIZE) {
1587            paramBits -= EXPECTED_PARAM_SIZE;
1588            decodeSuccess = true;
1589            bData.displayMode = inStream.read(2);
1590            inStream.skip(6);
1591        }
1592        if ((! decodeSuccess) || (paramBits > 0)) {
1593            Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
1594                      (decodeSuccess ? "succeeded" : "failed") +
1595                      " (extra bits = " + paramBits + ")");
1596        }
1597        inStream.skip(paramBits);
1598        bData.displayModeSet = decodeSuccess;
1599        return decodeSuccess;
1600    }
1601
1602    private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
1603        throws BitwiseInputStream.AccessException {
1604        final int EXPECTED_PARAM_SIZE = 1 * 8;
1605        boolean decodeSuccess = false;
1606        int paramBits = inStream.read(8) * 8;
1607        if (paramBits >= EXPECTED_PARAM_SIZE) {
1608            paramBits -= EXPECTED_PARAM_SIZE;
1609            decodeSuccess = true;
1610            bData.priority = inStream.read(2);
1611            inStream.skip(6);
1612        }
1613        if ((! decodeSuccess) || (paramBits > 0)) {
1614            Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
1615                      (decodeSuccess ? "succeeded" : "failed") +
1616                      " (extra bits = " + paramBits + ")");
1617        }
1618        inStream.skip(paramBits);
1619        bData.priorityIndicatorSet = decodeSuccess;
1620        return decodeSuccess;
1621    }
1622
1623    private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
1624        throws BitwiseInputStream.AccessException {
1625        final int EXPECTED_PARAM_SIZE = 1 * 8;
1626        boolean decodeSuccess = false;
1627        int paramBits = inStream.read(8) * 8;
1628        if (paramBits >= EXPECTED_PARAM_SIZE) {
1629            paramBits -= EXPECTED_PARAM_SIZE;
1630            decodeSuccess = true;
1631            bData.alert = inStream.read(2);
1632            inStream.skip(6);
1633        }
1634        if ((! decodeSuccess) || (paramBits > 0)) {
1635            Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
1636                      (decodeSuccess ? "succeeded" : "failed") +
1637                      " (extra bits = " + paramBits + ")");
1638        }
1639        inStream.skip(paramBits);
1640        bData.alertIndicatorSet = decodeSuccess;
1641        return decodeSuccess;
1642    }
1643
1644    private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
1645        throws BitwiseInputStream.AccessException {
1646        final int EXPECTED_PARAM_SIZE = 1 * 8;
1647        boolean decodeSuccess = false;
1648        int paramBits = inStream.read(8) * 8;
1649        if (paramBits >= EXPECTED_PARAM_SIZE) {
1650            paramBits -= EXPECTED_PARAM_SIZE;
1651            decodeSuccess = true;
1652            bData.userResponseCode = inStream.read(8);
1653        }
1654        if ((! decodeSuccess) || (paramBits > 0)) {
1655            Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
1656                      (decodeSuccess ? "succeeded" : "failed") +
1657                      " (extra bits = " + paramBits + ")");
1658        }
1659        inStream.skip(paramBits);
1660        bData.userResponseCodeSet = decodeSuccess;
1661        return decodeSuccess;
1662    }
1663
1664    private static boolean decodeServiceCategoryProgramData(BearerData bData,
1665            BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException
1666    {
1667        if (inStream.available() < 13) {
1668            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
1669                    + inStream.available() + " bits available");
1670        }
1671
1672        int paramBits = inStream.read(8) * 8;
1673        int msgEncoding = inStream.read(5);
1674        paramBits -= 5;
1675
1676        if (inStream.available() < paramBits) {
1677            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
1678                    + inStream.available() + " bits available (" + paramBits + " bits expected)");
1679        }
1680
1681        ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();
1682
1683        final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
1684        boolean decodeSuccess = false;
1685        while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
1686            int operation = inStream.read(4);
1687            int category = (inStream.read(8) << 8) | inStream.read(8);
1688            int language = inStream.read(8);
1689            int maxMessages = inStream.read(8);
1690            int alertOption = inStream.read(4);
1691            int numFields = inStream.read(8);
1692            paramBits -= CATEGORY_FIELD_MIN_SIZE;
1693
1694            int textBits = getBitsForNumFields(msgEncoding, numFields);
1695            if (paramBits < textBits) {
1696                throw new CodingException("category name is " + textBits + " bits in length,"
1697                        + " but there are only " + paramBits + " bits available");
1698            }
1699
1700            UserData userData = new UserData();
1701            userData.msgEncoding = msgEncoding;
1702            userData.msgEncodingSet = true;
1703            userData.numFields = numFields;
1704            userData.payload = inStream.readByteArray(textBits);
1705            paramBits -= textBits;
1706
1707            decodeUserDataPayload(userData, false);
1708            String categoryName = userData.payloadStr;
1709            CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
1710                    language, maxMessages, alertOption, categoryName);
1711            programDataList.add(programData);
1712
1713            decodeSuccess = true;
1714        }
1715
1716        if ((! decodeSuccess) || (paramBits > 0)) {
1717            Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
1718                      (decodeSuccess ? "succeeded" : "failed") +
1719                      " (extra bits = " + paramBits + ')');
1720        }
1721
1722        inStream.skip(paramBits);
1723        bData.serviceCategoryProgramData = programDataList;
1724        return decodeSuccess;
1725    }
1726
1727    private static int serviceCategoryToCmasMessageClass(int serviceCategory) {
1728        switch (serviceCategory) {
1729            case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
1730                return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
1731
1732            case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
1733                return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
1734
1735            case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
1736                return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
1737
1738            case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
1739                return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
1740
1741            case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
1742                return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
1743
1744            default:
1745                return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
1746        }
1747    }
1748
1749    /**
1750     * Calculates the number of bits to read for the specified number of encoded characters.
1751     * @param msgEncoding the message encoding to use
1752     * @param numFields the number of characters to read. For Shift-JIS and Korean encodings,
1753     *  this is the number of bytes to read.
1754     * @return the number of bits to read from the stream
1755     * @throws CodingException if the specified encoding is not supported
1756     */
1757    private static int getBitsForNumFields(int msgEncoding, int numFields)
1758            throws CodingException {
1759        switch (msgEncoding) {
1760            case UserData.ENCODING_OCTET:
1761            case UserData.ENCODING_SHIFT_JIS:
1762            case UserData.ENCODING_KOREAN:
1763            case UserData.ENCODING_LATIN:
1764            case UserData.ENCODING_LATIN_HEBREW:
1765                return numFields * 8;
1766
1767            case UserData.ENCODING_IA5:
1768            case UserData.ENCODING_7BIT_ASCII:
1769            case UserData.ENCODING_GSM_7BIT_ALPHABET:
1770                return numFields * 7;
1771
1772            case UserData.ENCODING_UNICODE_16:
1773                return numFields * 16;
1774
1775            default:
1776                throw new CodingException("unsupported message encoding (" + msgEncoding + ')');
1777        }
1778    }
1779
1780    /**
1781     * CMAS message decoding.
1782     * (See TIA-1149-0-1, CMAS over CDMA)
1783     *
1784     * @param serviceCategory is the service category from the SMS envelope
1785     */
1786    private static void decodeCmasUserData(BearerData bData, int serviceCategory)
1787            throws BitwiseInputStream.AccessException, CodingException {
1788        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
1789        if (inStream.available() < 8) {
1790            throw new CodingException("emergency CB with no CMAE_protocol_version");
1791        }
1792        int protocolVersion = inStream.read(8);
1793        if (protocolVersion != 0) {
1794            throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
1795        }
1796
1797        int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
1798        int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
1799        int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
1800        int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
1801        int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
1802        int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
1803
1804        while (inStream.available() >= 16) {
1805            int recordType = inStream.read(8);
1806            int recordLen = inStream.read(8);
1807            switch (recordType) {
1808                case 0:     // Type 0 elements (Alert text)
1809                    UserData alertUserData = new UserData();
1810                    alertUserData.msgEncoding = inStream.read(5);
1811                    alertUserData.msgEncodingSet = true;
1812                    alertUserData.msgType = 0;
1813
1814                    int numFields;                          // number of chars to decode
1815                    switch (alertUserData.msgEncoding) {
1816                        case UserData.ENCODING_OCTET:
1817                        case UserData.ENCODING_LATIN:
1818                            numFields = recordLen - 1;      // subtract 1 byte for encoding
1819                            break;
1820
1821                        case UserData.ENCODING_IA5:
1822                        case UserData.ENCODING_7BIT_ASCII:
1823                        case UserData.ENCODING_GSM_7BIT_ALPHABET:
1824                            numFields = ((recordLen * 8) - 5) / 7;  // subtract 5 bits for encoding
1825                            break;
1826
1827                        case UserData.ENCODING_UNICODE_16:
1828                            numFields = (recordLen - 1) / 2;
1829                            break;
1830
1831                        default:
1832                            numFields = 0;      // unsupported encoding
1833                    }
1834
1835                    alertUserData.numFields = numFields;
1836                    alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
1837                    decodeUserDataPayload(alertUserData, false);
1838                    bData.userData = alertUserData;
1839                    break;
1840
1841                case 1:     // Type 1 elements
1842                    category = inStream.read(8);
1843                    responseType = inStream.read(8);
1844                    severity = inStream.read(4);
1845                    urgency = inStream.read(4);
1846                    certainty = inStream.read(4);
1847                    inStream.skip(recordLen * 8 - 28);
1848                    break;
1849
1850                default:
1851                    Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
1852                    inStream.skip(recordLen * 8);
1853                    break;
1854            }
1855        }
1856
1857        bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
1858                urgency, certainty);
1859    }
1860
1861    /**
1862     * Create BearerData object from serialized representation.
1863     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
1864     *
1865     * @param smsData byte array of raw encoded SMS bearer data.
1866     * @return an instance of BearerData.
1867     */
1868    public static BearerData decode(byte[] smsData) {
1869        return decode(smsData, 0);
1870    }
1871
1872    private static boolean isCmasAlertCategory(int category) {
1873        return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
1874                && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
1875    }
1876
1877    /**
1878     * Create BearerData object from serialized representation.
1879     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
1880     *
1881     * @param smsData byte array of raw encoded SMS bearer data.
1882     * @param serviceCategory the envelope service category (for CMAS alert handling)
1883     * @return an instance of BearerData.
1884     */
1885    public static BearerData decode(byte[] smsData, int serviceCategory) {
1886        try {
1887            BitwiseInputStream inStream = new BitwiseInputStream(smsData);
1888            BearerData bData = new BearerData();
1889            int foundSubparamMask = 0;
1890            while (inStream.available() > 0) {
1891                int subparamId = inStream.read(8);
1892                int subparamIdBit = 1 << subparamId;
1893                // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8)
1894                // as 32th bit is the max bit in int.
1895                // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers:
1896                // last defined subparam ID is 23 (00010111 = 0x17 = 23).
1897                // Only do duplicate subparam ID check if subparam is within defined value as
1898                // reserved subparams are just skipped.
1899                if ((foundSubparamMask & subparamIdBit) != 0 &&
1900                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
1901                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
1902                    throw new CodingException("illegal duplicate subparameter (" +
1903                                              subparamId + ")");
1904                }
1905                boolean decodeSuccess;
1906                switch (subparamId) {
1907                case SUBPARAM_MESSAGE_IDENTIFIER:
1908                    decodeSuccess = decodeMessageId(bData, inStream);
1909                    break;
1910                case SUBPARAM_USER_DATA:
1911                    decodeSuccess = decodeUserData(bData, inStream);
1912                    break;
1913                case SUBPARAM_USER_RESPONSE_CODE:
1914                    decodeSuccess = decodeUserResponseCode(bData, inStream);
1915                    break;
1916                case SUBPARAM_REPLY_OPTION:
1917                    decodeSuccess = decodeReplyOption(bData, inStream);
1918                    break;
1919                case SUBPARAM_NUMBER_OF_MESSAGES:
1920                    decodeSuccess = decodeMsgCount(bData, inStream);
1921                    break;
1922                case SUBPARAM_CALLBACK_NUMBER:
1923                    decodeSuccess = decodeCallbackNumber(bData, inStream);
1924                    break;
1925                case SUBPARAM_MESSAGE_STATUS:
1926                    decodeSuccess = decodeMsgStatus(bData, inStream);
1927                    break;
1928                case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
1929                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
1930                    break;
1931                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
1932                    decodeSuccess = decodeValidityAbs(bData, inStream);
1933                    break;
1934                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
1935                    decodeSuccess = decodeValidityRel(bData, inStream);
1936                    break;
1937                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
1938                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
1939                    break;
1940                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
1941                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
1942                    break;
1943                case SUBPARAM_PRIVACY_INDICATOR:
1944                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
1945                    break;
1946                case SUBPARAM_LANGUAGE_INDICATOR:
1947                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
1948                    break;
1949                case SUBPARAM_MESSAGE_DISPLAY_MODE:
1950                    decodeSuccess = decodeDisplayMode(bData, inStream);
1951                    break;
1952                case SUBPARAM_PRIORITY_INDICATOR:
1953                    decodeSuccess = decodePriorityIndicator(bData, inStream);
1954                    break;
1955                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
1956                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
1957                    break;
1958                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
1959                    decodeSuccess = decodeDepositIndex(bData, inStream);
1960                    break;
1961                case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
1962                    decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
1963                    break;
1964                default:
1965                    decodeSuccess = decodeReserved(bData, inStream, subparamId);
1966                }
1967                if (decodeSuccess &&
1968                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
1969                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
1970                    foundSubparamMask |= subparamIdBit;
1971                }
1972            }
1973            if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
1974                throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
1975            }
1976            if (bData.userData != null) {
1977                if (isCmasAlertCategory(serviceCategory)) {
1978                    decodeCmasUserData(bData, serviceCategory);
1979                } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
1980                    if ((foundSubparamMask ^
1981                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
1982                             (1 << SUBPARAM_USER_DATA))
1983                            != 0) {
1984                        Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
1985                              foundSubparamMask + ")");
1986                    }
1987                    decodeIs91(bData);
1988                } else {
1989                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
1990                }
1991            }
1992            return bData;
1993        } catch (BitwiseInputStream.AccessException ex) {
1994            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
1995        } catch (CodingException ex) {
1996            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
1997        }
1998        return null;
1999    }
2000}
2001