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