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