FileDocCategorySizeDatePackage
BearerData.javaAPI DocAndroid 5.1 API87815Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony.cdma.sms

BearerData

public final class BearerData extends Object
An object to encode and decode CDMA SMS bearer data.

Fields Summary
private static final String
LOG_TAG
private static final byte
SUBPARAM_MESSAGE_IDENTIFIER
Bearer Data Subparameter Identifiers (See 3GPP2 C.S0015-B, v2.0, table 4.5-1) NOTE: Commented subparameter types are not implemented.
private static final byte
SUBPARAM_USER_DATA
private static final byte
SUBPARAM_USER_RESPONSE_CODE
private static final byte
SUBPARAM_MESSAGE_CENTER_TIME_STAMP
private static final byte
SUBPARAM_VALIDITY_PERIOD_ABSOLUTE
private static final byte
SUBPARAM_VALIDITY_PERIOD_RELATIVE
private static final byte
SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE
private static final byte
SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE
private static final byte
SUBPARAM_PRIORITY_INDICATOR
private static final byte
SUBPARAM_PRIVACY_INDICATOR
private static final byte
SUBPARAM_REPLY_OPTION
private static final byte
SUBPARAM_NUMBER_OF_MESSAGES
private static final byte
SUBPARAM_ALERT_ON_MESSAGE_DELIVERY
private static final byte
SUBPARAM_LANGUAGE_INDICATOR
private static final byte
SUBPARAM_CALLBACK_NUMBER
private static final byte
SUBPARAM_MESSAGE_DISPLAY_MODE
private static final byte
SUBPARAM_MESSAGE_DEPOSIT_INDEX
private static final byte
SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA
private static final byte
SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS
private static final byte
SUBPARAM_MESSAGE_STATUS
private static final byte
SUBPARAM_ID_LAST_DEFINED
public static final int
MESSAGE_TYPE_DELIVER
Supported message types for CDMA SMS messages (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
public static final int
MESSAGE_TYPE_SUBMIT
public static final int
MESSAGE_TYPE_CANCELLATION
public static final int
MESSAGE_TYPE_DELIVERY_ACK
public static final int
MESSAGE_TYPE_USER_ACK
public static final int
MESSAGE_TYPE_READ_ACK
public static final int
MESSAGE_TYPE_DELIVER_REPORT
public static final int
MESSAGE_TYPE_SUBMIT_REPORT
public int
messageType
public int
messageId
16-bit value indicating the message ID, which increments modulo 65536. (Special rules apply for WAP-messages.) (See 3GPP2 C.S0015-B, v2, 4.5.1)
public static final int
PRIORITY_NORMAL
Supported priority modes for CDMA SMS messages (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
public static final int
PRIORITY_INTERACTIVE
public static final int
PRIORITY_URGENT
public static final int
PRIORITY_EMERGENCY
public boolean
priorityIndicatorSet
public int
priority
public static final int
PRIVACY_NOT_RESTRICTED
Supported privacy modes for CDMA SMS messages (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1)
public static final int
PRIVACY_RESTRICTED
public static final int
PRIVACY_CONFIDENTIAL
public static final int
PRIVACY_SECRET
public boolean
privacyIndicatorSet
public int
privacy
public static final int
ALERT_DEFAULT
Supported alert priority modes for CDMA SMS messages (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1)
public static final int
ALERT_LOW_PRIO
public static final int
ALERT_MEDIUM_PRIO
public static final int
ALERT_HIGH_PRIO
public boolean
alertIndicatorSet
public int
alert
public static final int
DISPLAY_MODE_IMMEDIATE
Supported display modes for CDMA SMS messages. Display mode is a 2-bit value used to indicate to the mobile station when to display the received message. (See 3GPP2 C.S0015-B, v2, 4.5.16)
public static final int
DISPLAY_MODE_DEFAULT
public static final int
DISPLAY_MODE_USER
public boolean
displayModeSet
public int
displayMode
public static final int
LANGUAGE_UNKNOWN
Language Indicator values. NOTE: the spec (3GPP2 C.S0015-B, v2, 4.5.14) is ambiguous as to the meaning of this field, as it refers to C.R1001-D but that reference has been crossed out. It would seem reasonable to assume the values from C.R1001-F (table 9.2-1) are to be used instead.
public static final int
LANGUAGE_ENGLISH
public static final int
LANGUAGE_FRENCH
public static final int
LANGUAGE_SPANISH
public static final int
LANGUAGE_JAPANESE
public static final int
LANGUAGE_KOREAN
public static final int
LANGUAGE_CHINESE
public static final int
LANGUAGE_HEBREW
public boolean
languageIndicatorSet
public int
language
public static final int
ERROR_NONE
SMS Message Status Codes. The first component of the Message status indicates if an error has occurred and whether the error is considered permanent or temporary. The second component of the Message status indicates the cause of the error (if any). (See 3GPP2 C.S0015-B, v2.0, 4.5.21)
public static final int
STATUS_ACCEPTED
public static final int
STATUS_DEPOSITED_TO_INTERNET
public static final int
STATUS_DELIVERED
public static final int
STATUS_CANCELLED
public static final int
ERROR_TEMPORARY
public static final int
STATUS_NETWORK_CONGESTION
public static final int
STATUS_NETWORK_ERROR
public static final int
STATUS_UNKNOWN_ERROR
public static final int
ERROR_PERMANENT
public static final int
STATUS_CANCEL_FAILED
public static final int
STATUS_BLOCKED_DESTINATION
public static final int
STATUS_TEXT_TOO_LONG
public static final int
STATUS_DUPLICATE_MESSAGE
public static final int
STATUS_INVALID_DESTINATION
public static final int
STATUS_MESSAGE_EXPIRED
public static final int
ERROR_UNDEFINED
public static final int
STATUS_UNDEFINED
public boolean
messageStatusSet
public int
errorClass
public int
messageStatus
public boolean
hasUserDataHeader
1-bit value that indicates whether a User Data Header (UDH) is present. (See 3GPP2 C.S0015-B, v2, 4.5.1) NOTE: during encoding, this value will be set based on the presence of a UDH in the structured data, any existing setting will be overwritten.
public UserData
userData
provides the information for the user data (e.g. padding bits, user data, user data header, etc) (See 3GPP2 C.S.0015-B, v2, 4.5.2)
public boolean
userResponseCodeSet
The User Response Code subparameter is used in the SMS User Acknowledgment Message to respond to previously received short messages. This message center-specific element carries the identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2, 4.5.3)
public int
userResponseCode
public TimeStamp
msgCenterTimeStamp
public TimeStamp
validityPeriodAbsolute
public TimeStamp
deferredDeliveryTimeAbsolute
public static final int
RELATIVE_TIME_MINS_LIMIT
Relative time is specified as one byte, the value of which falls into a series of ranges, as specified below. The idea is that shorter time intervals allow greater precision -- the value means minutes from zero until the MINS_LIMIT (inclusive), upon which it means hours until the HOURS_LIMIT, and so forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1)
public static final int
RELATIVE_TIME_HOURS_LIMIT
public static final int
RELATIVE_TIME_DAYS_LIMIT
public static final int
RELATIVE_TIME_WEEKS_LIMIT
public static final int
RELATIVE_TIME_INDEFINITE
public static final int
RELATIVE_TIME_NOW
public static final int
RELATIVE_TIME_MOBILE_INACTIVE
public static final int
RELATIVE_TIME_RESERVED
public boolean
validityPeriodRelativeSet
public int
validityPeriodRelative
public boolean
deferredDeliveryTimeRelativeSet
public int
deferredDeliveryTimeRelative
public boolean
userAckReq
The Reply Option subparameter contains 1-bit values which indicate whether SMS acknowledgment is requested or not. (See 3GPP2 C.S0015-B, v2, 4.5.11)
public boolean
deliveryAckReq
public boolean
readAckReq
public boolean
reportReq
public int
numberOfMessages
The Number of Messages subparameter (8-bit value) is a decimal number in the 0 to 99 range representing the number of messages stored at the Voice Mail System. This element is used by the Voice Mail Notification service. (See 3GPP2 C.S0015-B, v2, 4.5.12)
public int
depositIndex
The Message Deposit Index subparameter is assigned by the message center as a unique index to the contents of the User Data subparameter in each message sent to a particular mobile station. The mobile station, when replying to a previously received short message which included a Message Deposit Index subparameter, may include the Message Deposit Index of the received message to indicate to the message center that the original contents of the message are to be included in the reply. (See 3GPP2 C.S0015-B, v2, 4.5.18)
public CdmaSmsAddress
callbackNumber
4-bit or 8-bit value that indicates the number to be dialed in reply to a received SMS message. (See 3GPP2 C.S0015-B, v2, 4.5.15)
public android.telephony.SmsCbCmasInfo
cmasWarningInfo
CMAS warning notification information.
public ArrayList
serviceCategoryProgramData
The Service Category Program Data subparameter is used to enable and disable SMS broadcast service categories to display. If this subparameter is present, this field will contain a list of one or more {@link android.telephony.cdma.CdmaSmsCbProgramData} objects containing the operation(s) to perform.
public ArrayList
serviceCategoryProgramResults
The Service Category Program Results subparameter informs the message center of the results of a Service Category Program Data request.
Constructors Summary
Methods Summary
public static com.android.internal.telephony.GsmAlphabet.TextEncodingDetailscalcTextEncodingDetails(java.lang.CharSequence msg, boolean force7BitEncoding, boolean isEntireMsg)
Calculate the message text encoding length, fragmentation, and other details.

