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