Home | History | Annotate | Download | only in cdma
      1 /*
      2  * Copyright (C) 2008 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.cdma;
     18 
     19 import android.os.Parcel;
     20 import android.os.SystemProperties;
     21 import android.telephony.PhoneNumberUtils;
     22 import android.telephony.SmsCbLocation;
     23 import android.telephony.SmsCbMessage;
     24 import android.telephony.cdma.CdmaSmsCbProgramData;
     25 import android.telephony.Rlog;
     26 import android.util.Log;
     27 
     28 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
     29 import com.android.internal.telephony.SmsConstants;
     30 import com.android.internal.telephony.SmsHeader;
     31 import com.android.internal.telephony.SmsMessageBase;
     32 import com.android.internal.telephony.TelephonyProperties;
     33 import com.android.internal.telephony.cdma.sms.BearerData;
     34 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
     35 import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
     36 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
     37 import com.android.internal.telephony.cdma.sms.UserData;
     38 import com.android.internal.telephony.uicc.IccUtils;
     39 import com.android.internal.util.BitwiseInputStream;
     40 import com.android.internal.util.HexDump;
     41 
     42 import java.io.BufferedOutputStream;
     43 import java.io.ByteArrayInputStream;
     44 import java.io.ByteArrayOutputStream;
     45 import java.io.DataInputStream;
     46 import java.io.DataOutputStream;
     47 import java.io.IOException;
     48 import java.util.ArrayList;
     49 
     50 /**
     51  * TODO(cleanup): these constants are disturbing... are they not just
     52  * different interpretations on one number?  And if we did not have
     53  * terrible class name overlap, they would not need to be directly
     54  * imported like this.  The class in this file could just as well be
     55  * named CdmaSmsMessage, could it not?
     56  */
     57 
     58 /**
     59  * TODO(cleanup): internally returning null in many places makes
     60  * debugging very hard (among many other reasons) and should be made
     61  * more meaningful (replaced with exceptions for example).  Null
     62  * returns should only occur at the very outside of the module/class
     63  * scope.
     64  */
     65 
     66 /**
     67  * A Short Message Service message.
     68  *
     69  */
     70 public class SmsMessage extends SmsMessageBase {
     71     static final String LOG_TAG = "SmsMessage";
     72     static private final String LOGGABLE_TAG = "CDMA:SMS";
     73     private static final boolean VDBG = false;
     74 
     75     private final static byte TELESERVICE_IDENTIFIER                    = 0x00;
     76     private final static byte SERVICE_CATEGORY                          = 0x01;
     77     private final static byte ORIGINATING_ADDRESS                       = 0x02;
     78     private final static byte ORIGINATING_SUB_ADDRESS                   = 0x03;
     79     private final static byte DESTINATION_ADDRESS                       = 0x04;
     80     private final static byte DESTINATION_SUB_ADDRESS                   = 0x05;
     81     private final static byte BEARER_REPLY_OPTION                       = 0x06;
     82     private final static byte CAUSE_CODES                               = 0x07;
     83     private final static byte BEARER_DATA                               = 0x08;
     84 
     85     /**
     86      *  Status of a previously submitted SMS.
     87      *  This field applies to SMS Delivery Acknowledge messages. 0 indicates success;
     88      *  Here, the error class is defined by the bits from 9-8, the status code by the bits from 7-0.
     89      *  See C.S0015-B, v2.0, 4.5.21 for a detailed description of possible values.
     90      */
     91     private int status;
     92 
     93     /** Specifies if a return of an acknowledgment is requested for send SMS */
     94     private static final int RETURN_NO_ACK  = 0;
     95     private static final int RETURN_ACK     = 1;
     96 
     97     private SmsEnvelope mEnvelope;
     98     private BearerData mBearerData;
     99 
    100     public static class SubmitPdu extends SubmitPduBase {
    101     }
    102 
    103     /**
    104      * Create an SmsMessage from a raw PDU.
    105      * Note: In CDMA the PDU is just a byte representation of the received Sms.
    106      */
    107     public static SmsMessage createFromPdu(byte[] pdu) {
    108         SmsMessage msg = new SmsMessage();
    109 
    110         try {
    111             msg.parsePdu(pdu);
    112             return msg;
    113         } catch (RuntimeException ex) {
    114             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
    115             return null;
    116         }
    117     }
    118 
    119     /**
    120      *  Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp.
    121      *  Note: Only primitive fields are set.
    122      */
    123     public static SmsMessage newFromParcel(Parcel p) {
    124         // Note: Parcel.readByte actually reads one Int and masks to byte
    125         SmsMessage msg = new SmsMessage();
    126         SmsEnvelope env = new SmsEnvelope();
    127         CdmaSmsAddress addr = new CdmaSmsAddress();
    128         CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
    129         byte[] data;
    130         byte count;
    131         int countInt;
    132         int addressDigitMode;
    133 
    134         //currently not supported by the modem-lib: env.mMessageType
    135         env.teleService = p.readInt(); //p_cur->uTeleserviceID
    136 
    137         if (0 != p.readByte()) { //p_cur->bIsServicePresent
    138             env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST;
    139         }
    140         else {
    141             if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) {
    142                 // assume type ACK
    143                 env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE;
    144             } else {
    145                 env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
    146             }
    147         }
    148         env.serviceCategory = p.readInt(); //p_cur->uServicecategory
    149 
    150         // address
    151         addressDigitMode = p.readInt();
    152         addr.digitMode = (byte) (0xFF & addressDigitMode); //p_cur->sAddress.digit_mode
    153         addr.numberMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_mode
    154         addr.ton = p.readInt(); //p_cur->sAddress.number_type
    155         addr.numberPlan = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_plan
    156         count = p.readByte(); //p_cur->sAddress.number_of_digits
    157         addr.numberOfDigits = count;
    158         data = new byte[count];
    159         //p_cur->sAddress.digits[digitCount]
    160         for (int index=0; index < count; index++) {
    161             data[index] = p.readByte();
    162 
    163             // convert the value if it is 4-bit DTMF to 8 bit
    164             if (addressDigitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
    165                 data[index] = msg.convertDtmfToAscii(data[index]);
    166             }
    167         }
    168 
    169         addr.origBytes = data;
    170 
    171         subaddr.type = p.readInt(); // p_cur->sSubAddress.subaddressType
    172         subaddr.odd = p.readByte();     // p_cur->sSubAddress.odd
    173         count = p.readByte();           // p_cur->sSubAddress.number_of_digits
    174 
    175         if (count < 0) {
    176             count = 0;
    177         }
    178 
    179         // p_cur->sSubAddress.digits[digitCount] :
    180 
    181         data = new byte[count];
    182 
    183         for (int index = 0; index < count; ++index) {
    184             data[index] = p.readByte();
    185         }
    186 
    187         subaddr.origBytes = data;
    188 
    189         /* currently not supported by the modem-lib:
    190             env.bearerReply
    191             env.replySeqNo
    192             env.errorClass
    193             env.causeCode
    194         */
    195 
    196         // bearer data
    197         countInt = p.readInt(); //p_cur->uBearerDataLen
    198         if (countInt < 0) {
    199             countInt = 0;
    200         }
    201 
    202         data = new byte[countInt];
    203         for (int index=0; index < countInt; index++) {
    204             data[index] = p.readByte();
    205         }
    206         // BD gets further decoded when accessed in SMSDispatcher
    207         env.bearerData = data;
    208 
    209         // link the the filled objects to the SMS
    210         env.origAddress = addr;
    211         env.origSubaddress = subaddr;
    212         msg.mOriginatingAddress = addr;
    213         msg.mEnvelope = env;
    214 
    215         // create byte stream representation for transportation through the layers.
    216         msg.createPdu();
    217 
    218         return msg;
    219     }
    220 
    221     /**
    222      * Create an SmsMessage from an SMS EF record.
    223      *
    224      * @param index Index of SMS record. This should be index in ArrayList
    225      *              returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1.
    226      * @param data Record data.
    227      * @return An SmsMessage representing the record.
    228      *
    229      * @hide
    230      */
    231     public static SmsMessage createFromEfRecord(int index, byte[] data) {
    232         try {
    233             SmsMessage msg = new SmsMessage();
    234 
    235             msg.mIndexOnIcc = index;
    236 
    237             // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
    238             // or STORED_UNSENT
    239             // See 3GPP2 C.S0023 3.4.27
    240             if ((data[0] & 1) == 0) {
    241                 Rlog.w(LOG_TAG, "SMS parsing failed: Trying to parse a free record");
    242                 return null;
    243             } else {
    244                 msg.mStatusOnIcc = data[0] & 0x07;
    245             }
    246 
    247             // Second byte is the MSG_LEN, length of the message
    248             // See 3GPP2 C.S0023 3.4.27
    249             int size = data[1];
    250 
    251             // Note: Data may include trailing FF's.  That's OK; message
    252             // should still parse correctly.
    253             byte[] pdu = new byte[size];
    254             System.arraycopy(data, 2, pdu, 0, size);
    255             // the message has to be parsed before it can be displayed
    256             // see gsm.SmsMessage
    257             msg.parsePduFromEfRecord(pdu);
    258             return msg;
    259         } catch (RuntimeException ex) {
    260             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
    261             return null;
    262         }
    263 
    264     }
    265 
    266     /**
    267      * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
    268      */
    269     public static int getTPLayerLengthForPDU(String pdu) {
    270         Rlog.w(LOG_TAG, "getTPLayerLengthForPDU: is not supported in CDMA mode.");
    271         return 0;
    272     }
    273 
    274     /**
    275      * TODO(cleanup): why do getSubmitPdu methods take an scAddr input
    276      * and do nothing with it?  GSM allows us to specify a SC (eg,
    277      * when responding to an SMS that explicitly requests the response
    278      * is sent to a specific SC), or pass null to use the default
    279      * value.  Is there no similar notion in CDMA? Or do we just not
    280      * have it hooked up?
    281      */
    282 
    283     /**
    284      * Get an SMS-SUBMIT PDU for a destination address and a message
    285      *
    286      * @param scAddr                Service Centre address.  Null means use default.
    287      * @param destAddr              Address of the recipient.
    288      * @param message               String representation of the message payload.
    289      * @param statusReportRequested Indicates whether a report is requested for this message.
    290      * @param smsHeader             Array containing the data for the User Data Header, preceded
    291      *                              by the Element Identifiers.
    292      * @return a <code>SubmitPdu</code> containing the encoded SC
    293      *         address, if applicable, and the encoded message.
    294      *         Returns null on encode error.
    295      * @hide
    296      */
    297     public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
    298             boolean statusReportRequested, SmsHeader smsHeader) {
    299 
    300         /**
    301          * TODO(cleanup): Do we really want silent failure like this?
    302          * Would it not be much more reasonable to make sure we don't
    303          * call this function if we really want nothing done?
    304          */
    305         if (message == null || destAddr == null) {
    306             return null;
    307         }
    308 
    309         UserData uData = new UserData();
    310         uData.payloadStr = message;
    311         uData.userDataHeader = smsHeader;
    312         return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
    313     }
    314 
    315     /**
    316      * Get an SMS-SUBMIT PDU for a data message to a destination address and port.
    317      *
    318      * @param scAddr Service Centre address. null == use default
    319      * @param destAddr the address of the destination for the message
    320      * @param destPort the port to deliver the message to at the
    321      *        destination
    322      * @param data the data for the message
    323      * @return a <code>SubmitPdu</code> containing the encoded SC
    324      *         address, if applicable, and the encoded message.
    325      *         Returns null on encode error.
    326      */
    327     public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
    328             byte[] data, boolean statusReportRequested) {
    329 
    330         /**
    331          * TODO(cleanup): this is not a general-purpose SMS creation
    332          * method, but rather something specialized to messages
    333          * containing OCTET encoded (meaning non-human-readable) user
    334          * data.  The name should reflect that, and not just overload.
    335          */
    336 
    337         SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
    338         portAddrs.destPort = destPort;
    339         portAddrs.origPort = 0;
    340         portAddrs.areEightBits = false;
    341 
    342         SmsHeader smsHeader = new SmsHeader();
    343         smsHeader.portAddrs = portAddrs;
    344 
    345         UserData uData = new UserData();
    346         uData.userDataHeader = smsHeader;
    347         uData.msgEncoding = UserData.ENCODING_OCTET;
    348         uData.msgEncodingSet = true;
    349         uData.payload = data;
    350 
    351         return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
    352     }
    353 
    354     /**
    355      * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
    356      *
    357      * @param destAddr the address of the destination for the message
    358      * @param userData the data for the message
    359      * @param statusReportRequested Indicates whether a report is requested for this message.
    360      * @return a <code>SubmitPdu</code> containing the encoded SC
    361      *         address, if applicable, and the encoded message.
    362      *         Returns null on encode error.
    363      */
    364     public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
    365             boolean statusReportRequested) {
    366         return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
    367     }
    368 
    369     /**
    370      * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
    371      */
    372     @Override
    373     public int getProtocolIdentifier() {
    374         Rlog.w(LOG_TAG, "getProtocolIdentifier: is not supported in CDMA mode.");
    375         // (3GPP TS 23.040): "no interworking, but SME to SME protocol":
    376         return 0;
    377     }
    378 
    379     /**
    380      * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
    381      */
    382     @Override
    383     public boolean isReplace() {
    384         Rlog.w(LOG_TAG, "isReplace: is not supported in CDMA mode.");
    385         return false;
    386     }
    387 
    388     /**
    389      * {@inheritDoc}
    390      * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
    391      */
    392     @Override
    393     public boolean isCphsMwiMessage() {
    394         Rlog.w(LOG_TAG, "isCphsMwiMessage: is not supported in CDMA mode.");
    395         return false;
    396     }
    397 
    398     /**
    399      * {@inheritDoc}
    400      */
    401     @Override
    402     public boolean isMWIClearMessage() {
    403         return ((mBearerData != null) && (mBearerData.numberOfMessages == 0));
    404     }
    405 
    406     /**
    407      * {@inheritDoc}
    408      */
    409     @Override
    410     public boolean isMWISetMessage() {
    411         return ((mBearerData != null) && (mBearerData.numberOfMessages > 0));
    412     }
    413 
    414     /**
    415      * {@inheritDoc}
    416      */
    417     @Override
    418     public boolean isMwiDontStore() {
    419         return ((mBearerData != null) &&
    420                 (mBearerData.numberOfMessages > 0) &&
    421                 (mBearerData.userData == null));
    422     }
    423 
    424     /**
    425      * Returns the status for a previously submitted message.
    426      * For not interfering with status codes from GSM, this status code is
    427      * shifted to the bits 31-16.
    428      */
    429     @Override
    430     public int getStatus() {
    431         return (status << 16);
    432     }
    433 
    434     /** Return true iff the bearer data message type is DELIVERY_ACK. */
    435     @Override
    436     public boolean isStatusReportMessage() {
    437         return (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK);
    438     }
    439 
    440     /**
    441      * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
    442      */
    443     @Override
    444     public boolean isReplyPathPresent() {
    445         Rlog.w(LOG_TAG, "isReplyPathPresent: is not supported in CDMA mode.");
    446         return false;
    447     }
    448 
    449     /**
    450      * Calculate the number of septets needed to encode the message.
    451      *
    452      * @param messageBody the message to encode
    453      * @param use7bitOnly ignore (but still count) illegal characters if true
    454      * @return TextEncodingDetails
    455      */
    456     public static TextEncodingDetails calculateLength(CharSequence messageBody,
    457             boolean use7bitOnly) {
    458         return BearerData.calcTextEncodingDetails(messageBody, use7bitOnly);
    459     }
    460 
    461     /**
    462      * Returns the teleservice type of the message.
    463      * @return the teleservice:
    464      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET},
    465      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WMT},
    466      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WEMT},
    467      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN},
    468      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP}
    469     */
    470     /* package */ int getTeleService() {
    471         return mEnvelope.teleService;
    472     }
    473 
    474     /**
    475      * Returns the message type of the message.
    476      * @return the message type:
    477      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_POINT_TO_POINT},
    478      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_BROADCAST},
    479      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_ACKNOWLEDGE},
    480     */
    481     /* package */ int getMessageType() {
    482         // NOTE: mEnvelope.messageType is not set correctly for cell broadcasts with some RILs.
    483         // Use the service category parameter to detect CMAS and other cell broadcast messages.
    484         if (mEnvelope.serviceCategory != 0) {
    485             return SmsEnvelope.MESSAGE_TYPE_BROADCAST;
    486         } else {
    487             return SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
    488         }
    489     }
    490 
    491     /**
    492      * Decodes pdu to an empty SMS object.
    493      * In the CDMA case the pdu is just an internal byte stream representation
    494      * of the SMS Java-object.
    495      * @see #createPdu()
    496      */
    497     private void parsePdu(byte[] pdu) {
    498         ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
    499         DataInputStream dis = new DataInputStream(bais);
    500         int length;
    501         int bearerDataLength;
    502         SmsEnvelope env = new SmsEnvelope();
    503         CdmaSmsAddress addr = new CdmaSmsAddress();
    504 
    505         try {
    506             env.messageType = dis.readInt();
    507             env.teleService = dis.readInt();
    508             env.serviceCategory = dis.readInt();
    509 
    510             addr.digitMode = dis.readByte();
    511             addr.numberMode = dis.readByte();
    512             addr.ton = dis.readByte();
    513             addr.numberPlan = dis.readByte();
    514 
    515             length = dis.readUnsignedByte();
    516             addr.numberOfDigits = length;
    517             addr.origBytes = new byte[length];
    518             dis.read(addr.origBytes, 0, length); // digits
    519 
    520             env.bearerReply = dis.readInt();
    521             // CauseCode values:
    522             env.replySeqNo = dis.readByte();
    523             env.errorClass = dis.readByte();
    524             env.causeCode = dis.readByte();
    525 
    526             //encoded BearerData:
    527             bearerDataLength = dis.readInt();
    528             env.bearerData = new byte[bearerDataLength];
    529             dis.read(env.bearerData, 0, bearerDataLength);
    530             dis.close();
    531         } catch (Exception ex) {
    532             Rlog.e(LOG_TAG, "createFromPdu: conversion from byte array to object failed: " + ex);
    533         }
    534 
    535         // link the filled objects to this SMS
    536         mOriginatingAddress = addr;
    537         env.origAddress = addr;
    538         mEnvelope = env;
    539         mPdu = pdu;
    540 
    541         parseSms();
    542     }
    543 
    544     /**
    545      * Decodes 3GPP2 sms stored in CSIM/RUIM cards As per 3GPP2 C.S0015-0
    546      */
    547     private void parsePduFromEfRecord(byte[] pdu) {
    548         ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
    549         DataInputStream dis = new DataInputStream(bais);
    550         SmsEnvelope env = new SmsEnvelope();
    551         CdmaSmsAddress addr = new CdmaSmsAddress();
    552         CdmaSmsSubaddress subAddr = new CdmaSmsSubaddress();
    553 
    554         try {
    555             env.messageType = dis.readByte();
    556 
    557             while (dis.available() > 0) {
    558                 int parameterId = dis.readByte();
    559                 int parameterLen = dis.readUnsignedByte();
    560                 byte[] parameterData = new byte[parameterLen];
    561 
    562                 switch (parameterId) {
    563                     case TELESERVICE_IDENTIFIER:
    564                         /*
    565                          * 16 bit parameter that identifies which upper layer
    566                          * service access point is sending or should receive
    567                          * this message
    568                          */
    569                         env.teleService = dis.readUnsignedShort();
    570                         Rlog.i(LOG_TAG, "teleservice = " + env.teleService);
    571                         break;
    572                     case SERVICE_CATEGORY:
    573                         /*
    574                          * 16 bit parameter that identifies type of service as
    575                          * in 3GPP2 C.S0015-0 Table 3.4.3.2-1
    576                          */
    577                         env.serviceCategory = dis.readUnsignedShort();
    578                         break;
    579                     case ORIGINATING_ADDRESS:
    580                     case DESTINATION_ADDRESS:
    581                         dis.read(parameterData, 0, parameterLen);
    582                         BitwiseInputStream addrBis = new BitwiseInputStream(parameterData);
    583                         addr.digitMode = addrBis.read(1);
    584                         addr.numberMode = addrBis.read(1);
    585                         int numberType = 0;
    586                         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
    587                             numberType = addrBis.read(3);
    588                             addr.ton = numberType;
    589 
    590                             if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK)
    591                                 addr.numberPlan = addrBis.read(4);
    592                         }
    593 
    594                         addr.numberOfDigits = addrBis.read(8);
    595 
    596                         byte[] data = new byte[addr.numberOfDigits];
    597                         byte b = 0x00;
    598 
    599                         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
    600                             /* As per 3GPP2 C.S0005-0 Table 2.7.1.3.2.4-4 */
    601                             for (int index = 0; index < addr.numberOfDigits; index++) {
    602                                 b = (byte) (0xF & addrBis.read(4));
    603                                 // convert the value if it is 4-bit DTMF to 8
    604                                 // bit
    605                                 data[index] = convertDtmfToAscii(b);
    606                             }
    607                         } else if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
    608                             if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK) {
    609                                 for (int index = 0; index < addr.numberOfDigits; index++) {
    610                                     b = (byte) (0xFF & addrBis.read(8));
    611                                     data[index] = b;
    612                                 }
    613 
    614                             } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
    615                                 if (numberType == 2)
    616                                     Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
    617                                 else
    618                                     Rlog.e(LOG_TAG,
    619                                           "TODO: Originating Addr is data network address");
    620                             } else {
    621                                 Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
    622                             }
    623                         } else {
    624                             Rlog.e(LOG_TAG, "Incorrect Digit mode");
    625                         }
    626                         addr.origBytes = data;
    627                         Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
    628                         break;
    629                     case ORIGINATING_SUB_ADDRESS:
    630                     case DESTINATION_SUB_ADDRESS:
    631                         dis.read(parameterData, 0, parameterLen);
    632                         BitwiseInputStream subAddrBis = new BitwiseInputStream(parameterData);
    633                         subAddr.type = subAddrBis.read(3);
    634                         subAddr.odd = subAddrBis.readByteArray(1)[0];
    635                         int subAddrLen = subAddrBis.read(8);
    636                         byte[] subdata = new byte[subAddrLen];
    637                         for (int index = 0; index < subAddrLen; index++) {
    638                             b = (byte) (0xFF & subAddrBis.read(4));
    639                             // convert the value if it is 4-bit DTMF to 8 bit
    640                             subdata[index] = convertDtmfToAscii(b);
    641                         }
    642                         subAddr.origBytes = subdata;
    643                         break;
    644                     case BEARER_REPLY_OPTION:
    645                         dis.read(parameterData, 0, parameterLen);
    646                         BitwiseInputStream replyOptBis = new BitwiseInputStream(parameterData);
    647                         env.bearerReply = replyOptBis.read(6);
    648                         break;
    649                     case CAUSE_CODES:
    650                         dis.read(parameterData, 0, parameterLen);
    651                         BitwiseInputStream ccBis = new BitwiseInputStream(parameterData);
    652                         env.replySeqNo = ccBis.readByteArray(6)[0];
    653                         env.errorClass = ccBis.readByteArray(2)[0];
    654                         if (env.errorClass != 0x00)
    655                             env.causeCode = ccBis.readByteArray(8)[0];
    656                         break;
    657                     case BEARER_DATA:
    658                         dis.read(parameterData, 0, parameterLen);
    659                         env.bearerData = parameterData;
    660                         break;
    661                     default:
    662                         throw new Exception("unsupported parameterId (" + parameterId + ")");
    663                 }
    664             }
    665             bais.close();
    666             dis.close();
    667         } catch (Exception ex) {
    668             Rlog.e(LOG_TAG, "parsePduFromEfRecord: conversion from pdu to SmsMessage failed" + ex);
    669         }
    670 
    671         // link the filled objects to this SMS
    672         mOriginatingAddress = addr;
    673         env.origAddress = addr;
    674         env.origSubaddress = subAddr;
    675         mEnvelope = env;
    676         mPdu = pdu;
    677 
    678         parseSms();
    679     }
    680 
    681     /**
    682      * Parses a SMS message from its BearerData stream. (mobile-terminated only)
    683      */
    684     protected void parseSms() {
    685         // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
    686         // It contains only an 8-bit number with the number of messages waiting
    687         if (mEnvelope.teleService == SmsEnvelope.TELESERVICE_MWI) {
    688             mBearerData = new BearerData();
    689             if (mEnvelope.bearerData != null) {
    690                 mBearerData.numberOfMessages = 0x000000FF & mEnvelope.bearerData[0];
    691             }
    692             if (VDBG) {
    693                 Rlog.d(LOG_TAG, "parseSms: get MWI " +
    694                       Integer.toString(mBearerData.numberOfMessages));
    695             }
    696             return;
    697         }
    698         mBearerData = BearerData.decode(mEnvelope.bearerData);
    699         if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
    700             Rlog.d(LOG_TAG, "MT raw BearerData = '" +
    701                       HexDump.toHexString(mEnvelope.bearerData) + "'");
    702             Rlog.d(LOG_TAG, "MT (decoded) BearerData = " + mBearerData);
    703         }
    704         mMessageRef = mBearerData.messageId;
    705         if (mBearerData.userData != null) {
    706             mUserData = mBearerData.userData.payload;
    707             mUserDataHeader = mBearerData.userData.userDataHeader;
    708             mMessageBody = mBearerData.userData.payloadStr;
    709         }
    710 
    711         if (mOriginatingAddress != null) {
    712             mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
    713             if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
    714                     + mOriginatingAddress.address);
    715         }
    716 
    717         if (mBearerData.msgCenterTimeStamp != null) {
    718             mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
    719         }
    720 
    721         if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
    722 
    723         // Message Type (See 3GPP2 C.S0015-B, v2, 4.5.1)
    724         if (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK) {
    725             // The BearerData MsgStatus subparameter should only be
    726             // included for DELIVERY_ACK messages.  If it occurred for
    727             // other messages, it would be unclear what the status
    728             // being reported refers to.  The MsgStatus subparameter
    729             // is primarily useful to indicate error conditions -- a
    730             // message without this subparameter is assumed to
    731             // indicate successful delivery (status == 0).
    732             if (! mBearerData.messageStatusSet) {
    733                 Rlog.d(LOG_TAG, "DELIVERY_ACK message without msgStatus (" +
    734                         (mUserData == null ? "also missing" : "does have") +
    735                         " userData).");
    736                 status = 0;
    737             } else {
    738                 status = mBearerData.errorClass << 8;
    739                 status |= mBearerData.messageStatus;
    740             }
    741         } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
    742             throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
    743         }
    744 
    745         if (mMessageBody != null) {
    746             if (VDBG) Rlog.v(LOG_TAG, "SMS message body: '" + mMessageBody + "'");
    747             parseMessageBody();
    748         } else if ((mUserData != null) && VDBG) {
    749             Rlog.v(LOG_TAG, "SMS payload: '" + IccUtils.bytesToHexString(mUserData) + "'");
    750         }
    751     }
    752 
    753     /**
    754      * Parses a broadcast SMS, possibly containing a CMAS alert.
    755      */
    756     SmsCbMessage parseBroadcastSms() {
    757         BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
    758         if (bData == null) {
    759             Rlog.w(LOG_TAG, "BearerData.decode() returned null");
    760             return null;
    761         }
    762 
    763         if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
    764             Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData));
    765         }
    766 
    767         String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
    768         SmsCbLocation location = new SmsCbLocation(plmn);
    769 
    770         return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
    771                 SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location,
    772                 mEnvelope.serviceCategory, bData.getLanguage(), bData.userData.payloadStr,
    773                 bData.priority, null, bData.cmasWarningInfo);
    774     }
    775 
    776     /**
    777      * {@inheritDoc}
    778      */
    779     @Override
    780     public SmsConstants.MessageClass getMessageClass() {
    781         if (BearerData.DISPLAY_MODE_IMMEDIATE == mBearerData.displayMode ) {
    782             return SmsConstants.MessageClass.CLASS_0;
    783         } else {
    784             return SmsConstants.MessageClass.UNKNOWN;
    785         }
    786     }
    787 
    788     /**
    789      * Calculate the next message id, starting at 1 and iteratively
    790      * incrementing within the range 1..65535 remembering the state
    791      * via a persistent system property.  (See C.S0015-B, v2.0,
    792      * 4.3.1.5) Since this routine is expected to be accessed via via
    793      * binder-call, and hence should be thread-safe, it has been
    794      * synchronized.
    795      */
    796     synchronized static int getNextMessageId() {
    797         // Testing and dialog with partners has indicated that
    798         // msgId==0 is (sometimes?) treated specially by lower levels.
    799         // Specifically, the ID is not preserved for delivery ACKs.
    800         // Hence, avoid 0 -- constraining the range to 1..65535.
    801         int msgId = SystemProperties.getInt(TelephonyProperties.PROPERTY_CDMA_MSG_ID, 1);
    802         String nextMsgId = Integer.toString((msgId % 0xFFFF) + 1);
    803         SystemProperties.set(TelephonyProperties.PROPERTY_CDMA_MSG_ID, nextMsgId);
    804         if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
    805             Rlog.d(LOG_TAG, "next " + TelephonyProperties.PROPERTY_CDMA_MSG_ID + " = " + nextMsgId);
    806             Rlog.d(LOG_TAG, "readback gets " +
    807                     SystemProperties.get(TelephonyProperties.PROPERTY_CDMA_MSG_ID));
    808         }
    809         return msgId;
    810     }
    811 
    812     /**
    813      * Creates BearerData and Envelope from parameters for a Submit SMS.
    814      * @return byte stream for SubmitPdu.
    815      */
    816     private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
    817             UserData userData) {
    818 
    819         /**
    820          * TODO(cleanup): give this function a more meaningful name.
    821          */
    822 
    823         /**
    824          * TODO(cleanup): Make returning null from the getSubmitPdu
    825          * variations meaningful -- clean up the error feedback
    826          * mechanism, and avoid null pointer exceptions.
    827          */
    828 
    829         /**
    830          * North America Plus Code :
    831          * Convert + code to 011 and dial out for international SMS
    832          */
    833         CdmaSmsAddress destAddr = CdmaSmsAddress.parse(
    834                 PhoneNumberUtils.cdmaCheckAndProcessPlusCode(destAddrStr));
    835         if (destAddr == null) return null;
    836 
    837         BearerData bearerData = new BearerData();
    838         bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
    839 
    840         bearerData.messageId = getNextMessageId();
    841 
    842         bearerData.deliveryAckReq = statusReportRequested;
    843         bearerData.userAckReq = false;
    844         bearerData.readAckReq = false;
    845         bearerData.reportReq = false;
    846 
    847         bearerData.userData = userData;
    848 
    849         byte[] encodedBearerData = BearerData.encode(bearerData);
    850         if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
    851             Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
    852             Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
    853         }
    854         if (encodedBearerData == null) return null;
    855 
    856         int teleservice = bearerData.hasUserDataHeader ?
    857                 SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
    858 
    859         SmsEnvelope envelope = new SmsEnvelope();
    860         envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
    861         envelope.teleService = teleservice;
    862         envelope.destAddress = destAddr;
    863         envelope.bearerReply = RETURN_ACK;
    864         envelope.bearerData = encodedBearerData;
    865 
    866         /**
    867          * TODO(cleanup): envelope looks to be a pointless class, get
    868          * rid of it.  Also -- most of the envelope fields set here
    869          * are ignored, why?
    870          */
    871 
    872         try {
    873             /**
    874              * TODO(cleanup): reference a spec and get rid of the ugly comments
    875              */
    876             ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
    877             DataOutputStream dos = new DataOutputStream(baos);
    878             dos.writeInt(envelope.teleService);
    879             dos.writeInt(0); //servicePresent
    880             dos.writeInt(0); //serviceCategory
    881             dos.write(destAddr.digitMode);
    882             dos.write(destAddr.numberMode);
    883             dos.write(destAddr.ton); // number_type
    884             dos.write(destAddr.numberPlan);
    885             dos.write(destAddr.numberOfDigits);
    886             dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
    887             // Subaddress is not supported.
    888             dos.write(0); //subaddressType
    889             dos.write(0); //subaddr_odd
    890             dos.write(0); //subaddr_nbr_of_digits
    891             dos.write(encodedBearerData.length);
    892             dos.write(encodedBearerData, 0, encodedBearerData.length);
    893             dos.close();
    894 
    895             SubmitPdu pdu = new SubmitPdu();
    896             pdu.encodedMessage = baos.toByteArray();
    897             pdu.encodedScAddress = null;
    898             return pdu;
    899         } catch(IOException ex) {
    900             Rlog.e(LOG_TAG, "creating SubmitPdu failed: " + ex);
    901         }
    902         return null;
    903     }
    904 
    905     /**
    906      * Creates byte array (pseudo pdu) from SMS object.
    907      * Note: Do not call this method more than once per object!
    908      */
    909     private void createPdu() {
    910         SmsEnvelope env = mEnvelope;
    911         CdmaSmsAddress addr = env.origAddress;
    912         ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
    913         DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
    914 
    915         try {
    916             dos.writeInt(env.messageType);
    917             dos.writeInt(env.teleService);
    918             dos.writeInt(env.serviceCategory);
    919 
    920             dos.writeByte(addr.digitMode);
    921             dos.writeByte(addr.numberMode);
    922             dos.writeByte(addr.ton);
    923             dos.writeByte(addr.numberPlan);
    924             dos.writeByte(addr.numberOfDigits);
    925             dos.write(addr.origBytes, 0, addr.origBytes.length); // digits
    926 
    927             dos.writeInt(env.bearerReply);
    928             // CauseCode values:
    929             dos.writeByte(env.replySeqNo);
    930             dos.writeByte(env.errorClass);
    931             dos.writeByte(env.causeCode);
    932             //encoded BearerData:
    933             dos.writeInt(env.bearerData.length);
    934             dos.write(env.bearerData, 0, env.bearerData.length);
    935             dos.close();
    936 
    937             /**
    938              * TODO(cleanup) -- The mPdu field is managed in
    939              * a fragile manner, and it would be much nicer if
    940              * accessing the serialized representation used a less
    941              * fragile mechanism.  Maybe the getPdu method could
    942              * generate a representation if there was not yet one?
    943              */
    944 
    945             mPdu = baos.toByteArray();
    946         } catch (IOException ex) {
    947             Rlog.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex);
    948         }
    949     }
    950 
    951     /**
    952      * Converts a 4-Bit DTMF encoded symbol from the calling address number to ASCII character
    953      */
    954     private byte convertDtmfToAscii(byte dtmfDigit) {
    955         byte asciiDigit;
    956 
    957         switch (dtmfDigit) {
    958         case  0: asciiDigit = 68; break; // 'D'
    959         case  1: asciiDigit = 49; break; // '1'
    960         case  2: asciiDigit = 50; break; // '2'
    961         case  3: asciiDigit = 51; break; // '3'
    962         case  4: asciiDigit = 52; break; // '4'
    963         case  5: asciiDigit = 53; break; // '5'
    964         case  6: asciiDigit = 54; break; // '6'
    965         case  7: asciiDigit = 55; break; // '7'
    966         case  8: asciiDigit = 56; break; // '8'
    967         case  9: asciiDigit = 57; break; // '9'
    968         case 10: asciiDigit = 48; break; // '0'
    969         case 11: asciiDigit = 42; break; // '*'
    970         case 12: asciiDigit = 35; break; // '#'
    971         case 13: asciiDigit = 65; break; // 'A'
    972         case 14: asciiDigit = 66; break; // 'B'
    973         case 15: asciiDigit = 67; break; // 'C'
    974         default:
    975             asciiDigit = 32; // Invalid DTMF code
    976             break;
    977         }
    978 
    979         return asciiDigit;
    980     }
    981 
    982     /** This function  shall be called to get the number of voicemails.
    983      * @hide
    984      */
    985     /*package*/ int getNumOfVoicemails() {
    986         return mBearerData.numberOfMessages;
    987     }
    988 
    989     /**
    990      * Returns a byte array that can be use to uniquely identify a received SMS message.
    991      * C.S0015-B  4.3.1.6 Unique Message Identification.
    992      *
    993      * @return byte array uniquely identifying the message.
    994      * @hide
    995      */
    996     /* package */ byte[] getIncomingSmsFingerprint() {
    997         ByteArrayOutputStream output = new ByteArrayOutputStream();
    998 
    999         output.write(mEnvelope.teleService);
   1000         output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
   1001         output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
   1002         output.write(mEnvelope.origSubaddress.origBytes, 0,
   1003                 mEnvelope.origSubaddress.origBytes.length);
   1004 
   1005         return output.toByteArray();
   1006     }
   1007 
   1008     /**
   1009      * Returns the list of service category program data, if present.
   1010      * @return a list of CdmaSmsCbProgramData objects, or null if not present
   1011      */
   1012     ArrayList<CdmaSmsCbProgramData> getSmsCbProgramData() {
   1013         return mBearerData.serviceCategoryProgramData;
   1014     }
   1015 }
   1016