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