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