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