• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.telephony.cdma;
18 
19 import android.os.Parcel;
20 import android.os.SystemProperties;
21 import android.telephony.PhoneNumberUtils;
22 import android.telephony.SmsCbLocation;
23 import android.telephony.SmsCbMessage;
24 import android.telephony.TelephonyManager;
25 import android.telephony.cdma.CdmaSmsCbProgramData;
26 import android.telephony.Rlog;
27 import android.util.Log;
28 import android.text.TextUtils;
29 import android.content.res.Resources;
30 
31 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
32 import com.android.internal.telephony.SmsConstants;
33 import com.android.internal.telephony.SmsHeader;
34 import com.android.internal.telephony.SmsMessageBase;
35 import com.android.internal.telephony.TelephonyProperties;
36 import com.android.internal.telephony.cdma.sms.BearerData;
37 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
38 import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
39 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
40 import com.android.internal.telephony.cdma.sms.UserData;
41 import com.android.internal.telephony.uicc.IccUtils;
42 import com.android.internal.util.BitwiseInputStream;
43 import com.android.internal.util.HexDump;
44 import com.android.internal.telephony.Sms7BitEncodingTranslator;
45 
46 import java.io.BufferedOutputStream;
47 import java.io.ByteArrayInputStream;
48 import java.io.ByteArrayOutputStream;
49 import java.io.DataInputStream;
50 import java.io.DataOutputStream;
51 import java.io.IOException;
52 import 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  */
74 public 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      */
createFromPdu(byte[] pdu)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      */
newFromParcel(Parcel p)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      */
createFromEfRecord(int index, byte[] data)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      */
getTPLayerLengthForPDU(String pdu)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      */
getSubmitPdu(String scAddr, String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader)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      */
getSubmitPdu(String scAddr, String destAddr, int destPort, byte[] data, boolean statusReportRequested)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      */
getSubmitPdu(String destAddr, UserData userData, boolean statusReportRequested)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
getProtocolIdentifier()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
isReplace()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
isCphsMwiMessage()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
isMWIClearMessage()409     public boolean isMWIClearMessage() {
410         return ((mBearerData != null) && (mBearerData.numberOfMessages == 0));
411     }
412 
413     /**
414      * {@inheritDoc}
415      */
416     @Override
isMWISetMessage()417     public boolean isMWISetMessage() {
418         return ((mBearerData != null) && (mBearerData.numberOfMessages > 0));
419     }
420 
421     /**
422      * {@inheritDoc}
423      */
424     @Override
isMwiDontStore()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
getStatus()437     public int getStatus() {
438         return (status << 16);
439     }
440 
441     /** Return true iff the bearer data message type is DELIVERY_ACK. */
442     @Override
isStatusReportMessage()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
isReplyPathPresent()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      */
calculateLength(CharSequence messageBody, boolean use7bitOnly, boolean isEntireMsg)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     */
getTeleService()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     */
getMessageType()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      */
parsePdu(byte[] pdu)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      */
parsePduFromEfRecord(byte[] pdu)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      */
parseSms()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      */
parseBroadcastSms()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
getMessageClass()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      */
getNextMessageId()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      */
privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested, UserData userData)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      */
createPdu()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      */
convertDtmfToAscii(byte dtmfDigit)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      */
getNumOfVoicemails()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      */
getIncomingSmsFingerprint()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      */
getSmsCbProgramData()1055     public ArrayList<CdmaSmsCbProgramData> getSmsCbProgramData() {
1056         return mBearerData.serviceCategoryProgramData;
1057     }
1058 }
1059