param
msg message text
param
force7BitEncoding ignore (but still count) illegal characters if true
param
isEntireMsg indicates if this is entire msg or a segment in multipart msg
return
septet count, or -1 on failure

        TextEncodingDetails ted;
        int septets = countAsciiSeptets(msg, force7BitEncoding);
        if (septets != -1 && septets <= SmsConstants.MAX_USER_DATA_SEPTETS) {
            ted = new TextEncodingDetails();
            ted.msgCount = 1;
            ted.codeUnitCount = septets;
            ted.codeUnitsRemaining = SmsConstants.MAX_USER_DATA_SEPTETS - septets;
            ted.codeUnitSize = SmsConstants.ENCODING_7BIT;
        } else {
            ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
                    msg, force7BitEncoding);
            if (ted.msgCount == 1 && ted.codeUnitSize == SmsConstants.ENCODING_7BIT &&
                    isEntireMsg) {
                // We don't support single-segment EMS, so calculate for 16-bit
                // TODO: Consider supporting single-segment EMS
                ted.codeUnitCount = msg.length();
                int octets = ted.codeUnitCount * 2;
                if (octets > SmsConstants.MAX_USER_DATA_BYTES) {
                    // If EMS is not supported, break down EMS into single segment SMS
                    // and add page info " x/y".
                    // In the case of UCS2 encoding type, we need 8 bytes for this
                    // but we only have 6 bytes from UDH, so truncate the limit for
                    // each segment by 2 bytes (1 char).
                    int max_user_data_bytes_with_header =
                            SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
                    if (!android.telephony.SmsMessage.hasEmsSupport()) {
                        // make sure total number of segments is less than 10
                        if (octets <= 9 * (max_user_data_bytes_with_header - 2))
                            max_user_data_bytes_with_header -= 2;
                    }

                    ted.msgCount = (octets + (max_user_data_bytes_with_header - 1)) /
                            max_user_data_bytes_with_header;
                    ted.codeUnitsRemaining = ((ted.msgCount *
                            max_user_data_bytes_with_header) - octets) / 2;
                } else {
                    ted.msgCount = 1;
                    ted.codeUnitsRemaining = (SmsConstants.MAX_USER_DATA_BYTES - octets)/2;
                }
                ted.codeUnitSize = SmsConstants.ENCODING_16BIT;
            }
        }
        return ted;
    
private static intcountAsciiSeptets(java.lang.CharSequence msg, boolean force)

        int msgLen = msg.length();
        if (force) return msgLen;
        for (int i = 0; i < msgLen; i++) {
            if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
                return -1;
            }
        }
        return msgLen;
    
public static com.android.internal.telephony.cdma.sms.BearerDatadecode(byte[] smsData)
Create BearerData object from serialized representation. (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)

param
smsData byte array of raw encoded SMS bearer data.
return
an instance of BearerData.

        return decode(smsData, 0);
    
public static com.android.internal.telephony.cdma.sms.BearerDatadecode(byte[] smsData, int serviceCategory)
Create BearerData object from serialized representation. (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)

