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