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