param
smsData byte array of raw encoded SMS bearer data.
param
serviceCategory the envelope service category (for CMAS alert handling)
return
an instance of BearerData.

        try {
            BitwiseInputStream inStream = new BitwiseInputStream(smsData);
            BearerData bData = new BearerData();
            int foundSubparamMask = 0;
            while (inStream.available() > 0) {
                int subparamId = inStream.read(8);
                int subparamIdBit = 1 << subparamId;
                // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8)
                // as 32th bit is the max bit in int.
                // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers:
                // last defined subparam ID is 23 (00010111 = 0x17 = 23).
                // Only do duplicate subparam ID check if subparam is within defined value as
                // reserved subparams are just skipped.
                if ((foundSubparamMask & subparamIdBit) != 0 &&
                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
                    throw new CodingException("illegal duplicate subparameter (" +
                                              subparamId + ")");
                }
                boolean decodeSuccess;
                switch (subparamId) {
                case SUBPARAM_MESSAGE_IDENTIFIER:
                    decodeSuccess = decodeMessageId(bData, inStream);
                    break;
                case SUBPARAM_USER_DATA:
                    decodeSuccess = decodeUserData(bData, inStream);
                    break;
                case SUBPARAM_USER_RESPONSE_CODE:
                    decodeSuccess = decodeUserResponseCode(bData, inStream);
                    break;
                case SUBPARAM_REPLY_OPTION:
                    decodeSuccess = decodeReplyOption(bData, inStream);
                    break;
                case SUBPARAM_NUMBER_OF_MESSAGES:
                    decodeSuccess = decodeMsgCount(bData, inStream);
                    break;
                case SUBPARAM_CALLBACK_NUMBER:
                    decodeSuccess = decodeCallbackNumber(bData, inStream);
                    break;
                case SUBPARAM_MESSAGE_STATUS:
                    decodeSuccess = decodeMsgStatus(bData, inStream);
                    break;
                case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
                    break;
                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
                    decodeSuccess = decodeValidityAbs(bData, inStream);
                    break;
                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
                    decodeSuccess = decodeValidityRel(bData, inStream);
                    break;
                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
                    break;
                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
                    break;
                case SUBPARAM_PRIVACY_INDICATOR:
                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
                    break;
                case SUBPARAM_LANGUAGE_INDICATOR:
                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
                    break;
                case SUBPARAM_MESSAGE_DISPLAY_MODE:
                    decodeSuccess = decodeDisplayMode(bData, inStream);
                    break;
                case SUBPARAM_PRIORITY_INDICATOR:
                    decodeSuccess = decodePriorityIndicator(bData, inStream);
                    break;
                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
                    break;
                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
                    decodeSuccess = decodeDepositIndex(bData, inStream);
                    break;
                case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
                    decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
                    break;
                default:
                    decodeSuccess = decodeReserved(bData, inStream, subparamId);
                }
                if (decodeSuccess &&
                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
                    foundSubparamMask |= subparamIdBit;
                }
            }
            if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
                throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
            }
            if (bData.userData != null) {
                if (isCmasAlertCategory(serviceCategory)) {
                    decodeCmasUserData(bData, serviceCategory);
                } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
                    if ((foundSubparamMask ^
                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
                             (1 << SUBPARAM_USER_DATA))
                            != 0) {
                        Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
                              foundSubparamMask + ")");
                    }
                    decodeIs91(bData);
                } else {
                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
                }
            }
            return bData;
        } catch (BitwiseInputStream.AccessException ex) {
            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
        } catch (CodingException ex) {
            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
        }
        return null;
    
private static java.lang.Stringdecode7bitAscii(byte[] data, int offset, int numFields)

        try {
            offset *= 8;
            StringBuffer strBuf = new StringBuffer(numFields);
            BitwiseInputStream inStream = new BitwiseInputStream(data);
            int wantedBits = (offset * 8) + (numFields * 7);
            if (inStream.available() < wantedBits) {
                throw new CodingException("insufficient data (wanted " + wantedBits +
                                          " bits, but only have " + inStream.available() + ")");
            }
            inStream.skip(offset);
            for (int i = 0; i < numFields; i++) {
                int charCode = inStream.read(7);
                if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
                        (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
                    strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
                } else if (charCode == UserData.ASCII_NL_INDEX) {
                    strBuf.append('\n");
                } else if (charCode == UserData.ASCII_CR_INDEX) {
                    strBuf.append('\r");
                } else {
                    /* For other charCodes, they are unprintable, and so simply use SPACE. */
                    strBuf.append(' ");
                }
            }
            return strBuf.toString();
        } catch (BitwiseInputStream.AccessException ex) {
            throw new CodingException("7bit ASCII decode failed: " + ex);
        }
    
private static java.lang.Stringdecode7bitGsm(byte[] data, int offset, int numFields)

        // Start reading from the next 7-bit aligned boundary after offset.
        int offsetBits = offset * 8;
        int offsetSeptets = (offsetBits + 6) / 7;
        numFields -= offsetSeptets;
        int paddingBits = (offsetSeptets * 7) - offsetBits;
        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
                0, 0);
        if (result == null) {
            throw new CodingException("7bit GSM decoding failed");
        }
        return result;
    
private static booleandecodeCallbackNumber(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8; //at least
        int paramBits = inStream.read(8) * 8;
        if (paramBits < EXPECTED_PARAM_SIZE) {
            inStream.skip(paramBits);
            return false;
        }
        CdmaSmsAddress addr = new CdmaSmsAddress();
        addr.digitMode = inStream.read(1);
        byte fieldBits = 4;
        byte consumedBits = 1;
        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
            addr.ton = inStream.read(3);
            addr.numberPlan = inStream.read(4);
            fieldBits = 8;
            consumedBits += 7;
        }
        addr.numberOfDigits = inStream.read(8);
        consumedBits += 8;
        int remainingBits = paramBits - consumedBits;
        int dataBits = addr.numberOfDigits * fieldBits;
        int paddingBits = remainingBits - dataBits;
        if (remainingBits < dataBits) {
            throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
                                      "remainingBits + " + remainingBits + ", dataBits + " +
                                      dataBits + ", paddingBits + " + paddingBits + ")");
        }
        addr.origBytes = inStream.readByteArray(dataBits);
        inStream.skip(paddingBits);
        decodeSmsAddress(addr);
        bData.callbackNumber = addr;
        return true;
    
private static java.lang.StringdecodeCharset(byte[] data, int offset, int numFields, int width, java.lang.String charset)

        if (numFields < 0 || (numFields * width + offset) > data.length) {
            // Try to decode the max number of characters in payload
            int padding = offset % width;
            int maxNumFields = (data.length - offset - padding) / width;
            if (maxNumFields < 0) {
                throw new CodingException(charset + " decode failed: offset out of range");
            }
            Rlog.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
                    + numFields + " data.length = " + data.length + " maxNumFields = "
                    + maxNumFields);
            numFields = maxNumFields;
        }
        try {
            return new String(data, offset, numFields * width, charset);
        } catch (java.io.UnsupportedEncodingException ex) {
            throw new CodingException(charset + " decode failed: " + ex);
        }
    
