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