10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/*
20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2008 The Android Open Source Project
30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License");
50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License.
60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at
70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *      http://www.apache.org/licenses/LICENSE-2.0
90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software
110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS,
120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and
140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License.
150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony.cdma.sms;
180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.res.Resources;
200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.SmsCbCmasInfo;
210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.cdma.CdmaSmsCbProgramData;
22b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hambyimport android.telephony.cdma.CdmaSmsCbProgramResults;
230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.text.format.Time;
240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.util.Log;
250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.GsmAlphabet;
270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.IccUtils;
280825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsConstants;
290825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsHeader;
300825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.util.BitwiseInputStream;
320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.util.BitwiseOutputStream;
330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
340825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList;
350825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.TimeZone;
360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/**
380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * An object to encode and decode CDMA SMS bearer data.
390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic final class BearerData {
410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static String LOG_TAG = "SMS";
420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Bearer Data Subparameter Identifiers
450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * NOTE: Commented subparameter types are not implemented.
470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_MESSAGE_IDENTIFIER               = 0x00;
490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_USER_DATA                        = 0x01;
500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_USER_RESPONSE_CODE               = 0x02;
510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP        = 0x03;
520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_REPLY_OPTION                     = 0x0A;
590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_NUMBER_OF_MESSAGES               = 0x0B;
600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY        = 0x0C;
610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_LANGUAGE_INDICATOR               = 0x0D;
620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_CALLBACK_NUMBER                  = 0x0E;
630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA      = 0x10;
650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final static byte SUBPARAM_MESSAGE_STATUS                   = 0x14;
690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    //private final static byte SUBPARAM_TP_FAILURE_CAUSE                 = 0x15;
700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    //private final static byte SUBPARAM_ENHANCED_VMN                     = 0x16;
710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    //private final static byte SUBPARAM_ENHANCED_VMN_ACK                 = 0x17;
720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Supported message types for CDMA SMS messages
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_DELIVER        = 0x01;
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_SUBMIT         = 0x02;
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_CANCELLATION   = 0x03;
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_DELIVERY_ACK   = 0x04;
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_USER_ACK       = 0x05;
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_READ_ACK       = 0x06;
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int MESSAGE_TYPE_SUBMIT_REPORT  = 0x08;
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int messageType;
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 16-bit value indicating the message ID, which increments modulo 65536.
900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (Special rules apply for WAP-messages.)
910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int messageId;
940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Supported priority modes for CDMA SMS messages
970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIORITY_NORMAL        = 0x0;
1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIORITY_INTERACTIVE   = 0x1;
1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIORITY_URGENT        = 0x2;
1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIORITY_EMERGENCY     = 0x3;
1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean priorityIndicatorSet = false;
1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int priority = PRIORITY_NORMAL;
1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Supported privacy modes for CDMA SMS messages
1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1)
1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIVACY_NOT_RESTRICTED = 0x0;
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIVACY_RESTRICTED     = 0x1;
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIVACY_CONFIDENTIAL   = 0x2;
1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PRIVACY_SECRET         = 0x3;
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean privacyIndicatorSet = false;
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int privacy = PRIVACY_NOT_RESTRICTED;
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Supported alert priority modes for CDMA SMS messages
1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1)
1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ALERT_DEFAULT          = 0x0;
1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ALERT_LOW_PRIO         = 0x1;
1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ALERT_MEDIUM_PRIO      = 0x2;
1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ALERT_HIGH_PRIO        = 0x3;
1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean alertIndicatorSet = false;
1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int alert = ALERT_DEFAULT;
1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Supported display modes for CDMA SMS messages.  Display mode is
1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * a 2-bit value used to indicate to the mobile station when to
1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * display the received message.  (See 3GPP2 C.S0015-B, v2,
1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 4.5.16)
1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int DISPLAY_MODE_IMMEDIATE      = 0x0;
1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int DISPLAY_MODE_DEFAULT        = 0x1;
1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int DISPLAY_MODE_USER           = 0x2;
1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean displayModeSet = false;
1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int displayMode = DISPLAY_MODE_DEFAULT;
1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Language Indicator values.  NOTE: the spec (3GPP2 C.S0015-B,
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * v2, 4.5.14) is ambiguous as to the meaning of this field, as it
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * refers to C.R1001-D but that reference has been crossed out.
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * It would seem reasonable to assume the values from C.R1001-F
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (table 9.2-1) are to be used instead.
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_UNKNOWN  = 0x00;
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_ENGLISH  = 0x01;
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_FRENCH   = 0x02;
1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_SPANISH  = 0x03;
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_JAPANESE = 0x04;
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_KOREAN   = 0x05;
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_CHINESE  = 0x06;
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int LANGUAGE_HEBREW   = 0x07;
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean languageIndicatorSet = false;
1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int language = LANGUAGE_UNKNOWN;
1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * SMS Message Status Codes.  The first component of the Message
1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * status indicates if an error has occurred and whether the error
1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * is considered permanent or temporary.  The second component of
1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * the Message status indicates the cause of the error (if any).
1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2.0, 4.5.21)
1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* no-error codes */
1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ERROR_NONE                   = 0x00;
1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_ACCEPTED              = 0x00;
1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_DEPOSITED_TO_INTERNET = 0x01;
1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_DELIVERED             = 0x02;
1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_CANCELLED             = 0x03;
1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* temporary-error and permanent-error codes */
1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ERROR_TEMPORARY              = 0x02;
1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_NETWORK_CONGESTION    = 0x04;
1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_NETWORK_ERROR         = 0x05;
1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_UNKNOWN_ERROR         = 0x1F;
1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* permanent-error codes */
1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ERROR_PERMANENT              = 0x03;
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_CANCEL_FAILED         = 0x06;
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_BLOCKED_DESTINATION   = 0x07;
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_TEXT_TOO_LONG         = 0x08;
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_DUPLICATE_MESSAGE     = 0x09;
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_INVALID_DESTINATION   = 0x0A;
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_MESSAGE_EXPIRED       = 0x0D;
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /* undefined-status codes */
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int ERROR_UNDEFINED              = 0xFF;
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int STATUS_UNDEFINED             = 0xFF;
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean messageStatusSet = false;
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int errorClass = ERROR_UNDEFINED;
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int messageStatus = STATUS_UNDEFINED;
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 1-bit value that indicates whether a User Data Header (UDH) is present.
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * NOTE: during encoding, this value will be set based on the
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * presence of a UDH in the structured data, any existing setting
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * will be overwritten.
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean hasUserDataHeader;
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * provides the information for the user data
2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (e.g. padding bits, user data, user data header, etc)
2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S.0015-B, v2, 4.5.2)
2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public UserData userData;
2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The User Response Code subparameter is used in the SMS User
2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Acknowledgment Message to respond to previously received short
2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * messages. This message center-specific element carries the
2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2,
2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 4.5.3)
2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean userResponseCodeSet = false;
2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int userResponseCode;
2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4
2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static class TimeStamp extends Time {
2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public TimeStamp() {
2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            super(TimeZone.getDefault().getID());   // 3GPP2 timestamps use the local timezone
2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public static TimeStamp fromByteArray(byte[] data) {
2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            TimeStamp ts = new TimeStamp();
2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // C.S0015-B v2.0, 4.5.4: range is 1996-2095
2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int year = IccUtils.cdmaBcdByteToInt(data[0]);
2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (year > 99 || year < 0) return null;
2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ts.year = year >= 96 ? year + 1900 : year + 2000;
2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int month = IccUtils.cdmaBcdByteToInt(data[1]);
2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (month < 1 || month > 12) return null;
2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ts.month = month - 1;
2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int day = IccUtils.cdmaBcdByteToInt(data[2]);
2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (day < 1 || day > 31) return null;
2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ts.monthDay = day;
2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int hour = IccUtils.cdmaBcdByteToInt(data[3]);
2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (hour < 0 || hour > 23) return null;
2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ts.hour = hour;
2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int minute = IccUtils.cdmaBcdByteToInt(data[4]);
2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (minute < 0 || minute > 59) return null;
2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ts.minute = minute;
2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int second = IccUtils.cdmaBcdByteToInt(data[5]);
2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (second < 0 || second > 59) return null;
2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ts.second = second;
2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return ts;
2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public String toString() {
2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            StringBuilder builder = new StringBuilder();
2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append("TimeStamp ");
2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append("{ year=" + year);
2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append(", month=" + month);
2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append(", day=" + monthDay);
2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append(", hour=" + hour);
2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append(", minute=" + minute);
2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append(", second=" + second);
2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            builder.append(" }");
2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return builder.toString();
2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public TimeStamp msgCenterTimeStamp;
2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public TimeStamp validityPeriodAbsolute;
2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public TimeStamp deferredDeliveryTimeAbsolute;
2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Relative time is specified as one byte, the value of which
2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * falls into a series of ranges, as specified below.  The idea is
2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * that shorter time intervals allow greater precision -- the
2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * value means minutes from zero until the MINS_LIMIT (inclusive),
2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * upon which it means hours until the HOURS_LIMIT, and so
2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1)
2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_MINS_LIMIT      = 143;
2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_HOURS_LIMIT     = 167;
2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_DAYS_LIMIT      = 196;
2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_WEEKS_LIMIT     = 244;
2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_INDEFINITE      = 245;
2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_NOW             = 246;
2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_MOBILE_INACTIVE = 247;
2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int RELATIVE_TIME_RESERVED        = 248;
2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean validityPeriodRelativeSet;
2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int validityPeriodRelative;
2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean deferredDeliveryTimeRelativeSet;
2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int deferredDeliveryTimeRelative;
2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The Reply Option subparameter contains 1-bit values which
3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * indicate whether SMS acknowledgment is requested or not.  (See
3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 3GPP2 C.S0015-B, v2, 4.5.11)
3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean userAckReq;
3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean deliveryAckReq;
3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean readAckReq;
3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean reportReq;
3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The Number of Messages subparameter (8-bit value) is a decimal
3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * number in the 0 to 99 range representing the number of messages
3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * stored at the Voice Mail System. This element is used by the
3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Voice Mail Notification service.  (See 3GPP2 C.S0015-B, v2,
3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 4.5.12)
3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int numberOfMessages;
3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The Message Deposit Index subparameter is assigned by the
3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * message center as a unique index to the contents of the User
3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Data subparameter in each message sent to a particular mobile
3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * station. The mobile station, when replying to a previously
3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * received short message which included a Message Deposit Index
3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * subparameter, may include the Message Deposit Index of the
3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * received message to indicate to the message center that the
3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * original contents of the message are to be included in the
3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * reply.  (See 3GPP2 C.S0015-B, v2, 4.5.18)
3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int depositIndex;
3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * 4-bit or 8-bit value that indicates the number to be dialed in reply to a
3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * received SMS message.
3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-B, v2, 4.5.15)
3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public CdmaSmsAddress callbackNumber;
3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * CMAS warning notification information.
3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @see #decodeCmasUserData(BearerData, int)
3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public SmsCbCmasInfo cmasWarningInfo;
3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The Service Category Program Data subparameter is used to enable and disable
3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * SMS broadcast service categories to display. If this subparameter is present,
3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * this field will contain a list of one or more
3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * {@link android.telephony.cdma.CdmaSmsCbProgramData} objects containing the
3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * operation(s) to perform.
3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
350b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby    public ArrayList<CdmaSmsCbProgramData> serviceCategoryProgramData;
351b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby
352b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby    /**
353b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby     * The Service Category Program Results subparameter informs the message center
354b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby     * of the results of a Service Category Program Data request.
355b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby     */
356b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby    public ArrayList<CdmaSmsCbProgramResults> serviceCategoryProgramResults;
357b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby
3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static class CodingException extends Exception {
3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public CodingException(String s) {
3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            super(s);
3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Returns the language indicator as a two-character ISO 639 string.
3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return a two character ISO 639 language code
3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public String getLanguage() {
3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return getLanguageCodeForValue(language);
3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Converts a CDMA language indicator value to an ISO 639 two character language code.
3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param languageValue the CDMA language value to convert
3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return the two character ISO 639 language code for the specified value, or null if unknown
3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String getLanguageCodeForValue(int languageValue) {
3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        switch (languageValue) {
3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_ENGLISH:
3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "en";
3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_FRENCH:
3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "fr";
3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_SPANISH:
3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "es";
3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_JAPANESE:
3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "ja";
3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_KOREAN:
3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "ko";
3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_CHINESE:
3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "zh";
3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case LANGUAGE_HEBREW:
3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return "he";
4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            default:
4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return null;
4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    @Override
4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public String toString() {
4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        StringBuilder builder = new StringBuilder();
4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append("BearerData ");
4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append("{ messageType=" + messageType);
4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", messageId=" + (int)messageId);
4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", msgCenterTimeStamp=" +
4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset"));
4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", validityPeriodAbsolute=" +
4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset"));
4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", validityPeriodRelative=" +
4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset"));
4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", deferredDeliveryTimeAbsolute=" +
4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset"));
4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", deferredDeliveryTimeRelative=" +
4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset"));
4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", userAckReq=" + userAckReq);
4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", deliveryAckReq=" + deliveryAckReq);
4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", readAckReq=" + readAckReq);
4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", reportReq=" + reportReq);
4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", numberOfMessages=" + numberOfMessages);
4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", callbackNumber=" + callbackNumber);
4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", depositIndex=" + depositIndex);
4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", hasUserDataHeader=" + hasUserDataHeader);
4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(", userData=" + userData);
4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        builder.append(" }");
4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return builder.toString();
4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream)
4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 3);
4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(4, bData.messageType);
4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, bData.messageId >> 8);
4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, bData.messageId);
4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(1, bData.hasUserDataHeader ? 1 : 0);
4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.skip(3);
4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static int countAsciiSeptets(CharSequence msg, boolean force) {
4540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int msgLen = msg.length();
4550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (force) return msgLen;
4560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0; i < msgLen; i++) {
4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return -1;
4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return msgLen;
4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Calculate the message text encoding length, fragmentation, and other details.
4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param msg message text
4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param force7BitEncoding ignore (but still count) illegal characters if true
4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return septet count, or -1 on failure
4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            boolean force7BitEncoding) {
4730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        TextEncodingDetails ted;
4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int septets = countAsciiSeptets(msg, force7BitEncoding);
4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (septets != -1 && septets <= SmsConstants.MAX_USER_DATA_SEPTETS) {
4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ted = new TextEncodingDetails();
4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ted.msgCount = 1;
4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ted.codeUnitCount = septets;
4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ted.codeUnitsRemaining = SmsConstants.MAX_USER_DATA_SEPTETS - septets;
4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ted.codeUnitSize = SmsConstants.ENCODING_7BIT;
4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    msg, force7BitEncoding);
4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (ted.msgCount == 1 && ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // We don't support single-segment EMS, so calculate for 16-bit
4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // TODO: Consider supporting single-segment EMS
4870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ted.codeUnitCount = msg.length();
4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int octets = ted.codeUnitCount * 2;
4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (octets > SmsConstants.MAX_USER_DATA_BYTES) {
4900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ted.msgCount = (octets + (SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
4910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ted.codeUnitsRemaining = ((ted.msgCount *
4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ted.msgCount = 1;
4960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ted.codeUnitsRemaining = (SmsConstants.MAX_USER_DATA_BYTES - octets)/2;
4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ted.codeUnitSize = SmsConstants.ENCODING_16BIT;
4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return ted;
5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static byte[] encode7bitAscii(String msg, boolean force)
5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int msgLen = msg.length();
5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i = 0; i < msgLen; i++) {
5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
5120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (charCode == -1) {
5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (force) {
5140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
5150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    outStream.write(7, charCode);
5200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
5210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return outStream.toByteArray();
5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (BitwiseOutputStream.AccessException ex) {
5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("7bit ASCII encode failed: " + ex);
5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static byte[] encodeUtf16(String msg)
5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
5310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
5320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return msg.getBytes("utf-16be");
5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (java.io.UnsupportedEncodingException ex) {
5340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("UTF-16 encode failed: " + ex);
5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static class Gsm7bitCodingResult {
5390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int septets;
5400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        byte[] data;
5410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force)
5440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
5470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            /*
5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * TODO(cleanup): It would be nice if GsmAlphabet provided
5490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * an option to produce just the data without prepending
5500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * the septet count, as this function is really just a
5510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * wrapper to strip that off.  Not to mention that the
5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * septet count is generally known prior to invocation of
5530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * the encoder.  Note that it cannot be derived from the
5540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * resulting array length, since that cannot distinguish
5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * if the last contains either 1 or 8 valid bits.
5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             *
5570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * TODO(cleanup): The BitwiseXStreams could also be
5580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * extended with byte-wise reversed endianness read/write
5590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * routines to allow a corresponding implementation of
5600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * stringToGsm7BitPacked, and potentially directly support
5610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             * access to the main bitwise stream from encode/decode.
5620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville             */
5630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
5640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Gsm7bitCodingResult result = new Gsm7bitCodingResult();
5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            result.data = new byte[fullData.length - 1];
5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            result.septets = fullData[0] & 0x00FF;
5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return result;
5690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (com.android.internal.telephony.EncodeException ex) {
5700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("7bit GSM encode failed: " + ex);
5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encode7bitEms(UserData uData, byte[] udhData, boolean force)
5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int udhBytes = udhData.length + 1;  // Add length octet.
5780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int udhSeptets = ((udhBytes * 8) + 6) / 7;
5790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
5800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
5810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.msgEncodingSet = true;
5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.numFields = gcr.septets;
5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.payload = gcr.data;
5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.payload[0] = (byte)udhData.length;
5850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
5860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encode16bitEms(UserData uData, byte[] udhData)
5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        byte[] payload = encodeUtf16(uData.payloadStr);
5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int udhBytes = udhData.length + 1;  // Add length octet.
5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int udhCodeUnits = (udhBytes + 1) / 2;
5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int payloadCodeUnits = payload.length / 2;
5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.msgEncoding = UserData.ENCODING_UNICODE_16;
5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.msgEncodingSet = true;
5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.numFields = udhCodeUnits + payloadCodeUnits;
5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.payload = new byte[uData.numFields * 2];
5990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        uData.payload[0] = (byte)udhData.length;
6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
601115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
6020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeEmsUserDataPayload(UserData uData)
6050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (uData.msgEncodingSet) {
6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encode7bitEms(uData, headerData, true);
6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encode16bitEms(uData, headerData);
6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("unsupported EMS user data encoding (" +
6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                          uData.msgEncoding + ")");
6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            try {
6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encode7bitEms(uData, headerData, false);
6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } catch (CodingException ex) {
6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encode16bitEms(uData, headerData);
6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeUserDataPayload(UserData uData)
6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
6280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
6300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(LOG_TAG, "user data with null payloadStr");
6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            uData.payloadStr = "";
6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (uData.userDataHeader != null) {
6350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            encodeEmsUserDataPayload(uData);
6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return;
6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (uData.msgEncodingSet) {
6400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (uData.msgEncoding == UserData.ENCODING_OCTET) {
6410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (uData.payload == null) {
6420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Log.e(LOG_TAG, "user data with octet encoding but null payload");
6430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.payload = new byte[0];
6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.numFields = 0;
6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.numFields = uData.payload.length;
6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (uData.payloadStr == null) {
6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Log.e(LOG_TAG, "non-octet user data with null payloadStr");
6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.payloadStr = "";
6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.payload = gcr.data;
6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.numFields = gcr.septets;
6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.payload = encode7bitAscii(uData.payloadStr, true);
6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.numFields = uData.payloadStr.length();
6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.payload = encodeUtf16(uData.payloadStr);
6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    uData.numFields = uData.payloadStr.length();
6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    throw new CodingException("unsupported user data encoding (" +
6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                              uData.msgEncoding + ")");
6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
6670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
6690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            try {
6700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                uData.payload = encode7bitAscii(uData.payloadStr, false);
6710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
6720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } catch (CodingException ex) {
6730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                uData.payload = encodeUtf16(uData.payloadStr);
6740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
6750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            uData.numFields = uData.payloadStr.length();
6770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            uData.msgEncodingSet = true;
6780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
6820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException, CodingException
6830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
6840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /*
6850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * TODO(cleanup): Do we really need to set userData.payload as
6860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * a side effect of encoding?  If not, we could avoid data
6870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * copies by passing outStream directly.
6880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         */
6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        encodeUserDataPayload(bData.userData);
6900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.hasUserDataHeader = bData.userData.userDataHeader != null;
6910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (bData.userData.payload.length > SmsConstants.MAX_USER_DATA_BYTES) {
6930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("encoded user data too large (" +
6940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                      bData.userData.payload.length +
6950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                      " > " + SmsConstants.MAX_USER_DATA_BYTES + " bytes)");
6960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /*
6990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * TODO(cleanup): figure out what the right answer is WRT paddingBits field
7000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         *
7010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         *   userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         *   userData.paddingBits = 0; // XXX this seems better, but why?
7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         *
7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         */
7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
7060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = dataBits + 13;
7070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits += 8;
7100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paddingBits = (paramBytes * 8) - paramBits;
7130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, paramBytes);
7140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(5, bData.userData.msgEncoding);
7150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
7160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
7170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            outStream.write(8, bData.userData.msgType);
7180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, bData.userData.numFields);
7200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.writeByteArray(dataBits, bData.userData.payload);
7210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paddingBits > 0) outStream.write(paddingBits, 0);
7220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)
7250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
7260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
7280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(1, bData.userAckReq     ? 1 : 0);
7290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(1, bData.deliveryAckReq ? 1 : 0);
7300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(1, bData.readAckReq     ? 1 : 0);
7310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(1, bData.reportReq      ? 1 : 0);
7320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(4, 0);
7330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static byte[] encodeDtmfSmsAddress(String address) {
7360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int digits = address.length();
7370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataBits = digits * 4;
7380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataBytes = (dataBits / 8);
7390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        dataBytes += (dataBits % 8) > 0 ? 1 : 0;
7400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        byte[] rawData = new byte[dataBytes];
7410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0; i < digits; i++) {
7420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            char c = address.charAt(i);
7430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int val = 0;
7440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if ((c >= '1') && (c <= '9')) val = c - '0';
7450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else if (c == '0') val = 10;
7460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else if (c == '*') val = 11;
7470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else if (c == '#') val = 12;
7480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else return null;
7490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            rawData[i / 2] |= val << (4 - ((i % 2) * 4));
7500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return rawData;
7520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /*
7550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * TODO(cleanup): CdmaSmsAddress encoding should make use of
7560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * CdmaSmsAddress.parse provided that DTMF encoding is unified,
7570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * and the difference in 4-bit vs. 8-bit is resolved.
7580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
7590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
7610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
7620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            try {
7630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                addr.origBytes = addr.address.getBytes("US-ASCII");
7640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } catch (java.io.UnsupportedEncodingException ex) {
7650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("invalid SMS address, cannot convert to ASCII");
7660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
7670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
7680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            addr.origBytes = encodeDtmfSmsAddress(addr.address);
7690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)
7730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException, CodingException
7740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
7750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        CdmaSmsAddress addr = bData.callbackNumber;
7760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        encodeCdmaSmsAddress(addr);
7770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = 9;
7780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataBits = 0;
7790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
7800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits += 7;
7810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            dataBits = addr.numberOfDigits * 8;
7820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
7830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            dataBits = addr.numberOfDigits * 4;
7840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        paramBits += dataBits;
7860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
7870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paddingBits = (paramBytes * 8) - paramBits;
7880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, paramBytes);
7890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(1, addr.digitMode);
7900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
7910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            outStream.write(3, addr.ton);
7920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            outStream.write(4, addr.numberPlan);
7930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, addr.numberOfDigits);
7950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.writeByteArray(dataBits, addr.origBytes);
7960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paddingBits > 0) outStream.write(paddingBits, 0);
7970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)
8000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(2, bData.errorClass);
8040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(6, bData.messageStatus);
8050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)
8080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, bData.numberOfMessages);
8120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)
8150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, bData.validityPeriodRelative);
8190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
8220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(2, bData.privacy);
8260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.skip(6);
8270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)
8300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, bData.language);
8340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)
8370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(2, bData.displayMode);
8410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.skip(6);
8420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)
8450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(2, bData.priority);
8490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.skip(6);
8500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
8520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)
8530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseOutputStream.AccessException
8540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
8550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(8, 1);
8560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.write(2, bData.alert);
8570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        outStream.skip(6);
8580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
8590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
860b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby    private static void encodeScpResults(BearerData bData, BitwiseOutputStream outStream)
861b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby        throws BitwiseOutputStream.AccessException
862b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby    {
863b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby        ArrayList<CdmaSmsCbProgramResults> results = bData.serviceCategoryProgramResults;
864b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby        outStream.write(8, (results.size() * 4));   // 4 octets per program result
865b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby        for (CdmaSmsCbProgramResults result : results) {
866b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            int category = result.getCategory();
867b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            outStream.write(8, category >> 8);
868b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            outStream.write(8, category);
869b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            outStream.write(8, result.getLanguage());
870b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            outStream.write(4, result.getCategoryResult());
871b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            outStream.skip(4);
872b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby        }
873b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby    }
874b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby
8750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
8760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Create serialized representation for BearerData object.
8770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
8780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
8790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param bData an instance of BearerData.
8800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
8810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return byte array of raw encoded SMS bearer data.
8820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
8830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static byte[] encode(BearerData bData) {
8840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.hasUserDataHeader = ((bData.userData != null) &&
8850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                (bData.userData.userDataHeader != null));
8860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
8870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            BitwiseOutputStream outStream = new BitwiseOutputStream(200);
8880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
8890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            encodeMessageId(bData, outStream);
8900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.userData != null) {
8910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_USER_DATA);
8920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeUserData(bData, outStream);
8930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
8940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.callbackNumber != null) {
8950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
8960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeCallbackNumber(bData, outStream);
8970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
8980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
8990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_REPLY_OPTION);
9000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeReplyOption(bData, outStream);
9010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.numberOfMessages != 0) {
9030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
9040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeMsgCount(bData, outStream);
9050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.validityPeriodRelativeSet) {
9070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
9080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeValidityPeriodRel(bData, outStream);
9090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.privacyIndicatorSet) {
9110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
9120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodePrivacyIndicator(bData, outStream);
9130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.languageIndicatorSet) {
9150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
9160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeLanguageIndicator(bData, outStream);
9170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.displayModeSet) {
9190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
9200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeDisplayMode(bData, outStream);
9210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.priorityIndicatorSet) {
9230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
9240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodePriorityIndicator(bData, outStream);
9250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.alertIndicatorSet) {
9270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
9280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeMsgDeliveryAlert(bData, outStream);
9290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
9300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.messageStatusSet) {
9310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                outStream.write(8, SUBPARAM_MESSAGE_STATUS);
9320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                encodeMsgStatus(bData, outStream);
9330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
934b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            if (bData.serviceCategoryProgramResults != null) {
935b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby                outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
936b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby                encodeScpResults(bData, outStream);
937b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            }
9380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return outStream.toByteArray();
9390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (BitwiseOutputStream.AccessException ex) {
9400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(LOG_TAG, "BearerData encode failed: " + ex);
9410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (CodingException ex) {
9420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(LOG_TAG, "BearerData encode failed: " + ex);
9430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
9440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;
9450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville   }
9460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
9470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
9480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
9490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
9500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 3 * 8;
9510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
9520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
9530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
9540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
9550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
9560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.messageType = inStream.read(4);
9570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.messageId = inStream.read(8) << 8;
9580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.messageId |= inStream.read(8);
9590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.hasUserDataHeader = (inStream.read(1) == 1);
9600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(3);
9610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
9620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
9630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
9640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
9650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
9660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
9670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
9680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
9690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
9700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
9710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
9720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException
9730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
9740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
9750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData = new UserData();
9760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData.msgEncoding = inStream.read(5);
9770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData.msgEncodingSet = true;
9780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData.msgType = 0;
9790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int consumedBits = 5;
9800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
9810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
9820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.userData.msgType = inStream.read(8);
9830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            consumedBits += 8;
9840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
9850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData.numFields = inStream.read(8);
9860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        consumedBits += 8;
9870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataBits = paramBits - consumedBits;
9880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData.payload = inStream.readByteArray(dataBits);
9890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return true;
9900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
9910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
9920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String decodeUtf8(byte[] data, int offset, int numFields)
9930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
9940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
995115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        return decodeCharset(data, offset, numFields, 1, "UTF-8");
9960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
9970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
9980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String decodeUtf16(byte[] data, int offset, int numFields)
9990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
10000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
1001115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        // Subtract header and possible padding byte (at end) from num fields.
1002115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        int padding = offset % 2;
1003115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        numFields -= (offset + padding) / 2;
1004115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        return decodeCharset(data, offset, numFields, 2, "utf-16be");
1005115632cc56f813bd21543f8532c763d7d2968046Jake Hamby    }
1006115632cc56f813bd21543f8532c763d7d2968046Jake Hamby
1007115632cc56f813bd21543f8532c763d7d2968046Jake Hamby    private static String decodeCharset(byte[] data, int offset, int numFields, int width,
1008115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            String charset) throws CodingException
1009115632cc56f813bd21543f8532c763d7d2968046Jake Hamby    {
1010115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        if (numFields < 0 || (numFields * width + offset) > data.length) {
1011115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            // Try to decode the max number of characters in payload
1012115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            int padding = offset % width;
1013115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            int maxNumFields = (data.length - offset - padding) / width;
1014115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            if (maxNumFields < 0) {
1015115632cc56f813bd21543f8532c763d7d2968046Jake Hamby                throw new CodingException(charset + " decode failed: offset out of range");
1016115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            }
1017115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            Log.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
1018115632cc56f813bd21543f8532c763d7d2968046Jake Hamby                    + numFields + " data.length = " + data.length + " maxNumFields = "
1019115632cc56f813bd21543f8532c763d7d2968046Jake Hamby                    + maxNumFields);
1020115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            numFields = maxNumFields;
10210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
10220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
1023115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            return new String(data, offset, numFields * width, charset);
10240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (java.io.UnsupportedEncodingException ex) {
1025115632cc56f813bd21543f8532c763d7d2968046Jake Hamby            throw new CodingException(charset + " decode failed: " + ex);
10260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
10270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
10280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
10290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String decode7bitAscii(byte[] data, int offset, int numFields)
10300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
10310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
10320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
10330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            offset *= 8;
10340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            StringBuffer strBuf = new StringBuffer(numFields);
10350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            BitwiseInputStream inStream = new BitwiseInputStream(data);
10360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int wantedBits = (offset * 8) + (numFields * 7);
10370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (inStream.available() < wantedBits) {
10380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("insufficient data (wanted " + wantedBits +
10390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                          " bits, but only have " + inStream.available() + ")");
10400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
10410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(offset);
10420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i = 0; i < numFields; i++) {
10430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int charCode = inStream.read(7);
10440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
10450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
10460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
10470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else if (charCode == UserData.ASCII_NL_INDEX) {
10480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    strBuf.append('\n');
10490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else if (charCode == UserData.ASCII_CR_INDEX) {
10500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    strBuf.append('\r');
10510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
10520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    /* For other charCodes, they are unprintable, and so simply use SPACE. */
10530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    strBuf.append(' ');
10540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
10550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
10560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return strBuf.toString();
10570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (BitwiseInputStream.AccessException ex) {
10580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("7bit ASCII decode failed: " + ex);
10590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
10600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
10610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
10620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String decode7bitGsm(byte[] data, int offset, int numFields)
10630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
10640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
10650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // Start reading from the next 7-bit aligned boundary after offset.
10660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int offsetBits = offset * 8;
10670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int offsetSeptets = (offsetBits + 6) / 7;
10680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        numFields -= offsetSeptets;
10690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paddingBits = (offsetSeptets * 7) - offsetBits;
10700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
10710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                0, 0);
10720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (result == null) {
10730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("7bit GSM decoding failed");
10740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
10750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return result;
10760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
10770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
10780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String decodeLatin(byte[] data, int offset, int numFields)
10790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
10800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
1081115632cc56f813bd21543f8532c763d7d2968046Jake Hamby        return decodeCharset(data, offset, numFields, 1, "ISO-8859-1");
10820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
10830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
10840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
10850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
10860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
10870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int offset = 0;
10880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (hasUserDataHeader) {
10890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int udhLen = userData.payload[0] & 0x00FF;
10900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            offset += udhLen + 1;
10910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            byte[] headerData = new byte[udhLen];
10920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
10930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.userDataHeader = SmsHeader.fromByteArray(headerData);
10940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
10950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        switch (userData.msgEncoding) {
10960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.ENCODING_OCTET:
10970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            /*
10980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            *  Octet decoding depends on the carrier service.
10990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            */
11000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            boolean decodingtypeUTF8 = Resources.getSystem()
11010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
11020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
11030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // Strip off any padding bytes, meaning any differences between the length of the
11040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // array and the target length specified by numFields.  This is to avoid any
11050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // confusion by code elsewhere that only considers the payload array length.
11060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            byte[] payload = new byte[userData.numFields];
11070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int copyLen = userData.numFields < userData.payload.length
11080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ? userData.numFields : userData.payload.length;
11090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
11100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            System.arraycopy(userData.payload, 0, payload, 0, copyLen);
11110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.payload = payload;
11120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
11130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (!decodingtypeUTF8) {
11140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // There are many devices in the market that send 8bit text sms (latin encoded) as
11150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // octet encoded.
11160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
11170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
11180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
11190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
11200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
11210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
11220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.ENCODING_IA5:
11230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.ENCODING_7BIT_ASCII:
11240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
11250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
11260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.ENCODING_UNICODE_16:
11270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
11280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
11290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.ENCODING_GSM_7BIT_ALPHABET:
11300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
11310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
11320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.ENCODING_LATIN:
11330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
11340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
11350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        default:
11360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("unsupported user data encoding ("
11370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                      + userData.msgEncoding + ")");
11380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
11390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
11400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
11410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
11420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * IS-91 Voice Mail message decoding
11430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
11440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (For character encodings, see TIA/EIA/IS-91, Annex B)
11450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
11460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Protocol Summary: The user data payload may contain 3-14
11470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * characters.  The first two characters are parsed as a number
11480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * and indicate the number of voicemails.  The third character is
11490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * either a SPACE or '!' to indicate normal or urgent priority,
11500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * respectively.  Any following characters are treated as normal
11510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * text user data payload.
11520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
11530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Note that the characters encoding is 6-bit packed.
11540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
11550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeIs91VoicemailStatus(BearerData bData)
11560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
11570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
11580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
11590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
11600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int numFields = bData.userData.numFields;
11610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
11620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("IS-91 voicemail status decoding failed");
11630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
11640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
11650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            StringBuffer strbuf = new StringBuffer(dataLen);
11660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            while (inStream.available() >= 6) {
11670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
11680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
11690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            String data = strbuf.toString();
11700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
11710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            char prioCode = data.charAt(2);
11720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (prioCode == ' ') {
11730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                bData.priority = PRIORITY_NORMAL;
11740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else if (prioCode == '!') {
11750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                bData.priority = PRIORITY_URGENT;
11760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
11770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("IS-91 voicemail status decoding failed: " +
11780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        "illegal priority setting (" + prioCode + ")");
11790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
11800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.priorityIndicatorSet = true;
11810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.userData.payloadStr = data.substring(3, numFields - 3);
11820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville       } catch (java.lang.NumberFormatException ex) {
11830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
11840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (java.lang.IndexOutOfBoundsException ex) {
11850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
11860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
11870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
11880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
11890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
11900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * IS-91 Short Message decoding
11910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
11920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (For character encodings, see TIA/EIA/IS-91, Annex B)
11930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
11940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Protocol Summary: The user data payload may contain 1-14
11950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * characters, which are treated as normal text user data payload.
11960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Note that the characters encoding is 6-bit packed.
11970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
11980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeIs91ShortMessage(BearerData bData)
11990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
12000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
12010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
12020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
12030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int numFields = bData.userData.numFields;
12040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // dataLen may be > 14 characters due to octet padding
12050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((numFields > 14) || (dataLen < numFields)) {
12060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("IS-91 short message decoding failed");
12070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        StringBuffer strbuf = new StringBuffer(dataLen);
12090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0; i < numFields; i++) {
12100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
12110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userData.payloadStr = strbuf.toString();
12130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
12140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
12150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
12160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * IS-91 CLI message (callback number) decoding
12170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
12180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
12190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Protocol Summary: The data payload may contain 1-32 digits,
12200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * encoded using standard 4-bit DTMF, which are treated as a
12210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * callback number.
12220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
12230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeIs91Cli(BearerData bData) throws CodingException {
12240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
12250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
12260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int numFields = bData.userData.numFields;
12270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
12280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("IS-91 voicemail status decoding failed");
12290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        CdmaSmsAddress addr = new CdmaSmsAddress();
12310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
12320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        addr.origBytes = bData.userData.payload;
12330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        addr.numberOfDigits = (byte)numFields;
12340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        decodeSmsAddress(addr);
12350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.callbackNumber = addr;
12360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
12370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
12380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeIs91(BearerData bData)
12390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
12400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
12410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        switch (bData.userData.msgType) {
12420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
12430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeIs91VoicemailStatus(bData);
12440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
12450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.IS91_MSG_TYPE_CLI:
12460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeIs91Cli(bData);
12470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
12480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
12490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
12500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeIs91ShortMessage(bData);
12510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
12520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        default:
12530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("unsupported IS-91 message type (" +
12540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    bData.userData.msgType + ")");
12550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
12570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
12580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
12590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
12600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
12610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
12620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
12630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
12640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
12650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
12660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
12670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.userAckReq     = (inStream.read(1) == 1);
12680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.deliveryAckReq = (inStream.read(1) == 1);
12690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.readAckReq     = (inStream.read(1) == 1);
12700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.reportReq      = (inStream.read(1) == 1);
12710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(4);
12720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
12740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "REPLY_OPTION decode " +
12750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
12760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
12770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
12790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
12800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
12810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
12820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
12830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
12840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
12850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
12860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
12870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
12880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
12890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
12900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
12910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
12920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
12940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
12950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
12960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
12970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
12980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
12990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
13000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
13010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
13020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
13030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
13040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
13050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 2 * 8;
13060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
13070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
13080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
13090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
13100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
13110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
13120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
13140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
13150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
13160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
13170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
13190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
13200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
13210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
13220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
13230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws CodingException
13240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
13250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /* DTMF 4-bit digit encoding, defined in at
13260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
13270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        StringBuffer strBuf = new StringBuffer(numFields);
13280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i = 0; i < numFields; i++) {
13290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
13300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
13310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else if (val == 10) strBuf.append('0');
13320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else if (val == 11) strBuf.append('*');
13330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else if (val == 12) strBuf.append('#');
13340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
13350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return strBuf.toString();
13370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
13380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
13390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
13400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
13410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            try {
13420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
13430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                 * just 7-bit ASCII encoding, with the MSB being zero. */
13440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
13450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } catch (java.io.UnsupportedEncodingException ex) {
13460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("invalid SMS address ASCII code");
13470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
13480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
13490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
13500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
13520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
13530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
13540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
13550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
13560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
13570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        CdmaSmsAddress addr = new CdmaSmsAddress();
13580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        addr.digitMode = inStream.read(1);
13590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        byte fieldBits = 4;
13600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        byte consumedBits = 1;
13610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
13620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            addr.ton = inStream.read(3);
13630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            addr.numberPlan = inStream.read(4);
13640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            fieldBits = 8;
13650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            consumedBits += 7;
13660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        addr.numberOfDigits = inStream.read(8);
13680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        consumedBits += 8;
13690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int remainingBits = paramBits - consumedBits;
13700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int dataBits = addr.numberOfDigits * fieldBits;
13710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paddingBits = remainingBits - dataBits;
13720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (remainingBits < dataBits) {
13730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
13740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                      "remainingBits + " + remainingBits + ", dataBits + " +
13750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                      dataBits + ", paddingBits + " + paddingBits + ")");
13760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        addr.origBytes = inStream.readByteArray(dataBits);
13780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paddingBits);
13790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        decodeSmsAddress(addr);
13800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.callbackNumber = addr;
13810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return true;
13820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
13830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
13840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
13850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
13860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
13870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
13880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
13890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
13900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
13910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
13920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
13930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.errorClass = inStream.read(2);
13940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.messageStatus = inStream.read(6);
13950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
13960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
13970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "MESSAGE_STATUS decode " +
13980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
13990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
14000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
14020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.messageStatusSet = decodeSuccess;
14030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
14040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
14050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
14060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
14070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
14080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
14090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 6 * 8;
14100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
14110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
14120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
14130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
14140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
14150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
14160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
14180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
14190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
14200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
14210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
14230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
14240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
14250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
14260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
14270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
14280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
14290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 6 * 8;
14300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
14310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
14320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
14330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
14340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
14350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
14360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
14380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
14390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
14400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
14410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
14430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
14440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
14450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
14460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
14470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
14480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
14490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 6 * 8;
14500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
14510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
14520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
14530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
14540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
14550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
14560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    inStream.readByteArray(6 * 8));
14570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
14590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
14600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
14610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
14620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
14640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
14650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
14660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
14670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
14680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
14690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
14700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
14710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
14720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
14730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
14740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
14750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
14760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.deferredDeliveryTimeRelative = inStream.read(8);
14770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
14790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
14800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
14810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
14820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
14840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
14850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
14860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
14870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
14880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
14890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
14900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
14910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
14920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
14930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
14940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
14950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
14960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
14970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.validityPeriodRelative = inStream.read(8);
14980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
14990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
15000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
15010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
15020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
15030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
15050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.validityPeriodRelativeSet = decodeSuccess;
15060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
15070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
15080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
15090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
15100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
15110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
15120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
15130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
15140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
15150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
15160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
15170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
15180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.privacy = inStream.read(2);
15190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(6);
15200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
15220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
15230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
15240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
15250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
15270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.privacyIndicatorSet = decodeSuccess;
15280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
15290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
15300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
15310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
15320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
15330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
15340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
15350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
15360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
15370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
15380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
15390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
15400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.language = inStream.read(8);
15410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
15430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
15440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
15450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
15460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
15480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.languageIndicatorSet = decodeSuccess;
15490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
15500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
15510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
15520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
15530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
15540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
15550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
15560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
15570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
15580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
15590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
15600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
15610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.displayMode = inStream.read(2);
15620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(6);
15630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
15650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "DISPLAY_MODE decode " +
15660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
15670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
15680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
15700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.displayModeSet = decodeSuccess;
15710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
15720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
15730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
15740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
15750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
15760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
15770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
15780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
15790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
15800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
15810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
15820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
15830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.priority = inStream.read(2);
15840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(6);
15850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
15870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
15880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
15890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
15900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
15910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
15920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.priorityIndicatorSet = decodeSuccess;
15930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
15940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
15950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
15960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
15970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
15980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
15990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
16000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
16010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
16020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
16030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
16040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
16050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.alert = inStream.read(2);
16060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            inStream.skip(6);
16070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
16090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
16100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
16110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
16120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
16140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.alertIndicatorSet = decodeSuccess;
16150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
16160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
16170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
16190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        throws BitwiseInputStream.AccessException, CodingException
16200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
16210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int EXPECTED_PARAM_SIZE = 1 * 8;
16220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
16230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
16240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (paramBits >= EXPECTED_PARAM_SIZE) {
16250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= EXPECTED_PARAM_SIZE;
16260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
16270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            bData.userResponseCode = inStream.read(8);
16280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
16300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
16310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
16320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ")");
16330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
16350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.userResponseCodeSet = decodeSuccess;
16360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
16370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
16380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean decodeServiceCategoryProgramData(BearerData bData,
16400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException
16410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
16420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (inStream.available() < 13) {
16430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
16440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    + inStream.available() + " bits available");
16450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int paramBits = inStream.read(8) * 8;
16480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int msgEncoding = inStream.read(5);
16490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        paramBits -= 5;
16500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (inStream.available() < paramBits) {
16520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
16530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    + inStream.available() + " bits available (" + paramBits + " bits expected)");
16540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();
16570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
16590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        boolean decodeSuccess = false;
16600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
16610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int operation = inStream.read(4);
16620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int category = (inStream.read(8) << 8) | inStream.read(8);
1663b8a13d300b245e0080aa01275e232f54d5d0e09cJake Hamby            int language = inStream.read(8);
16640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int maxMessages = inStream.read(8);
16650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int alertOption = inStream.read(4);
16660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int numFields = inStream.read(8);
16670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= CATEGORY_FIELD_MIN_SIZE;
16680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int textBits = getBitsForNumFields(msgEncoding, numFields);
16700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (paramBits < textBits) {
16710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("category name is " + textBits + " bits in length,"
16720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        + " but there are only " + paramBits + " bits available");
16730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
16740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            UserData userData = new UserData();
16760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.msgEncoding = msgEncoding;
16770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.msgEncodingSet = true;
16780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.numFields = numFields;
16790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            userData.payload = inStream.readByteArray(textBits);
16800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            paramBits -= textBits;
16810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeUserDataPayload(userData, false);
16830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            String categoryName = userData.payloadStr;
16840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
16850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    language, maxMessages, alertOption, categoryName);
16860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            programDataList.add(programData);
16870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            decodeSuccess = true;
16890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((! decodeSuccess) || (paramBits > 0)) {
16920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
16930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      (decodeSuccess ? "succeeded" : "failed") +
16940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                      " (extra bits = " + paramBits + ')');
16950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
16960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
16970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        inStream.skip(paramBits);
16980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.serviceCategoryProgramData = programDataList;
16990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decodeSuccess;
17000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
17010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static int serviceCategoryToCmasMessageClass(int serviceCategory) {
17030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        switch (serviceCategory) {
17040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
17050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
17060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
17080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
17090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
17110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
17120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
17140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
17150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
17170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
17180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            default:
17200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
17210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
17220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
17230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
17250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Calculates the number of bits to read for the specified number of encoded characters.
17260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param msgEncoding the message encoding to use
17270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param numFields the number of characters to read. For Shift-JIS and Korean encodings,
17280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  this is the number of bytes to read.
17290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return the number of bits to read from the stream
17300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @throws CodingException if the specified encoding is not supported
17310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
17320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static int getBitsForNumFields(int msgEncoding, int numFields)
17330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throws CodingException {
17340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        switch (msgEncoding) {
17350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_OCTET:
17360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_SHIFT_JIS:
17370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_KOREAN:
17380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_LATIN:
17390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_LATIN_HEBREW:
17400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return numFields * 8;
17410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_IA5:
17430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_7BIT_ASCII:
17440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_GSM_7BIT_ALPHABET:
17450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return numFields * 7;
17460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case UserData.ENCODING_UNICODE_16:
17480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return numFields * 16;
17490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            default:
17510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("unsupported message encoding (" + msgEncoding + ')');
17520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
17530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
17540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
17560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * CMAS message decoding.
17570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See TIA-1149-0-1, CMAS over CDMA)
17580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
17590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param serviceCategory is the service category from the SMS envelope
17600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
17610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void decodeCmasUserData(BearerData bData, int serviceCategory)
17620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throws BitwiseInputStream.AccessException, CodingException {
17630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
17640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (inStream.available() < 8) {
17650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("emergency CB with no CMAE_protocol_version");
17660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
17670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int protocolVersion = inStream.read(8);
17680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (protocolVersion != 0) {
17690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
17700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
17710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
17730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
17740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
17750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
17760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
17770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
17780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        while (inStream.available() >= 16) {
17800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int recordType = inStream.read(8);
17810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int recordLen = inStream.read(8);
17820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            switch (recordType) {
17830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case 0:     // Type 0 elements (Alert text)
17840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    UserData alertUserData = new UserData();
17850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    alertUserData.msgEncoding = inStream.read(5);
17860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    alertUserData.msgEncodingSet = true;
17870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    alertUserData.msgType = 0;
17880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    int numFields;                          // number of chars to decode
17900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    switch (alertUserData.msgEncoding) {
17910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        case UserData.ENCODING_OCTET:
17920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        case UserData.ENCODING_LATIN:
17930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            numFields = recordLen - 1;      // subtract 1 byte for encoding
17940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            break;
17950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
17960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        case UserData.ENCODING_IA5:
17970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        case UserData.ENCODING_7BIT_ASCII:
17980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        case UserData.ENCODING_GSM_7BIT_ALPHABET:
17990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            numFields = ((recordLen * 8) - 5) / 7;  // subtract 5 bits for encoding
18000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            break;
18010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        case UserData.ENCODING_UNICODE_16:
18030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            numFields = (recordLen - 1) / 2;
18040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            break;
18050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        default:
18070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            numFields = 0;      // unsupported encoding
18080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
18090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    alertUserData.numFields = numFields;
18110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
18120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeUserDataPayload(alertUserData, false);
18130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    bData.userData = alertUserData;
18140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case 1:     // Type 1 elements
18170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    category = inStream.read(8);
18180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    responseType = inStream.read(8);
18190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    severity = inStream.read(4);
18200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    urgency = inStream.read(4);
18210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    certainty = inStream.read(4);
18220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    inStream.skip(recordLen * 8 - 28);
18230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                default:
18260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Log.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
18270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    inStream.skip(recordLen * 8);
18280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
18300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
18310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
18330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                urgency, certainty);
18340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
18350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
18370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Create BearerData object from serialized representation.
18380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
18390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
18400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param smsData byte array of raw encoded SMS bearer data.
18410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return an instance of BearerData.
18420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
18430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static BearerData decode(byte[] smsData) {
18440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return decode(smsData, 0);
18450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
18460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static boolean isCmasAlertCategory(int category) {
18480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
18490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
18500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
18510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
18520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
18530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Create BearerData object from serialized representation.
18540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
18550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
18560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param smsData byte array of raw encoded SMS bearer data.
18570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param serviceCategory the envelope service category (for CMAS alert handling)
18580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return an instance of BearerData.
18590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
18600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static BearerData decode(byte[] smsData, int serviceCategory) {
18610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
18620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            BitwiseInputStream inStream = new BitwiseInputStream(smsData);
18630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            BearerData bData = new BearerData();
18640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int foundSubparamMask = 0;
18650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            while (inStream.available() > 0) {
18660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int subparamId = inStream.read(8);
18670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int subparamIdBit = 1 << subparamId;
18680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if ((foundSubparamMask & subparamIdBit) != 0) {
18690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    throw new CodingException("illegal duplicate subparameter (" +
18700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                              subparamId + ")");
18710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
18720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                boolean decodeSuccess;
18730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                switch (subparamId) {
18740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_MESSAGE_IDENTIFIER:
18750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeMessageId(bData, inStream);
18760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_USER_DATA:
18780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeUserData(bData, inStream);
18790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_USER_RESPONSE_CODE:
18810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeUserResponseCode(bData, inStream);
18820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_REPLY_OPTION:
18840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeReplyOption(bData, inStream);
18850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_NUMBER_OF_MESSAGES:
18870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeMsgCount(bData, inStream);
18880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_CALLBACK_NUMBER:
18900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeCallbackNumber(bData, inStream);
18910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_MESSAGE_STATUS:
18930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeMsgStatus(bData, inStream);
18940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
18960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
18970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
18980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
18990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeValidityAbs(bData, inStream);
19000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
19020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeValidityRel(bData, inStream);
19030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
19050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
19060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
19080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
19090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_PRIVACY_INDICATOR:
19110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
19120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_LANGUAGE_INDICATOR:
19140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
19150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_MESSAGE_DISPLAY_MODE:
19170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeDisplayMode(bData, inStream);
19180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_PRIORITY_INDICATOR:
19200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodePriorityIndicator(bData, inStream);
19210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
19230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
19240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
19260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeDepositIndex(bData, inStream);
19270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
19290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
19300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
19310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                default:
19320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    throw new CodingException("unsupported bearer data subparameter ("
19330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                              + subparamId + ")");
19340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
19350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (decodeSuccess) foundSubparamMask |= subparamIdBit;
19360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
19370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
19380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
19390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
19400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (bData.userData != null) {
19410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (isCmasAlertCategory(serviceCategory)) {
19420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeCmasUserData(bData, serviceCategory);
19430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
19440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if ((foundSubparamMask ^
19450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
19460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                             (1 << SUBPARAM_USER_DATA))
19470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            != 0) {
19480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
19490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                              foundSubparamMask + ")");
19500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
19510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeIs91(bData);
19520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
19530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
19540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
19550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
19560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return bData;
19570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (BitwiseInputStream.AccessException ex) {
19580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(LOG_TAG, "BearerData decode failed: " + ex);
19590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (CodingException ex) {
19600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(LOG_TAG, "BearerData decode failed: " + ex);
19610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
19620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;
19630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
19640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
1965