private static voiddecodeCmasUserData(com.android.internal.telephony.cdma.sms.BearerData bData, int serviceCategory)
CMAS message decoding. (See TIA-1149-0-1, CMAS over CDMA)

param
serviceCategory is the service category from the SMS envelope

        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
        if (inStream.available() < 8) {
            throw new CodingException("emergency CB with no CMAE_protocol_version");
        }
        int protocolVersion = inStream.read(8);
        if (protocolVersion != 0) {
            throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
        }

        int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
        int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
        int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
        int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
        int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
        int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;

        while (inStream.available() >= 16) {
            int recordType = inStream.read(8);
            int recordLen = inStream.read(8);
            switch (recordType) {
                case 0:     // Type 0 elements (Alert text)
                    UserData alertUserData = new UserData();
                    alertUserData.msgEncoding = inStream.read(5);
                    alertUserData.msgEncodingSet = true;
                    alertUserData.msgType = 0;

                    int numFields;                          // number of chars to decode
                    switch (alertUserData.msgEncoding) {
                        case UserData.ENCODING_OCTET:
                        case UserData.ENCODING_LATIN:
                            numFields = recordLen - 1;      // subtract 1 byte for encoding
                            break;

                        case UserData.ENCODING_IA5:
                        case UserData.ENCODING_7BIT_ASCII:
                        case UserData.ENCODING_GSM_7BIT_ALPHABET:
                            numFields = ((recordLen * 8) - 5) / 7;  // subtract 5 bits for encoding
                            break;

                        case UserData.ENCODING_UNICODE_16:
                            numFields = (recordLen - 1) / 2;
                            break;

                        default:
                            numFields = 0;      // unsupported encoding
                    }

                    alertUserData.numFields = numFields;
                    alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
                    decodeUserDataPayload(alertUserData, false);
                    bData.userData = alertUserData;
                    break;

                case 1:     // Type 1 elements
                    category = inStream.read(8);
                    responseType = inStream.read(8);
                    severity = inStream.read(4);
                    urgency = inStream.read(4);
                    certainty = inStream.read(4);
                    inStream.skip(recordLen * 8 - 28);
                    break;

                default:
                    Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
                    inStream.skip(recordLen * 8);
                    break;
            }
        }

        bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
                urgency, certainty);
    
