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