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