private static booleandecodeDeferredDeliveryAbs(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 6 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
                    inStream.readByteArray(6 * 8));
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeDeferredDeliveryRel(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.validityPeriodRelative = inStream.read(8);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.validityPeriodRelativeSet = decodeSuccess;
        return decodeSuccess;
    
private static booleandecodeDepositIndex(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 2 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeDisplayMode(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.displayMode = inStream.read(2);
            inStream.skip(6);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.displayModeSet = decodeSuccess;
        return decodeSuccess;
    
private static java.lang.StringdecodeDtmfSmsAddress(byte[] rawData, int numFields)

        /* DTMF 4-bit digit encoding, defined in at
         * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
        StringBuffer strBuf = new StringBuffer(numFields);
        for (int i = 0; i < numFields; i++) {
            int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
            if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
            else if (val == 10) strBuf.append('0");
            else if (val == 11) strBuf.append('*");
            else if (val == 12) strBuf.append('#");
            else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
        }
        return strBuf.toString();
    
private static voiddecodeIs91(com.android.internal.telephony.cdma.sms.BearerData bData)

        switch (bData.userData.msgType) {
        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
            decodeIs91VoicemailStatus(bData);
            break;
        case UserData.IS91_MSG_TYPE_CLI:
            decodeIs91Cli(bData);
            break;
        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
            decodeIs91ShortMessage(bData);
            break;
        default:
            throw new CodingException("unsupported IS-91 message type (" +
                    bData.userData.msgType + ")");
        }
    
private static voiddecodeIs91Cli(com.android.internal.telephony.cdma.sms.BearerData bData)
IS-91 CLI message (callback number) decoding (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) Protocol Summary: The data payload may contain 1-32 digits, encoded using standard 4-bit DTMF, which are treated as a callback number.

        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
        int numFields = bData.userData.numFields;
        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
            throw new CodingException("IS-91 voicemail status decoding failed");
        }
        CdmaSmsAddress addr = new CdmaSmsAddress();
        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
        addr.origBytes = bData.userData.payload;
        addr.numberOfDigits = (byte)numFields;
        decodeSmsAddress(addr);
        bData.callbackNumber = addr;
    
private static voiddecodeIs91ShortMessage(com.android.internal.telephony.cdma.sms.BearerData bData)
IS-91 Short Message decoding (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) (For character encodings, see TIA/EIA/IS-91, Annex B) Protocol Summary: The user data payload may contain 1-14 characters, which are treated as normal text user data payload. Note that the characters encoding is 6-bit packed.

        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
        int numFields = bData.userData.numFields;
        // dataLen may be > 14 characters due to octet padding
        if ((numFields > 14) || (dataLen < numFields)) {
            throw new CodingException("IS-91 short message decoding failed");
        }
        StringBuffer strbuf = new StringBuffer(dataLen);
        for (int i = 0; i < numFields; i++) {
            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
        }
        bData.userData.payloadStr = strbuf.toString();
    
private static voiddecodeIs91VoicemailStatus(com.android.internal.telephony.cdma.sms.BearerData bData)
IS-91 Voice Mail message decoding (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1) (For character encodings, see TIA/EIA/IS-91, Annex B) Protocol Summary: The user data payload may contain 3-14 characters. The first two characters are parsed as a number and indicate the number of voicemails. The third character is either a SPACE or '!' to indicate normal or urgent priority, respectively. Any following characters are treated as normal text user data payload. Note that the characters encoding is 6-bit packed.

        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
        int numFields = bData.userData.numFields;
        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
            throw new CodingException("IS-91 voicemail status decoding failed");
        }
        try {
            StringBuffer strbuf = new StringBuffer(dataLen);
            while (inStream.available() >= 6) {
                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
            }
            String data = strbuf.toString();
            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
            char prioCode = data.charAt(2);
            if (prioCode == ' ") {
                bData.priority = PRIORITY_NORMAL;
            } else if (prioCode == '!") {
                bData.priority = PRIORITY_URGENT;
            } else {
                throw new CodingException("IS-91 voicemail status decoding failed: " +
                        "illegal priority setting (" + prioCode + ")");
            }
            bData.priorityIndicatorSet = true;
            bData.userData.payloadStr = data.substring(3, numFields - 3);
       } catch (java.lang.NumberFormatException ex) {
            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
        } catch (java.lang.IndexOutOfBoundsException ex) {
            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
        }
    
private static booleandecodeLanguageIndicator(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.language = inStream.read(8);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.languageIndicatorSet = decodeSuccess;
        return decodeSuccess;
    
private static java.lang.StringdecodeLatin(byte[] data, int offset, int numFields)

        return decodeCharset(data, offset, numFields, 1, "ISO-8859-1");
    
private static booleandecodeMessageId(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 3 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.messageType = inStream.read(4);
            bData.messageId = inStream.read(8) << 8;
            bData.messageId |= inStream.read(8);
            bData.hasUserDataHeader = (inStream.read(1) == 1);
            inStream.skip(3);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeMsgCenterTimeStamp(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 6 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeMsgCount(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeMsgDeliveryAlert(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.alert = inStream.read(2);
            inStream.skip(6);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.alertIndicatorSet = decodeSuccess;
        return decodeSuccess;
    
private static booleandecodeMsgStatus(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.errorClass = inStream.read(2);
            bData.messageStatus = inStream.read(6);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.messageStatusSet = decodeSuccess;
        return decodeSuccess;
    
private static booleandecodePriorityIndicator(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.priority = inStream.read(2);
            inStream.skip(6);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.priorityIndicatorSet = decodeSuccess;
        return decodeSuccess;
    
private static booleandecodePrivacyIndicator(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.privacy = inStream.read(2);
            inStream.skip(6);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.privacyIndicatorSet = decodeSuccess;
        return decodeSuccess;
    
private static booleandecodeReplyOption(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.userAckReq     = (inStream.read(1) == 1);
            bData.deliveryAckReq = (inStream.read(1) == 1);
            bData.readAckReq     = (inStream.read(1) == 1);
            bData.reportReq      = (inStream.read(1) == 1);
            inStream.skip(4);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeReserved(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream, int subparamId)

        boolean decodeSuccess = false;
        int subparamLen = inStream.read(8); // SUBPARAM_LEN
        int paramBits = subparamLen * 8;
        if (paramBits <= inStream.available()) {
            decodeSuccess = true;
            inStream.skip(paramBits);
        }
        Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode "
                + (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")");
        if (!decodeSuccess) {
            throw new CodingException("RESERVED bearer data subparameter " + subparamId
                    + " had invalid SUBPARAM_LEN " + subparamLen);
        }

        return decodeSuccess;
    
private static booleandecodeServiceCategoryProgramData(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        if (inStream.available() < 13) {
            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
                    + inStream.available() + " bits available");
        }

        int paramBits = inStream.read(8) * 8;
        int msgEncoding = inStream.read(5);
        paramBits -= 5;

        if (inStream.available() < paramBits) {
            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
                    + inStream.available() + " bits available (" + paramBits + " bits expected)");
        }

        ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();

        final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
        boolean decodeSuccess = false;
        while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
            int operation = inStream.read(4);
            int category = (inStream.read(8) << 8) | inStream.read(8);
            int language = inStream.read(8);
            int maxMessages = inStream.read(8);
            int alertOption = inStream.read(4);
            int numFields = inStream.read(8);
            paramBits -= CATEGORY_FIELD_MIN_SIZE;

            int textBits = getBitsForNumFields(msgEncoding, numFields);
            if (paramBits < textBits) {
                throw new CodingException("category name is " + textBits + " bits in length,"
                        + " but there are only " + paramBits + " bits available");
            }

            UserData userData = new UserData();
            userData.msgEncoding = msgEncoding;
            userData.msgEncodingSet = true;
            userData.numFields = numFields;
            userData.payload = inStream.readByteArray(textBits);
            paramBits -= textBits;

            decodeUserDataPayload(userData, false);
            String categoryName = userData.payloadStr;
            CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
                    language, maxMessages, alertOption, categoryName);
            programDataList.add(programData);

            decodeSuccess = true;
        }

        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ')");
        }

        inStream.skip(paramBits);
        bData.serviceCategoryProgramData = programDataList;
        return decodeSuccess;
    
private static java.lang.StringdecodeShiftJis(byte[] data, int offset, int numFields)

        return decodeCharset(data, offset, numFields, 1, "Shift_JIS");
    
private static voiddecodeSmsAddress(CdmaSmsAddress addr)

        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
            try {
                /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
                 * just 7-bit ASCII encoding, with the MSB being zero. */
                addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
            } catch (java.io.UnsupportedEncodingException ex) {
                throw new CodingException("invalid SMS address ASCII code");
            }
        } else {
            addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
        }
    
private static booleandecodeUserData(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        int paramBits = inStream.read(8) * 8;
        bData.userData = new UserData();
        bData.userData.msgEncoding = inStream.read(5);
        bData.userData.msgEncodingSet = true;
        bData.userData.msgType = 0;
        int consumedBits = 5;
        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
            bData.userData.msgType = inStream.read(8);
            consumedBits += 8;
        }
        bData.userData.numFields = inStream.read(8);
        consumedBits += 8;
        int dataBits = paramBits - consumedBits;
        bData.userData.payload = inStream.readByteArray(dataBits);
        return true;
    
private static voiddecodeUserDataPayload(UserData userData, boolean hasUserDataHeader)

        int offset = 0;
        if (hasUserDataHeader) {
            int udhLen = userData.payload[0] & 0x00FF;
            offset += udhLen + 1;
            byte[] headerData = new byte[udhLen];
            System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
            userData.userDataHeader = SmsHeader.fromByteArray(headerData);
        }
        switch (userData.msgEncoding) {
        case UserData.ENCODING_OCTET:
            /*
            *  Octet decoding depends on the carrier service.
            */
            boolean decodingtypeUTF8 = Resources.getSystem()
                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);

            // Strip off any padding bytes, meaning any differences between the length of the
            // array and the target length specified by numFields.  This is to avoid any
            // confusion by code elsewhere that only considers the payload array length.
            byte[] payload = new byte[userData.numFields];
            int copyLen = userData.numFields < userData.payload.length
                    ? userData.numFields : userData.payload.length;

            System.arraycopy(userData.payload, 0, payload, 0, copyLen);
            userData.payload = payload;

            if (!decodingtypeUTF8) {
                // There are many devices in the market that send 8bit text sms (latin encoded) as
                // octet encoded.
                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
            } else {
                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
            }
            break;

        case UserData.ENCODING_IA5:
        case UserData.ENCODING_7BIT_ASCII:
            userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
            break;
        case UserData.ENCODING_UNICODE_16:
            userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
            break;
        case UserData.ENCODING_GSM_7BIT_ALPHABET:
            userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
            break;
        case UserData.ENCODING_LATIN:
            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
            break;
        case UserData.ENCODING_SHIFT_JIS:
            userData.payloadStr = decodeShiftJis(userData.payload, offset, userData.numFields);
            break;
        default:
            throw new CodingException("unsupported user data encoding ("
                                      + userData.msgEncoding + ")");
        }
    
