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