Home | History | Annotate | Download | only in gsm
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony.gsm;
     18 
     19 import android.telephony.PhoneNumberUtils;
     20 import android.text.format.Time;
     21 import android.telephony.Rlog;
     22 import android.content.res.Resources;
     23 import android.text.TextUtils;
     24 
     25 import com.android.internal.telephony.EncodeException;
     26 import com.android.internal.telephony.GsmAlphabet;
     27 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
     28 import com.android.internal.telephony.uicc.IccUtils;
     29 import com.android.internal.telephony.SmsHeader;
     30 import com.android.internal.telephony.SmsMessageBase;
     31 import com.android.internal.telephony.Sms7BitEncodingTranslator;
     32 
     33 import java.io.ByteArrayOutputStream;
     34 import java.io.UnsupportedEncodingException;
     35 import java.text.ParseException;
     36 
     37 import static com.android.internal.telephony.SmsConstants.MessageClass;
     38 import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
     39 import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
     40 import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
     41 import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
     42 import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
     43 import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
     44 import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
     45 import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
     46 
     47 /**
     48  * A Short Message Service message.
     49  *
     50  */
     51 public class SmsMessage extends SmsMessageBase {
     52     static final String LOG_TAG = "SmsMessage";
     53     private static final boolean VDBG = false;
     54 
     55     private MessageClass messageClass;
     56 
     57     /**
     58      * TP-Message-Type-Indicator
     59      * 9.2.3
     60      */
     61     private int mMti;
     62 
     63     /** TP-Protocol-Identifier (TP-PID) */
     64     private int mProtocolIdentifier;
     65 
     66     // TP-Data-Coding-Scheme
     67     // see TS 23.038
     68     private int mDataCodingScheme;
     69 
     70     // TP-Reply-Path
     71     // e.g. 23.040 9.2.2.1
     72     private boolean mReplyPathPresent = false;
     73 
     74     /** The address of the receiver. */
     75     private GsmSmsAddress mRecipientAddress;
     76 
     77     /**
     78      *  TP-Status - status of a previously submitted SMS.
     79      *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
     80      *  see TS 23.040, 9.2.3.15 for description of other possible values.
     81      */
     82     private int mStatus;
     83 
     84     /**
     85      *  TP-Status - status of a previously submitted SMS.
     86      *  This field is true iff the message is a SMS-STATUS-REPORT message.
     87      */
     88     private boolean mIsStatusReportMessage = false;
     89 
     90     private int mVoiceMailCount = 0;
     91 
     92     public static class SubmitPdu extends SubmitPduBase {
     93     }
     94 
     95     /**
     96      * Create an SmsMessage from a raw PDU.
     97      */
     98     public static SmsMessage createFromPdu(byte[] pdu) {
     99         try {
    100             SmsMessage msg = new SmsMessage();
    101             msg.parsePdu(pdu);
    102             return msg;
    103         } catch (RuntimeException ex) {
    104             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
    105             return null;
    106         } catch (OutOfMemoryError e) {
    107             Rlog.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e);
    108             return null;
    109         }
    110     }
    111 
    112     /**
    113      * 3GPP TS 23.040 9.2.3.9 specifies that Type Zero messages are indicated
    114      * by TP_PID field set to value 0x40
    115      */
    116     public boolean isTypeZero() {
    117         return (mProtocolIdentifier == 0x40);
    118     }
    119 
    120     /**
    121      * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
    122      * +CMT unsolicited response (PDU mode, of course)
    123      *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
    124      *
    125      * Only public for debugging
    126      *
    127      * {@hide}
    128      */
    129     public static SmsMessage newFromCMT(String[] lines) {
    130         try {
    131             SmsMessage msg = new SmsMessage();
    132             msg.parsePdu(IccUtils.hexStringToBytes(lines[1]));
    133             return msg;
    134         } catch (RuntimeException ex) {
    135             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
    136             return null;
    137         }
    138     }
    139 
    140     /** @hide */
    141     public static SmsMessage newFromCDS(String line) {
    142         try {
    143             SmsMessage msg = new SmsMessage();
    144             msg.parsePdu(IccUtils.hexStringToBytes(line));
    145             return msg;
    146         } catch (RuntimeException ex) {
    147             Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
    148             return null;
    149         }
    150     }
    151 
    152     /**
    153      * Create an SmsMessage from an SMS EF record.
    154      *
    155      * @param index Index of SMS record. This should be index in ArrayList
    156      *              returned by SmsManager.getAllMessagesFromSim + 1.
    157      * @param data Record data.
    158      * @return An SmsMessage representing the record.
    159      *
    160      * @hide
    161      */
    162     public static SmsMessage createFromEfRecord(int index, byte[] data) {
    163         try {
    164             SmsMessage msg = new SmsMessage();
    165 
    166             msg.mIndexOnIcc = index;
    167 
    168             // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
    169             // or STORED_UNSENT
    170             // See TS 51.011 10.5.3
    171             if ((data[0] & 1) == 0) {
    172                 Rlog.w(LOG_TAG,
    173                         "SMS parsing failed: Trying to parse a free record");
    174                 return null;
    175             } else {
    176                 msg.mStatusOnIcc = data[0] & 0x07;
    177             }
    178 
    179             int size = data.length - 1;
    180 
    181             // Note: Data may include trailing FF's.  That's OK; message
    182             // should still parse correctly.
    183             byte[] pdu = new byte[size];
    184             System.arraycopy(data, 1, pdu, 0, size);
    185             msg.parsePdu(pdu);
    186             return msg;
    187         } catch (RuntimeException ex) {
    188             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
    189             return null;
    190         }
    191     }
    192 
    193     /**
    194      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
    195      * length in bytes (not hex chars) less the SMSC header
    196      */
    197     public static int getTPLayerLengthForPDU(String pdu) {
    198         int len = pdu.length() / 2;
    199         int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
    200 
    201         return len - smscLen - 1;
    202     }
    203 
    204     /**
    205      * Get an SMS-SUBMIT PDU for a destination address and a message
    206      *
    207      * @param scAddress Service Centre address.  Null means use default.
    208      * @return a <code>SubmitPdu</code> containing the encoded SC
    209      *         address, if applicable, and the encoded message.
    210      *         Returns null on encode error.
    211      * @hide
    212      */
    213     public static SubmitPdu getSubmitPdu(String scAddress,
    214             String destinationAddress, String message,
    215             boolean statusReportRequested, byte[] header) {
    216         return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
    217                 ENCODING_UNKNOWN, 0, 0);
    218     }
    219 
    220 
    221     /**
    222      * Get an SMS-SUBMIT PDU for a destination address and a message using the
    223      * specified encoding.
    224      *
    225      * @param scAddress Service Centre address.  Null means use default.
    226      * @param encoding Encoding defined by constants in
    227      *        com.android.internal.telephony.SmsConstants.ENCODING_*
    228      * @param languageTable
    229      * @param languageShiftTable
    230      * @return a <code>SubmitPdu</code> containing the encoded SC
    231      *         address, if applicable, and the encoded message.
    232      *         Returns null on encode error.
    233      * @hide
    234      */
    235     public static SubmitPdu getSubmitPdu(String scAddress,
    236             String destinationAddress, String message,
    237             boolean statusReportRequested, byte[] header, int encoding,
    238             int languageTable, int languageShiftTable) {
    239 
    240         // Perform null parameter checks.
    241         if (message == null || destinationAddress == null) {
    242             return null;
    243         }
    244 
    245         if (encoding == ENCODING_UNKNOWN) {
    246             // Find the best encoding to use
    247             TextEncodingDetails ted = calculateLength(message, false);
    248             encoding = ted.codeUnitSize;
    249             languageTable = ted.languageTable;
    250             languageShiftTable = ted.languageShiftTable;
    251 
    252             if (encoding == ENCODING_7BIT &&
    253                     (languageTable != 0 || languageShiftTable != 0)) {
    254                 if (header != null) {
    255                     SmsHeader smsHeader = SmsHeader.fromByteArray(header);
    256                     if (smsHeader.languageTable != languageTable
    257                             || smsHeader.languageShiftTable != languageShiftTable) {
    258                         Rlog.w(LOG_TAG, "Updating language table in SMS header: "
    259                                 + smsHeader.languageTable + " -> " + languageTable + ", "
    260                                 + smsHeader.languageShiftTable + " -> " + languageShiftTable);
    261                         smsHeader.languageTable = languageTable;
    262                         smsHeader.languageShiftTable = languageShiftTable;
    263                         header = SmsHeader.toByteArray(smsHeader);
    264                     }
    265                 } else {
    266                     SmsHeader smsHeader = new SmsHeader();
    267                     smsHeader.languageTable = languageTable;
    268                     smsHeader.languageShiftTable = languageShiftTable;
    269                     header = SmsHeader.toByteArray(smsHeader);
    270                 }
    271             }
    272         }
    273 
    274         SubmitPdu ret = new SubmitPdu();
    275         // MTI = SMS-SUBMIT, UDHI = header != null
    276         byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
    277         ByteArrayOutputStream bo = getSubmitPduHead(
    278                 scAddress, destinationAddress, mtiByte,
    279                 statusReportRequested, ret);
    280 
    281         // User Data (and length)
    282         byte[] userData;
    283         try {
    284             if (encoding == ENCODING_7BIT) {
    285                 userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
    286                         languageTable, languageShiftTable);
    287             } else { //assume UCS-2
    288                 try {
    289                     userData = encodeUCS2(message, header);
    290                 } catch(UnsupportedEncodingException uex) {
    291                     Rlog.e(LOG_TAG,
    292                             "Implausible UnsupportedEncodingException ",
    293                             uex);
    294                     return null;
    295                 }
    296             }
    297         } catch (EncodeException ex) {
    298             // Encoding to the 7-bit alphabet failed. Let's see if we can
    299             // send it as a UCS-2 encoded message
    300             try {
    301                 userData = encodeUCS2(message, header);
    302                 encoding = ENCODING_16BIT;
    303             } catch(UnsupportedEncodingException uex) {
    304                 Rlog.e(LOG_TAG,
    305                         "Implausible UnsupportedEncodingException ",
    306                         uex);
    307                 return null;
    308             }
    309         }
    310 
    311         if (encoding == ENCODING_7BIT) {
    312             if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
    313                 // Message too long
    314                 Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
    315                 return null;
    316             }
    317             // TP-Data-Coding-Scheme
    318             // Default encoding, uncompressed
    319             // To test writing messages to the SIM card, change this value 0x00
    320             // to 0x12, which means "bits 1 and 0 contain message class, and the
    321             // class is 2". Note that this takes effect for the sender. In other
    322             // words, messages sent by the phone with this change will end up on
    323             // the receiver's SIM card. You can then send messages to yourself
    324             // (on a phone with this change) and they'll end up on the SIM card.
    325             bo.write(0x00);
    326         } else { // assume UCS-2
    327             if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
    328                 // Message too long
    329                 Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
    330                 return null;
    331             }
    332             // TP-Data-Coding-Scheme
    333             // UCS-2 encoding, uncompressed
    334             bo.write(0x08);
    335         }
    336 
    337         // (no TP-Validity-Period)
    338         bo.write(userData, 0, userData.length);
    339         ret.encodedMessage = bo.toByteArray();
    340         return ret;
    341     }
    342 
    343     /**
    344      * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if necessary
    345      *
    346      * @return encoded message as UCS2
    347      * @throws UnsupportedEncodingException
    348      */
    349     private static byte[] encodeUCS2(String message, byte[] header)
    350         throws UnsupportedEncodingException {
    351         byte[] userData, textPart;
    352         textPart = message.getBytes("utf-16be");
    353 
    354         if (header != null) {
    355             // Need 1 byte for UDHL
    356             userData = new byte[header.length + textPart.length + 1];
    357 
    358             userData[0] = (byte)header.length;
    359             System.arraycopy(header, 0, userData, 1, header.length);
    360             System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
    361         }
    362         else {
    363             userData = textPart;
    364         }
    365         byte[] ret = new byte[userData.length+1];
    366         ret[0] = (byte) (userData.length & 0xff );
    367         System.arraycopy(userData, 0, ret, 1, userData.length);
    368         return ret;
    369     }
    370 
    371     /**
    372      * Get an SMS-SUBMIT PDU for a destination address and a message
    373      *
    374      * @param scAddress Service Centre address.  Null means use default.
    375      * @return a <code>SubmitPdu</code> containing the encoded SC
    376      *         address, if applicable, and the encoded message.
    377      *         Returns null on encode error.
    378      */
    379     public static SubmitPdu getSubmitPdu(String scAddress,
    380             String destinationAddress, String message,
    381             boolean statusReportRequested) {
    382 
    383         return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
    384     }
    385 
    386     /**
    387      * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
    388      *
    389      * @param scAddress Service Centre address. null == use default
    390      * @param destinationAddress the address of the destination for the message
    391      * @param destinationPort the port to deliver the message to at the
    392      *        destination
    393      * @param data the data for the message
    394      * @return a <code>SubmitPdu</code> containing the encoded SC
    395      *         address, if applicable, and the encoded message.
    396      *         Returns null on encode error.
    397      */
    398     public static SubmitPdu getSubmitPdu(String scAddress,
    399             String destinationAddress, int destinationPort, byte[] data,
    400             boolean statusReportRequested) {
    401 
    402         SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
    403         portAddrs.destPort = destinationPort;
    404         portAddrs.origPort = 0;
    405         portAddrs.areEightBits = false;
    406 
    407         SmsHeader smsHeader = new SmsHeader();
    408         smsHeader.portAddrs = portAddrs;
    409 
    410         byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
    411 
    412         if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
    413             Rlog.e(LOG_TAG, "SMS data message may only contain "
    414                     + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
    415             return null;
    416         }
    417 
    418         SubmitPdu ret = new SubmitPdu();
    419         ByteArrayOutputStream bo = getSubmitPduHead(
    420                 scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
    421                                                             // TP-UDHI = true
    422                 statusReportRequested, ret);
    423 
    424         // TP-Data-Coding-Scheme
    425         // No class, 8 bit data
    426         bo.write(0x04);
    427 
    428         // (no TP-Validity-Period)
    429 
    430         // Total size
    431         bo.write(data.length + smsHeaderData.length + 1);
    432 
    433         // User data header
    434         bo.write(smsHeaderData.length);
    435         bo.write(smsHeaderData, 0, smsHeaderData.length);
    436 
    437         // User data
    438         bo.write(data, 0, data.length);
    439 
    440         ret.encodedMessage = bo.toByteArray();
    441         return ret;
    442     }
    443 
    444     /**
    445      * Create the beginning of a SUBMIT PDU.  This is the part of the
    446      * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
    447      * one of which takes a byte array and the other of which takes a
    448      * <code>String</code>.
    449      *
    450      * @param scAddress Service Centre address. null == use default
    451      * @param destinationAddress the address of the destination for the message
    452      * @param mtiByte
    453      * @param ret <code>SubmitPdu</code> containing the encoded SC
    454      *        address, if applicable, and the encoded message
    455      */
    456     private static ByteArrayOutputStream getSubmitPduHead(
    457             String scAddress, String destinationAddress, byte mtiByte,
    458             boolean statusReportRequested, SubmitPdu ret) {
    459         ByteArrayOutputStream bo = new ByteArrayOutputStream(
    460                 MAX_USER_DATA_BYTES + 40);
    461 
    462         // SMSC address with length octet, or 0
    463         if (scAddress == null) {
    464             ret.encodedScAddress = null;
    465         } else {
    466             ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
    467                     scAddress);
    468         }
    469 
    470         // TP-Message-Type-Indicator (and friends)
    471         if (statusReportRequested) {
    472             // Set TP-Status-Report-Request bit.
    473             mtiByte |= 0x20;
    474             if (VDBG) Rlog.d(LOG_TAG, "SMS status report requested");
    475         }
    476         bo.write(mtiByte);
    477 
    478         // space for TP-Message-Reference
    479         bo.write(0);
    480 
    481         byte[] daBytes;
    482 
    483         daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
    484 
    485         // destination address length in BCD digits, ignoring TON byte and pad
    486         // TODO Should be better.
    487         bo.write((daBytes.length - 1) * 2
    488                 - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
    489 
    490         // destination address
    491         bo.write(daBytes, 0, daBytes.length);
    492 
    493         // TP-Protocol-Identifier
    494         bo.write(0);
    495         return bo;
    496     }
    497 
    498     private static class PduParser {
    499         byte mPdu[];
    500         int mCur;
    501         SmsHeader mUserDataHeader;
    502         byte[] mUserData;
    503         int mUserDataSeptetPadding;
    504 
    505         PduParser(byte[] pdu) {
    506             mPdu = pdu;
    507             mCur = 0;
    508             mUserDataSeptetPadding = 0;
    509         }
    510 
    511         /**
    512          * Parse and return the SC address prepended to SMS messages coming via
    513          * the TS 27.005 / AT interface.  Returns null on invalid address
    514          */
    515         String getSCAddress() {
    516             int len;
    517             String ret;
    518 
    519             // length of SC Address
    520             len = getByte();
    521 
    522             if (len == 0) {
    523                 // no SC address
    524                 ret = null;
    525             } else {
    526                 // SC address
    527                 try {
    528                     ret = PhoneNumberUtils
    529                             .calledPartyBCDToString(mPdu, mCur, len);
    530                 } catch (RuntimeException tr) {
    531                     Rlog.d(LOG_TAG, "invalid SC address: ", tr);
    532                     ret = null;
    533                 }
    534             }
    535 
    536             mCur += len;
    537 
    538             return ret;
    539         }
    540 
    541         /**
    542          * returns non-sign-extended byte value
    543          */
    544         int getByte() {
    545             return mPdu[mCur++] & 0xff;
    546         }
    547 
    548         /**
    549          * Any address except the SC address (eg, originating address) See TS
    550          * 23.040 9.1.2.5
    551          */
    552         GsmSmsAddress getAddress() {
    553             GsmSmsAddress ret;
    554 
    555             // "The Address-Length field is an integer representation of
    556             // the number field, i.e. excludes any semi-octet containing only
    557             // fill bits."
    558             // The TOA field is not included as part of this
    559             int addressLength = mPdu[mCur] & 0xff;
    560             int lengthBytes = 2 + (addressLength + 1) / 2;
    561 
    562             try {
    563                 ret = new GsmSmsAddress(mPdu, mCur, lengthBytes);
    564             } catch (ParseException e) {
    565                 ret = null;
    566                 //This is caught by createFromPdu(byte[] pdu)
    567                 throw new RuntimeException(e.getMessage());
    568             }
    569 
    570             mCur += lengthBytes;
    571 
    572             return ret;
    573         }
    574 
    575         /**
    576          * Parses an SC timestamp and returns a currentTimeMillis()-style
    577          * timestamp
    578          */
    579 
    580         long getSCTimestampMillis() {
    581             // TP-Service-Centre-Time-Stamp
    582             int year = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
    583             int month = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
    584             int day = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
    585             int hour = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
    586             int minute = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
    587             int second = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
    588 
    589             // For the timezone, the most significant bit of the
    590             // least significant nibble is the sign byte
    591             // (meaning the max range of this field is 79 quarter-hours,
    592             // which is more than enough)
    593 
    594             byte tzByte = mPdu[mCur++];
    595 
    596             // Mask out sign bit.
    597             int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
    598 
    599             timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
    600 
    601             Time time = new Time(Time.TIMEZONE_UTC);
    602 
    603             // It's 2006.  Should I really support years < 2000?
    604             time.year = year >= 90 ? year + 1900 : year + 2000;
    605             time.month = month - 1;
    606             time.monthDay = day;
    607             time.hour = hour;
    608             time.minute = minute;
    609             time.second = second;
    610 
    611             // Timezone offset is in quarter hours.
    612             return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000);
    613         }
    614 
    615         /**
    616          * Pulls the user data out of the PDU, and separates the payload from
    617          * the header if there is one.
    618          *
    619          * @param hasUserDataHeader true if there is a user data header
    620          * @param dataInSeptets true if the data payload is in septets instead
    621          *  of octets
    622          * @return the number of septets or octets in the user data payload
    623          */
    624         int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {
    625             int offset = mCur;
    626             int userDataLength = mPdu[offset++] & 0xff;
    627             int headerSeptets = 0;
    628             int userDataHeaderLength = 0;
    629 
    630             if (hasUserDataHeader) {
    631                 userDataHeaderLength = mPdu[offset++] & 0xff;
    632 
    633                 byte[] udh = new byte[userDataHeaderLength];
    634                 System.arraycopy(mPdu, offset, udh, 0, userDataHeaderLength);
    635                 mUserDataHeader = SmsHeader.fromByteArray(udh);
    636                 offset += userDataHeaderLength;
    637 
    638                 int headerBits = (userDataHeaderLength + 1) * 8;
    639                 headerSeptets = headerBits / 7;
    640                 headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
    641                 mUserDataSeptetPadding = (headerSeptets * 7) - headerBits;
    642             }
    643 
    644             int bufferLen;
    645             if (dataInSeptets) {
    646                 /*
    647                  * Here we just create the user data length to be the remainder of
    648                  * the pdu minus the user data header, since userDataLength means
    649                  * the number of uncompressed septets.
    650                  */
    651                 bufferLen = mPdu.length - offset;
    652             } else {
    653                 /*
    654                  * userDataLength is the count of octets, so just subtract the
    655                  * user data header.
    656                  */
    657                 bufferLen = userDataLength - (hasUserDataHeader ? (userDataHeaderLength + 1) : 0);
    658                 if (bufferLen < 0) {
    659                     bufferLen = 0;
    660                 }
    661             }
    662 
    663             mUserData = new byte[bufferLen];
    664             System.arraycopy(mPdu, offset, mUserData, 0, mUserData.length);
    665             mCur = offset;
    666 
    667             if (dataInSeptets) {
    668                 // Return the number of septets
    669                 int count = userDataLength - headerSeptets;
    670                 // If count < 0, return 0 (means UDL was probably incorrect)
    671                 return count < 0 ? 0 : count;
    672             } else {
    673                 // Return the number of octets
    674                 return mUserData.length;
    675             }
    676         }
    677 
    678         /**
    679          * Returns the user data payload, not including the headers
    680          *
    681          * @return the user data payload, not including the headers
    682          */
    683         byte[] getUserData() {
    684             return mUserData;
    685         }
    686 
    687         /**
    688          * Returns an object representing the user data headers
    689          *
    690          * {@hide}
    691          */
    692         SmsHeader getUserDataHeader() {
    693             return mUserDataHeader;
    694         }
    695 
    696         /**
    697          * Interprets the user data payload as packed GSM 7bit characters, and
    698          * decodes them into a String.
    699          *
    700          * @param septetCount the number of septets in the user data payload
    701          * @return a String with the decoded characters
    702          */
    703         String getUserDataGSM7Bit(int septetCount, int languageTable,
    704                 int languageShiftTable) {
    705             String ret;
    706 
    707             ret = GsmAlphabet.gsm7BitPackedToString(mPdu, mCur, septetCount,
    708                     mUserDataSeptetPadding, languageTable, languageShiftTable);
    709 
    710             mCur += (septetCount * 7) / 8;
    711 
    712             return ret;
    713         }
    714 
    715         /**
    716          * Interprets the user data payload as pack GSM 8-bit (a GSM alphabet string that's
    717          * stored in 8-bit unpacked format) characters, and decodes them into a String.
    718          *
    719          * @param byteCount the number of byest in the user data payload
    720          * @return a String with the decoded characters
    721          */
    722         String getUserDataGSM8bit(int byteCount) {
    723             String ret;
    724 
    725             ret = GsmAlphabet.gsm8BitUnpackedToString(mPdu, mCur, byteCount);
    726 
    727             mCur += byteCount;
    728 
    729             return ret;
    730         }
    731 
    732         /**
    733          * Interprets the user data payload as UCS2 characters, and
    734          * decodes them into a String.
    735          *
    736          * @param byteCount the number of bytes in the user data payload
    737          * @return a String with the decoded characters
    738          */
    739         String getUserDataUCS2(int byteCount) {
    740             String ret;
    741 
    742             try {
    743                 ret = new String(mPdu, mCur, byteCount, "utf-16");
    744             } catch (UnsupportedEncodingException ex) {
    745                 ret = "";
    746                 Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
    747             }
    748 
    749             mCur += byteCount;
    750             return ret;
    751         }
    752 
    753         /**
    754          * Interprets the user data payload as KSC-5601 characters, and
    755          * decodes them into a String.
    756          *
    757          * @param byteCount the number of bytes in the user data payload
    758          * @return a String with the decoded characters
    759          */
    760         String getUserDataKSC5601(int byteCount) {
    761             String ret;
    762 
    763             try {
    764                 ret = new String(mPdu, mCur, byteCount, "KSC5601");
    765             } catch (UnsupportedEncodingException ex) {
    766                 ret = "";
    767                 Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
    768             }
    769 
    770             mCur += byteCount;
    771             return ret;
    772         }
    773 
    774         boolean moreDataPresent() {
    775             return (mPdu.length > mCur);
    776         }
    777     }
    778 
    779     /**
    780      * Calculate the number of septets needed to encode the message.
    781      *
    782      * @param msgBody the message to encode
    783      * @param use7bitOnly ignore (but still count) illegal characters if true
    784      * @return TextEncodingDetails
    785      */
    786     public static TextEncodingDetails calculateLength(CharSequence msgBody,
    787             boolean use7bitOnly) {
    788         CharSequence newMsgBody = null;
    789         Resources r = Resources.getSystem();
    790         if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
    791             newMsgBody  = Sms7BitEncodingTranslator.translate(msgBody);
    792         }
    793         if (TextUtils.isEmpty(newMsgBody)) {
    794             newMsgBody = msgBody;
    795         }
    796         TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(newMsgBody, use7bitOnly);
    797         if (ted == null) {
    798             ted = new TextEncodingDetails();
    799             int octets = newMsgBody.length() * 2;
    800             ted.codeUnitCount = newMsgBody.length();
    801             if (octets > MAX_USER_DATA_BYTES) {
    802                 // If EMS is not supported, break down EMS into single segment SMS
    803                 // and add page info " x/y".
    804                 // In the case of UCS2 encoding type, we need 8 bytes for this
    805                 // but we only have 6 bytes from UDH, so truncate the limit for
    806                 // each segment by 2 bytes (1 char).
    807                 int max_user_data_bytes_with_header = MAX_USER_DATA_BYTES_WITH_HEADER;
    808                 if (!android.telephony.SmsMessage.hasEmsSupport()) {
    809                     // make sure total number of segments is less than 10
    810                     if (octets <= 9 * (max_user_data_bytes_with_header - 2))
    811                         max_user_data_bytes_with_header -= 2;
    812                 }
    813 
    814                 ted.msgCount = (octets + (max_user_data_bytes_with_header - 1)) /
    815                         max_user_data_bytes_with_header;
    816                 ted.codeUnitsRemaining = ((ted.msgCount *
    817                         max_user_data_bytes_with_header) - octets) / 2;
    818             } else {
    819                 ted.msgCount = 1;
    820                 ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
    821             }
    822             ted.codeUnitSize = ENCODING_16BIT;
    823         }
    824         return ted;
    825     }
    826 
    827     /** {@inheritDoc} */
    828     @Override
    829     public int getProtocolIdentifier() {
    830         return mProtocolIdentifier;
    831     }
    832 
    833     /**
    834      * Returns the TP-Data-Coding-Scheme byte, for acknowledgement of SMS-PP download messages.
    835      * @return the TP-DCS field of the SMS header
    836      */
    837     int getDataCodingScheme() {
    838         return mDataCodingScheme;
    839     }
    840 
    841     /** {@inheritDoc} */
    842     @Override
    843     public boolean isReplace() {
    844         return (mProtocolIdentifier & 0xc0) == 0x40
    845                 && (mProtocolIdentifier & 0x3f) > 0
    846                 && (mProtocolIdentifier & 0x3f) < 8;
    847     }
    848 
    849     /** {@inheritDoc} */
    850     @Override
    851     public boolean isCphsMwiMessage() {
    852         return ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageClear()
    853                 || ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet();
    854     }
    855 
    856     /** {@inheritDoc} */
    857     @Override
    858     public boolean isMWIClearMessage() {
    859         if (mIsMwi && !mMwiSense) {
    860             return true;
    861         }
    862 
    863         return mOriginatingAddress != null
    864                 && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageClear();
    865     }
    866 
    867     /** {@inheritDoc} */
    868     @Override
    869     public boolean isMWISetMessage() {
    870         if (mIsMwi && mMwiSense) {
    871             return true;
    872         }
    873 
    874         return mOriginatingAddress != null
    875                 && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet();
    876     }
    877 
    878     /** {@inheritDoc} */
    879     @Override
    880     public boolean isMwiDontStore() {
    881         if (mIsMwi && mMwiDontStore) {
    882             return true;
    883         }
    884 
    885         if (isCphsMwiMessage()) {
    886             // See CPHS 4.2 Section B.4.2.1
    887             // If the user data is a single space char, do not store
    888             // the message. Otherwise, store and display as usual
    889             if (" ".equals(getMessageBody())) {
    890                 return true;
    891             }
    892         }
    893 
    894         return false;
    895     }
    896 
    897     /** {@inheritDoc} */
    898     @Override
    899     public int getStatus() {
    900         return mStatus;
    901     }
    902 
    903     /** {@inheritDoc} */
    904     @Override
    905     public boolean isStatusReportMessage() {
    906         return mIsStatusReportMessage;
    907     }
    908 
    909     /** {@inheritDoc} */
    910     @Override
    911     public boolean isReplyPathPresent() {
    912         return mReplyPathPresent;
    913     }
    914 
    915     /**
    916      * TS 27.005 3.1, &lt;pdu&gt; definition "In the case of SMS: 3GPP TS 24.011 [6]
    917      * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
    918      * ME/TA converts each octet of TP data unit into two IRA character long
    919      * hex number (e.g. octet with integer value 42 is presented to TE as two
    920      * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
    921      * something else...
    922      */
    923     private void parsePdu(byte[] pdu) {
    924         mPdu = pdu;
    925         // Rlog.d(LOG_TAG, "raw sms message:");
    926         // Rlog.d(LOG_TAG, s);
    927 
    928         PduParser p = new PduParser(pdu);
    929 
    930         mScAddress = p.getSCAddress();
    931 
    932         if (mScAddress != null) {
    933             if (VDBG) Rlog.d(LOG_TAG, "SMS SC address: " + mScAddress);
    934         }
    935 
    936         // TODO(mkf) support reply path, user data header indicator
    937 
    938         // TP-Message-Type-Indicator
    939         // 9.2.3
    940         int firstByte = p.getByte();
    941 
    942         mMti = firstByte & 0x3;
    943         switch (mMti) {
    944         // TP-Message-Type-Indicator
    945         // 9.2.3
    946         case 0:
    947         case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
    948                 //This should be processed in the same way as MTI == 0 (Deliver)
    949             parseSmsDeliver(p, firstByte);
    950             break;
    951         case 1:
    952             parseSmsSubmit(p, firstByte);
    953             break;
    954         case 2:
    955             parseSmsStatusReport(p, firstByte);
    956             break;
    957         default:
    958             // TODO(mkf) the rest of these
    959             throw new RuntimeException("Unsupported message type");
    960         }
    961     }
    962 
    963     /**
    964      * Parses a SMS-STATUS-REPORT message.
    965      *
    966      * @param p A PduParser, cued past the first byte.
    967      * @param firstByte The first byte of the PDU, which contains MTI, etc.
    968      */
    969     private void parseSmsStatusReport(PduParser p, int firstByte) {
    970         mIsStatusReportMessage = true;
    971 
    972         // TP-Message-Reference
    973         mMessageRef = p.getByte();
    974         // TP-Recipient-Address
    975         mRecipientAddress = p.getAddress();
    976         // TP-Service-Centre-Time-Stamp
    977         mScTimeMillis = p.getSCTimestampMillis();
    978         p.getSCTimestampMillis();
    979         // TP-Status
    980         mStatus = p.getByte();
    981 
    982         // The following are optional fields that may or may not be present.
    983         if (p.moreDataPresent()) {
    984             // TP-Parameter-Indicator
    985             int extraParams = p.getByte();
    986             int moreExtraParams = extraParams;
    987             while ((moreExtraParams & 0x80) != 0) {
    988                 // We only know how to parse a few extra parameters, all
    989                 // indicated in the first TP-PI octet, so skip over any
    990                 // additional TP-PI octets.
    991                 moreExtraParams = p.getByte();
    992             }
    993             // As per 3GPP 23.040 section 9.2.3.27 TP-Parameter-Indicator,
    994             // only process the byte if the reserved bits (bits3 to 6) are zero.
    995             if ((extraParams & 0x78) == 0) {
    996                 // TP-Protocol-Identifier
    997                 if ((extraParams & 0x01) != 0) {
    998                     mProtocolIdentifier = p.getByte();
    999                 }
   1000                 // TP-Data-Coding-Scheme
   1001                 if ((extraParams & 0x02) != 0) {
   1002                     mDataCodingScheme = p.getByte();
   1003                 }
   1004                 // TP-User-Data-Length (implies existence of TP-User-Data)
   1005                 if ((extraParams & 0x04) != 0) {
   1006                     boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
   1007                     parseUserData(p, hasUserDataHeader);
   1008                 }
   1009             }
   1010         }
   1011     }
   1012 
   1013     private void parseSmsDeliver(PduParser p, int firstByte) {
   1014         mReplyPathPresent = (firstByte & 0x80) == 0x80;
   1015 
   1016         mOriginatingAddress = p.getAddress();
   1017 
   1018         if (mOriginatingAddress != null) {
   1019             if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
   1020                     + mOriginatingAddress.address);
   1021         }
   1022 
   1023         // TP-Protocol-Identifier (TP-PID)
   1024         // TS 23.040 9.2.3.9
   1025         mProtocolIdentifier = p.getByte();
   1026 
   1027         // TP-Data-Coding-Scheme
   1028         // see TS 23.038
   1029         mDataCodingScheme = p.getByte();
   1030 
   1031         if (VDBG) {
   1032             Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier
   1033                     + " data coding scheme: " + mDataCodingScheme);
   1034         }
   1035 
   1036         mScTimeMillis = p.getSCTimestampMillis();
   1037 
   1038         if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
   1039 
   1040         boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
   1041 
   1042         parseUserData(p, hasUserDataHeader);
   1043     }
   1044 
   1045     /**
   1046      * Parses a SMS-SUBMIT message.
   1047      *
   1048      * @param p A PduParser, cued past the first byte.
   1049      * @param firstByte The first byte of the PDU, which contains MTI, etc.
   1050      */
   1051     private void parseSmsSubmit(PduParser p, int firstByte) {
   1052         mReplyPathPresent = (firstByte & 0x80) == 0x80;
   1053 
   1054         // TP-MR (TP-Message Reference)
   1055         mMessageRef = p.getByte();
   1056 
   1057         mRecipientAddress = p.getAddress();
   1058 
   1059         if (mRecipientAddress != null) {
   1060             if (VDBG) Rlog.v(LOG_TAG, "SMS recipient address: " + mRecipientAddress.address);
   1061         }
   1062 
   1063         // TP-Protocol-Identifier (TP-PID)
   1064         // TS 23.040 9.2.3.9
   1065         mProtocolIdentifier = p.getByte();
   1066 
   1067         // TP-Data-Coding-Scheme
   1068         // see TS 23.038
   1069         mDataCodingScheme = p.getByte();
   1070 
   1071         if (VDBG) {
   1072             Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier
   1073                     + " data coding scheme: " + mDataCodingScheme);
   1074         }
   1075 
   1076         // TP-Validity-Period-Format
   1077         int validityPeriodLength = 0;
   1078         int validityPeriodFormat = ((firstByte>>3) & 0x3);
   1079         if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/
   1080         {
   1081             validityPeriodLength = 0;
   1082         }
   1083         else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/
   1084         {
   1085             validityPeriodLength = 1;
   1086         }
   1087         else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/
   1088         {
   1089             validityPeriodLength = 7;
   1090         }
   1091 
   1092         // TP-Validity-Period is not used on phone, so just ignore it for now.
   1093         while (validityPeriodLength-- > 0)
   1094         {
   1095             p.getByte();
   1096         }
   1097 
   1098         boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
   1099 
   1100         parseUserData(p, hasUserDataHeader);
   1101     }
   1102 
   1103     /**
   1104      * Parses the User Data of an SMS.
   1105      *
   1106      * @param p The current PduParser.
   1107      * @param hasUserDataHeader Indicates whether a header is present in the
   1108      *                          User Data.
   1109      */
   1110     private void parseUserData(PduParser p, boolean hasUserDataHeader) {
   1111         boolean hasMessageClass = false;
   1112         boolean userDataCompressed = false;
   1113 
   1114         int encodingType = ENCODING_UNKNOWN;
   1115 
   1116         // Look up the data encoding scheme
   1117         if ((mDataCodingScheme & 0x80) == 0) {
   1118             userDataCompressed = (0 != (mDataCodingScheme & 0x20));
   1119             hasMessageClass = (0 != (mDataCodingScheme & 0x10));
   1120 
   1121             if (userDataCompressed) {
   1122                 Rlog.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
   1123                         + "(compression) " + (mDataCodingScheme & 0xff));
   1124             } else {
   1125                 switch ((mDataCodingScheme >> 2) & 0x3) {
   1126                 case 0: // GSM 7 bit default alphabet
   1127                     encodingType = ENCODING_7BIT;
   1128                     break;
   1129 
   1130                 case 2: // UCS 2 (16bit)
   1131                     encodingType = ENCODING_16BIT;
   1132                     break;
   1133 
   1134                 case 1: // 8 bit data
   1135                     //Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
   1136                     //that's stored in 8-bit unpacked format) characters.
   1137                     Resources r = Resources.getSystem();
   1138                     if (r.getBoolean(com.android.internal.
   1139                             R.bool.config_sms_decode_gsm_8bit_data)) {
   1140                         encodingType = ENCODING_8BIT;
   1141                         break;
   1142                     }
   1143 
   1144                 case 3: // reserved
   1145                     Rlog.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
   1146                             + (mDataCodingScheme & 0xff));
   1147                     encodingType = ENCODING_8BIT;
   1148                     break;
   1149                 }
   1150             }
   1151         } else if ((mDataCodingScheme & 0xf0) == 0xf0) {
   1152             hasMessageClass = true;
   1153             userDataCompressed = false;
   1154 
   1155             if (0 == (mDataCodingScheme & 0x04)) {
   1156                 // GSM 7 bit default alphabet
   1157                 encodingType = ENCODING_7BIT;
   1158             } else {
   1159                 // 8 bit data
   1160                 encodingType = ENCODING_8BIT;
   1161             }
   1162         } else if ((mDataCodingScheme & 0xF0) == 0xC0
   1163                 || (mDataCodingScheme & 0xF0) == 0xD0
   1164                 || (mDataCodingScheme & 0xF0) == 0xE0) {
   1165             // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
   1166 
   1167             // 0xC0 == 7 bit, don't store
   1168             // 0xD0 == 7 bit, store
   1169             // 0xE0 == UCS-2, store
   1170 
   1171             if ((mDataCodingScheme & 0xF0) == 0xE0) {
   1172                 encodingType = ENCODING_16BIT;
   1173             } else {
   1174                 encodingType = ENCODING_7BIT;
   1175             }
   1176 
   1177             userDataCompressed = false;
   1178             boolean active = ((mDataCodingScheme & 0x08) == 0x08);
   1179             // bit 0x04 reserved
   1180 
   1181             // VM - If TP-UDH is present, these values will be overwritten
   1182             if ((mDataCodingScheme & 0x03) == 0x00) {
   1183                 mIsMwi = true; /* Indicates vmail */
   1184                 mMwiSense = active;/* Indicates vmail notification set/clear */
   1185                 mMwiDontStore = ((mDataCodingScheme & 0xF0) == 0xC0);
   1186 
   1187                 /* Set voice mail count based on notification bit */
   1188                 if (active == true) {
   1189                     mVoiceMailCount = -1; // unknown number of messages waiting
   1190                 } else {
   1191                     mVoiceMailCount = 0; // no unread messages
   1192                 }
   1193 
   1194                 Rlog.w(LOG_TAG, "MWI in DCS for Vmail. DCS = "
   1195                         + (mDataCodingScheme & 0xff) + " Dont store = "
   1196                         + mMwiDontStore + " vmail count = " + mVoiceMailCount);
   1197 
   1198             } else {
   1199                 mIsMwi = false;
   1200                 Rlog.w(LOG_TAG, "MWI in DCS for fax/email/other: "
   1201                         + (mDataCodingScheme & 0xff));
   1202             }
   1203         } else if ((mDataCodingScheme & 0xC0) == 0x80) {
   1204             // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
   1205             // 0x80..0xBF == Reserved coding groups
   1206             if (mDataCodingScheme == 0x84) {
   1207                 // This value used for KSC5601 by carriers in Korea.
   1208                 encodingType = ENCODING_KSC5601;
   1209             } else {
   1210                 Rlog.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
   1211                         + (mDataCodingScheme & 0xff));
   1212             }
   1213         } else {
   1214             Rlog.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
   1215                     + (mDataCodingScheme & 0xff));
   1216         }
   1217 
   1218         // set both the user data and the user data header.
   1219         int count = p.constructUserData(hasUserDataHeader,
   1220                 encodingType == ENCODING_7BIT);
   1221         this.mUserData = p.getUserData();
   1222         this.mUserDataHeader = p.getUserDataHeader();
   1223 
   1224         /*
   1225          * Look for voice mail indication in TP_UDH TS23.040 9.2.3.24
   1226          * ieid = 1 (0x1) (SPECIAL_SMS_MSG_IND)
   1227          * ieidl =2 octets
   1228          * ieda msg_ind_type = 0x00 (voice mail; discard sms )or
   1229          *                   = 0x80 (voice mail; store sms)
   1230          * msg_count = 0x00 ..0xFF
   1231          */
   1232         if (hasUserDataHeader && (mUserDataHeader.specialSmsMsgList.size() != 0)) {
   1233             for (SmsHeader.SpecialSmsMsg msg : mUserDataHeader.specialSmsMsgList) {
   1234                 int msgInd = msg.msgIndType & 0xff;
   1235                 /*
   1236                  * TS 23.040 V6.8.1 Sec 9.2.3.24.2
   1237                  * bits 1 0 : basic message indication type
   1238                  * bits 4 3 2 : extended message indication type
   1239                  * bits 6 5 : Profile id bit 7 storage type
   1240                  */
   1241                 if ((msgInd == 0) || (msgInd == 0x80)) {
   1242                     mIsMwi = true;
   1243                     if (msgInd == 0x80) {
   1244                         /* Store message because TP_UDH indicates so*/
   1245                         mMwiDontStore = false;
   1246                     } else if (mMwiDontStore == false) {
   1247                         /* Storage bit is not set by TP_UDH
   1248                          * Check for conflict
   1249                          * between message storage bit in TP_UDH
   1250                          * & DCS. The message shall be stored if either of
   1251                          * the one indicates so.
   1252                          * TS 23.040 V6.8.1 Sec 9.2.3.24.2
   1253                          */
   1254                         if (!((((mDataCodingScheme & 0xF0) == 0xD0)
   1255                                || ((mDataCodingScheme & 0xF0) == 0xE0))
   1256                                && ((mDataCodingScheme & 0x03) == 0x00))) {
   1257                             /* Even DCS did not have voice mail with Storage bit
   1258                              * 3GPP TS 23.038 V7.0.0 section 4
   1259                              * So clear this flag*/
   1260                             mMwiDontStore = true;
   1261                         }
   1262                     }
   1263 
   1264                     mVoiceMailCount = msg.msgCount & 0xff;
   1265 
   1266                     /*
   1267                      * In the event of a conflict between message count setting
   1268                      * and DCS then the Message Count in the TP-UDH shall
   1269                      * override the indication in the TP-DCS. Set voice mail
   1270                      * notification based on count in TP-UDH
   1271                      */
   1272                     if (mVoiceMailCount > 0)
   1273                         mMwiSense = true;
   1274                     else
   1275                         mMwiSense = false;
   1276 
   1277                     Rlog.w(LOG_TAG, "MWI in TP-UDH for Vmail. Msg Ind = " + msgInd
   1278                             + " Dont store = " + mMwiDontStore + " Vmail count = "
   1279                             + mVoiceMailCount);
   1280 
   1281                     /*
   1282                      * There can be only one IE for each type of message
   1283                      * indication in TP_UDH. In the event they are duplicated
   1284                      * last occurence will be used. Hence the for loop
   1285                      */
   1286                 } else {
   1287                     Rlog.w(LOG_TAG, "TP_UDH fax/email/"
   1288                             + "extended msg/multisubscriber profile. Msg Ind = " + msgInd);
   1289                 }
   1290             } // end of for
   1291         } // end of if UDH
   1292 
   1293         switch (encodingType) {
   1294         case ENCODING_UNKNOWN:
   1295             mMessageBody = null;
   1296             break;
   1297 
   1298         case ENCODING_8BIT:
   1299             //Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
   1300             //that's stored in 8-bit unpacked format) characters.
   1301             Resources r = Resources.getSystem();
   1302             if (r.getBoolean(com.android.internal.
   1303                     R.bool.config_sms_decode_gsm_8bit_data)) {
   1304                 mMessageBody = p.getUserDataGSM8bit(count);
   1305             } else {
   1306                 mMessageBody = null;
   1307             }
   1308             break;
   1309 
   1310         case ENCODING_7BIT:
   1311             mMessageBody = p.getUserDataGSM7Bit(count,
   1312                     hasUserDataHeader ? mUserDataHeader.languageTable : 0,
   1313                     hasUserDataHeader ? mUserDataHeader.languageShiftTable : 0);
   1314             break;
   1315 
   1316         case ENCODING_16BIT:
   1317             mMessageBody = p.getUserDataUCS2(count);
   1318             break;
   1319 
   1320         case ENCODING_KSC5601:
   1321             mMessageBody = p.getUserDataKSC5601(count);
   1322             break;
   1323         }
   1324 
   1325         if (VDBG) Rlog.v(LOG_TAG, "SMS message body (raw): '" + mMessageBody + "'");
   1326 
   1327         if (mMessageBody != null) {
   1328             parseMessageBody();
   1329         }
   1330 
   1331         if (!hasMessageClass) {
   1332             messageClass = MessageClass.UNKNOWN;
   1333         } else {
   1334             switch (mDataCodingScheme & 0x3) {
   1335             case 0:
   1336                 messageClass = MessageClass.CLASS_0;
   1337                 break;
   1338             case 1:
   1339                 messageClass = MessageClass.CLASS_1;
   1340                 break;
   1341             case 2:
   1342                 messageClass = MessageClass.CLASS_2;
   1343                 break;
   1344             case 3:
   1345                 messageClass = MessageClass.CLASS_3;
   1346                 break;
   1347             }
   1348         }
   1349     }
   1350 
   1351     /**
   1352      * {@inheritDoc}
   1353      */
   1354     @Override
   1355     public MessageClass getMessageClass() {
   1356         return messageClass;
   1357     }
   1358 
   1359     /**
   1360      * Returns true if this is a (U)SIM data download type SM.
   1361      * See 3GPP TS 31.111 section 9.1 and TS 23.040 section 9.2.3.9.
   1362      *
   1363      * @return true if this is a USIM data download message; false otherwise
   1364      */
   1365     boolean isUsimDataDownload() {
   1366         return messageClass == MessageClass.CLASS_2 &&
   1367                 (mProtocolIdentifier == 0x7f || mProtocolIdentifier == 0x7c);
   1368     }
   1369 
   1370     public int getNumOfVoicemails() {
   1371         /*
   1372          * Order of priority if multiple indications are present is 1.UDH,
   1373          *      2.DCS, 3.CPHS.
   1374          * Voice mail count if voice mail present indication is
   1375          * received
   1376          *  1. UDH (or both UDH & DCS): mVoiceMailCount = 0 to 0xff. Ref[TS 23. 040]
   1377          *  2. DCS only: count is unknown mVoiceMailCount= -1
   1378          *  3. CPHS only: count is unknown mVoiceMailCount = 0xff. Ref[GSM-BTR-1-4700]
   1379          * Voice mail clear, mVoiceMailCount = 0.
   1380          */
   1381         if ((!mIsMwi) && isCphsMwiMessage()) {
   1382             if (mOriginatingAddress != null
   1383                     && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet()) {
   1384                 mVoiceMailCount = 0xff;
   1385             } else {
   1386                 mVoiceMailCount = 0;
   1387             }
   1388             Rlog.v(LOG_TAG, "CPHS voice mail message");
   1389         }
   1390         return mVoiceMailCount;
   1391     }
   1392 }
   1393