private static booleandecodeUserResponseCode(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.userResponseCode = inStream.read(8);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.userResponseCodeSet = decodeSuccess;
        return decodeSuccess;
    
private static java.lang.StringdecodeUtf16(byte[] data, int offset, int numFields)

        // Subtract header and possible padding byte (at end) from num fields.
        int padding = offset % 2;
        numFields -= (offset + padding) / 2;
        return decodeCharset(data, offset, numFields, 2, "utf-16be");
    
private static java.lang.StringdecodeUtf8(byte[] data, int offset, int numFields)

        return decodeCharset(data, offset, numFields, 1, "UTF-8");
    
private static booleandecodeValidityAbs(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 6 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        return decodeSuccess;
    
private static booleandecodeValidityRel(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseInputStream inStream)

        final int EXPECTED_PARAM_SIZE = 1 * 8;
        boolean decodeSuccess = false;
        int paramBits = inStream.read(8) * 8;
        if (paramBits >= EXPECTED_PARAM_SIZE) {
            paramBits -= EXPECTED_PARAM_SIZE;
            decodeSuccess = true;
            bData.deferredDeliveryTimeRelative = inStream.read(8);
        }
        if ((! decodeSuccess) || (paramBits > 0)) {
            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
                      (decodeSuccess ? "succeeded" : "failed") +
                      " (extra bits = " + paramBits + ")");
        }
        inStream.skip(paramBits);
        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
        return decodeSuccess;
    
public static byte[]encode(com.android.internal.telephony.cdma.sms.BearerData bData)
Create serialized representation for BearerData object. (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)

param
bData an instance of BearerData.
return
byte array of raw encoded SMS bearer data.

        bData.hasUserDataHeader = ((bData.userData != null) &&
                (bData.userData.userDataHeader != null));
        try {
            BitwiseOutputStream outStream = new BitwiseOutputStream(200);
            outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
            encodeMessageId(bData, outStream);
            if (bData.userData != null) {
                outStream.write(8, SUBPARAM_USER_DATA);
                encodeUserData(bData, outStream);
            }
            if (bData.callbackNumber != null) {
                outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
                encodeCallbackNumber(bData, outStream);
            }
            if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
                outStream.write(8, SUBPARAM_REPLY_OPTION);
                encodeReplyOption(bData, outStream);
            }
            if (bData.numberOfMessages != 0) {
                outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
                encodeMsgCount(bData, outStream);
            }
            if (bData.validityPeriodRelativeSet) {
                outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
                encodeValidityPeriodRel(bData, outStream);
            }
            if (bData.privacyIndicatorSet) {
                outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
                encodePrivacyIndicator(bData, outStream);
            }
            if (bData.languageIndicatorSet) {
                outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
                encodeLanguageIndicator(bData, outStream);
            }
            if (bData.displayModeSet) {
                outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
                encodeDisplayMode(bData, outStream);
            }
            if (bData.priorityIndicatorSet) {
                outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
                encodePriorityIndicator(bData, outStream);
            }
            if (bData.alertIndicatorSet) {
                outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
                encodeMsgDeliveryAlert(bData, outStream);
            }
            if (bData.messageStatusSet) {
                outStream.write(8, SUBPARAM_MESSAGE_STATUS);
                encodeMsgStatus(bData, outStream);
            }
            if (bData.serviceCategoryProgramResults != null) {
                outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
                encodeScpResults(bData, outStream);
            }
            return outStream.toByteArray();
        } catch (BitwiseOutputStream.AccessException ex) {
            Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
        } catch (CodingException ex) {
            Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
        }
        return null;
   
private static voidencode16bitEms(UserData uData, byte[] udhData)

        byte[] payload = encodeUtf16(uData.payloadStr);
        int udhBytes = udhData.length + 1;  // Add length octet.
        int udhCodeUnits = (udhBytes + 1) / 2;
        int payloadCodeUnits = payload.length / 2;
        uData.msgEncoding = UserData.ENCODING_UNICODE_16;
        uData.msgEncodingSet = true;
        uData.numFields = udhCodeUnits + payloadCodeUnits;
        uData.payload = new byte[uData.numFields * 2];
        uData.payload[0] = (byte)udhData.length;
        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
        System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
    
private static byte[]encode7bitAscii(java.lang.String msg, boolean force)

        try {
            BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
            int msgLen = msg.length();
            for (int i = 0; i < msgLen; i++) {
                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
                if (charCode == -1) {
                    if (force) {
                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
                    } else {
                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
                    }
                } else {
                    outStream.write(7, charCode);
                }
            }
            return outStream.toByteArray();
        } catch (BitwiseOutputStream.AccessException ex) {
            throw new CodingException("7bit ASCII encode failed: " + ex);
        }
    
private static voidencode7bitEms(UserData uData, byte[] udhData, boolean force)

        int udhBytes = udhData.length + 1;  // Add length octet.
        int udhSeptets = ((udhBytes * 8) + 6) / 7;
        Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
        uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
        uData.msgEncodingSet = true;
        uData.numFields = gcr.septets;
        uData.payload = gcr.data;
        uData.payload[0] = (byte)udhData.length;
        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
    
