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