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