private static com.android.internal.telephony.cdma.sms.BearerData$Gsm7bitCodingResultencode7bitGsm(java.lang.String msg, int septetOffset, boolean force)

        try {
            /*
             * TODO(cleanup): It would be nice if GsmAlphabet provided
             * an option to produce just the data without prepending
             * the septet count, as this function is really just a
             * wrapper to strip that off.  Not to mention that the
             * septet count is generally known prior to invocation of
             * the encoder.  Note that it cannot be derived from the
             * resulting array length, since that cannot distinguish
             * if the last contains either 1 or 8 valid bits.
             *
             * TODO(cleanup): The BitwiseXStreams could also be
             * extended with byte-wise reversed endianness read/write
             * routines to allow a corresponding implementation of
             * stringToGsm7BitPacked, and potentially directly support
             * access to the main bitwise stream from encode/decode.
             */
            byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
            Gsm7bitCodingResult result = new Gsm7bitCodingResult();
            result.data = new byte[fullData.length - 1];
            System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
            result.septets = fullData[0] & 0x00FF;
            return result;
        } catch (com.android.internal.telephony.EncodeException ex) {
            throw new CodingException("7bit GSM encode failed: " + ex);
        }
    
private static voidencodeCallbackNumber(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        CdmaSmsAddress addr = bData.callbackNumber;
        encodeCdmaSmsAddress(addr);
        int paramBits = 9;
        int dataBits = 0;
        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
            paramBits += 7;
            dataBits = addr.numberOfDigits * 8;
        } else {
            dataBits = addr.numberOfDigits * 4;
        }
        paramBits += dataBits;
        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
        int paddingBits = (paramBytes * 8) - paramBits;
        outStream.write(8, paramBytes);
        outStream.write(1, addr.digitMode);
        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
            outStream.write(3, addr.ton);
            outStream.write(4, addr.numberPlan);
        }
        outStream.write(8, addr.numberOfDigits);
        outStream.writeByteArray(dataBits, addr.origBytes);
        if (paddingBits > 0) outStream.write(paddingBits, 0);
    
private static voidencodeCdmaSmsAddress(CdmaSmsAddress addr)

        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
            try {
                addr.origBytes = addr.address.getBytes("US-ASCII");
            } catch (java.io.UnsupportedEncodingException ex) {
                throw new CodingException("invalid SMS address, cannot convert to ASCII");
            }
        } else {
            addr.origBytes = encodeDtmfSmsAddress(addr.address);
        }
    
