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