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