private static voidencodeDisplayMode(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(2, bData.displayMode);
        outStream.skip(6);
    
private static byte[]encodeDtmfSmsAddress(java.lang.String address)

        int digits = address.length();
        int dataBits = digits * 4;
        int dataBytes = (dataBits / 8);
        dataBytes += (dataBits % 8) > 0 ? 1 : 0;
        byte[] rawData = new byte[dataBytes];
        for (int i = 0; i < digits; i++) {
            char c = address.charAt(i);
            int val = 0;
            if ((c >= '1") && (c <= '9")) val = c - '0";
            else if (c == '0") val = 10;
            else if (c == '*") val = 11;
            else if (c == '#") val = 12;
            else return null;
            rawData[i / 2] |= val << (4 - ((i % 2) * 4));
        }
        return rawData;
    
private static voidencodeEmsUserDataPayload(UserData uData)

        byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
        if (uData.msgEncodingSet) {
            if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
                encode7bitEms(uData, headerData, true);
            } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
                encode16bitEms(uData, headerData);
            } else {
                throw new CodingException("unsupported EMS user data encoding (" +
                                          uData.msgEncoding + ")");
            }
        } else {
            try {
                encode7bitEms(uData, headerData, false);
            } catch (CodingException ex) {
                encode16bitEms(uData, headerData);
            }
        }
    
private static voidencodeLanguageIndicator(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(8, bData.language);
    
private static voidencodeMessageId(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 3);
        outStream.write(4, bData.messageType);
        outStream.write(8, bData.messageId >> 8);
        outStream.write(8, bData.messageId);
        outStream.write(1, bData.hasUserDataHeader ? 1 : 0);
        outStream.skip(3);
    
private static voidencodeMsgCount(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(8, bData.numberOfMessages);
    
private static voidencodeMsgDeliveryAlert(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(2, bData.alert);
        outStream.skip(6);
    
private static voidencodeMsgStatus(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(2, bData.errorClass);
        outStream.write(6, bData.messageStatus);
    
private static voidencodePriorityIndicator(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(2, bData.priority);
        outStream.skip(6);
    
private static voidencodePrivacyIndicator(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(2, bData.privacy);
        outStream.skip(6);
    
private static voidencodeReplyOption(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(1, bData.userAckReq     ? 1 : 0);
        outStream.write(1, bData.deliveryAckReq ? 1 : 0);
        outStream.write(1, bData.readAckReq     ? 1 : 0);
        outStream.write(1, bData.reportReq      ? 1 : 0);
        outStream.write(4, 0);
    
private static voidencodeScpResults(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        ArrayList<CdmaSmsCbProgramResults> results = bData.serviceCategoryProgramResults;
        outStream.write(8, (results.size() * 4));   // 4 octets per program result
        for (CdmaSmsCbProgramResults result : results) {
            int category = result.getCategory();
            outStream.write(8, category >> 8);
            outStream.write(8, category);
            outStream.write(8, result.getLanguage());
            outStream.write(4, result.getCategoryResult());
            outStream.skip(4);
        }
    
private static byte[]encodeShiftJis(java.lang.String msg)

        try {
            return msg.getBytes("Shift_JIS");
        } catch (java.io.UnsupportedEncodingException ex) {
            throw new CodingException("Shift-JIS encode failed: " + ex);
        }
    
private static voidencodeUserData(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        /*
         * TODO(cleanup): Do we really need to set userData.payload as
         * a side effect of encoding?  If not, we could avoid data
         * copies by passing outStream directly.
         */
        encodeUserDataPayload(bData.userData);
        bData.hasUserDataHeader = bData.userData.userDataHeader != null;

        if (bData.userData.payload.length > SmsConstants.MAX_USER_DATA_BYTES) {
            throw new CodingException("encoded user data too large (" +
                                      bData.userData.payload.length +
                                      " > " + SmsConstants.MAX_USER_DATA_BYTES + " bytes)");
        }

        /*
         * TODO(cleanup): figure out what the right answer is WRT paddingBits field
         *
         *   userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
         *   userData.paddingBits = 0; // XXX this seems better, but why?
         *
         */
        int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
        int paramBits = dataBits + 13;
        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
            paramBits += 8;
        }
        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
        int paddingBits = (paramBytes * 8) - paramBits;
        outStream.write(8, paramBytes);
        outStream.write(5, bData.userData.msgEncoding);
        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
            outStream.write(8, bData.userData.msgType);
        }
        outStream.write(8, bData.userData.numFields);
        outStream.writeByteArray(dataBits, bData.userData.payload);
        if (paddingBits > 0) outStream.write(paddingBits, 0);
    
private static voidencodeUserDataPayload(UserData uData)

        if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
            Rlog.e(LOG_TAG, "user data with null payloadStr");
            uData.payloadStr = "";
        }

        if (uData.userDataHeader != null) {
            encodeEmsUserDataPayload(uData);
            return;
        }

        if (uData.msgEncodingSet) {
            if (uData.msgEncoding == UserData.ENCODING_OCTET) {
                if (uData.payload == null) {
                    Rlog.e(LOG_TAG, "user data with octet encoding but null payload");
                    uData.payload = new byte[0];
                    uData.numFields = 0;
                } else {
                    uData.numFields = uData.payload.length;
                }
            } else {
                if (uData.payloadStr == null) {
                    Rlog.e(LOG_TAG, "non-octet user data with null payloadStr");
                    uData.payloadStr = "";
                }
                if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
                    Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
                    uData.payload = gcr.data;
                    uData.numFields = gcr.septets;
                } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
                    uData.payload = encode7bitAscii(uData.payloadStr, true);
                    uData.numFields = uData.payloadStr.length();
                } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
                    uData.payload = encodeUtf16(uData.payloadStr);
                    uData.numFields = uData.payloadStr.length();
                } else if (uData.msgEncoding == UserData.ENCODING_SHIFT_JIS) {
                    uData.payload = encodeShiftJis(uData.payloadStr);
                    uData.numFields = uData.payload.length;
                } else {
                    throw new CodingException("unsupported user data encoding (" +
                                              uData.msgEncoding + ")");
                }
            }
        } else {
            try {
                uData.payload = encode7bitAscii(uData.payloadStr, false);
                uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
            } catch (CodingException ex) {
                uData.payload = encodeUtf16(uData.payloadStr);
                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
            }
            uData.numFields = uData.payloadStr.length();
            uData.msgEncodingSet = true;
        }
    
private static byte[]encodeUtf16(java.lang.String msg)

        try {
            return msg.getBytes("utf-16be");
        } catch (java.io.UnsupportedEncodingException ex) {
            throw new CodingException("UTF-16 encode failed: " + ex);
        }
    
private static voidencodeValidityPeriodRel(com.android.internal.telephony.cdma.sms.BearerData bData, com.android.internal.util.BitwiseOutputStream outStream)

        outStream.write(8, 1);
        outStream.write(8, bData.validityPeriodRelative);
    
private static intgetBitsForNumFields(int msgEncoding, int numFields)
Calculates the number of bits to read for the specified number of encoded characters.

param
msgEncoding the message encoding to use
param
numFields the number of characters to read. For Shift-JIS and Korean encodings, this is the number of bytes to read.
return
the number of bits to read from the stream
throws
CodingException if the specified encoding is not supported

        switch (msgEncoding) {
            case UserData.ENCODING_OCTET:
            case UserData.ENCODING_SHIFT_JIS:
            case UserData.ENCODING_KOREAN:
            case UserData.ENCODING_LATIN:
            case UserData.ENCODING_LATIN_HEBREW:
                return numFields * 8;

            case UserData.ENCODING_IA5:
            case UserData.ENCODING_7BIT_ASCII:
            case UserData.ENCODING_GSM_7BIT_ALPHABET:
                return numFields * 7;

            case UserData.ENCODING_UNICODE_16:
                return numFields * 16;

            default:
                throw new CodingException("unsupported message encoding (" + msgEncoding + ')");
        }
    
public java.lang.StringgetLanguage()
Returns the language indicator as a two-character ISO 639 string.

return
a two character ISO 639 language code

        return getLanguageCodeForValue(language);
    
private static java.lang.StringgetLanguageCodeForValue(int languageValue)
Converts a CDMA language indicator value to an ISO 639 two character language code.

param
languageValue the CDMA language value to convert
return
the two character ISO 639 language code for the specified value, or null if unknown

        switch (languageValue) {
            case LANGUAGE_ENGLISH:
                return "en";

            case LANGUAGE_FRENCH:
                return "fr";

            case LANGUAGE_SPANISH:
                return "es";

            case LANGUAGE_JAPANESE:
                return "ja";

            case LANGUAGE_KOREAN:
                return "ko";

            case LANGUAGE_CHINESE:
                return "zh";

            case LANGUAGE_HEBREW:
                return "he";

            default:
                return null;
        }
    
private static booleanisCmasAlertCategory(int category)

        return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
                && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
    
private static intserviceCategoryToCmasMessageClass(int serviceCategory)

        switch (serviceCategory) {
            case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
                return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;

            case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
                return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;

            case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
                return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;

            case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
                return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;

            case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
                return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;

            default:
                return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
        }
    
public java.lang.StringtoString()

        StringBuilder builder = new StringBuilder();
        builder.append("BearerData ");
        builder.append("{ messageType=" + messageType);
        builder.append(", messageId=" + messageId);
        builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
        builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
        builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
        builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
        builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
        builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
        builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
        builder.append(", msgCenterTimeStamp=" +
                ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset"));
        builder.append(", validityPeriodAbsolute=" +
                ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset"));
        builder.append(", validityPeriodRelative=" +
                ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset"));
        builder.append(", deferredDeliveryTimeAbsolute=" +
                ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset"));
        builder.append(", deferredDeliveryTimeRelative=" +
                ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset"));
        builder.append(", userAckReq=" + userAckReq);
        builder.append(", deliveryAckReq=" + deliveryAckReq);
        builder.append(", readAckReq=" + readAckReq);
        builder.append(", reportReq=" + reportReq);
        builder.append(", numberOfMessages=" + numberOfMessages);
        builder.append(", callbackNumber=" + callbackNumber);
        builder.append(", depositIndex=" + depositIndex);
        builder.append(", hasUserDataHeader=" + hasUserDataHeader);
        builder.append(", userData=" + userData);
        builder.append(" }");
        return builder.toString();