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