• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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;
18 
19 import android.content.ContentValues;
20 import android.database.Cursor;
21 
22 import com.android.internal.util.HexDump;
23 
24 import java.util.Arrays;
25 import java.util.Date;
26 
27 /**
28  * Tracker for an incoming SMS message ready to broadcast to listeners.
29  * This is similar to {@link com.android.internal.telephony.SMSDispatcher.SmsTracker} used for
30  * outgoing messages.
31  */
32 public class InboundSmsTracker {
33 
34     // Fields for single and multi-part messages
35     private final byte[] mPdu;
36     private final long mTimestamp;
37     private final int mDestPort;
38     private final boolean mIs3gpp2;
39     private final boolean mIs3gpp2WapPdu;
40     private final String mMessageBody;
41     // Copied from SmsMessageBase#getDisplayOriginatingAddress used for blocking messages.
42     private final String mAddress;
43 
44     // Fields for concatenating multi-part SMS messages
45     private final int mReferenceNumber;
46     private final int mSequenceNumber;
47     private final int mMessageCount;
48 
49     // Fields for deleting this message after delivery
50     private String mDeleteWhere;
51     private String[] mDeleteWhereArgs;
52 
53     /** Destination port flag bit for no destination port. */
54     private static final int DEST_PORT_FLAG_NO_PORT = (1 << 16);
55 
56     /** Destination port flag bit to indicate 3GPP format message. */
57     private static final int DEST_PORT_FLAG_3GPP = (1 << 17);
58 
59     /** Destination port flag bit to indicate 3GPP2 format message. */
60     private static final int DEST_PORT_FLAG_3GPP2 = (1 << 18);
61 
62     /** Destination port flag bit to indicate 3GPP2 format WAP message. */
63     private static final int DEST_PORT_FLAG_3GPP2_WAP_PDU = (1 << 19);
64 
65     /** Destination port mask (16-bit unsigned value on GSM and CDMA). */
66     private static final int DEST_PORT_MASK = 0xffff;
67 
68     /**
69      * Create a tracker for a single-part SMS.
70      *
71      * @param pdu the message PDU
72      * @param timestamp the message timestamp
73      * @param destPort the destination port
74      * @param is3gpp2 true for 3GPP2 format; false for 3GPP format
75      * @param is3gpp2WapPdu true for 3GPP2 format WAP PDU; false otherwise
76      * @param address originating address, or email if this message was from an email gateway
77      */
InboundSmsTracker(byte[] pdu, long timestamp, int destPort, boolean is3gpp2, boolean is3gpp2WapPdu, String address, String messageBody)78     public InboundSmsTracker(byte[] pdu, long timestamp, int destPort, boolean is3gpp2,
79             boolean is3gpp2WapPdu, String address, String messageBody) {
80         mPdu = pdu;
81         mTimestamp = timestamp;
82         mDestPort = destPort;
83         mIs3gpp2 = is3gpp2;
84         mIs3gpp2WapPdu = is3gpp2WapPdu;
85         mMessageBody = messageBody;
86         mAddress = address;
87         // fields for multi-part SMS
88         mReferenceNumber = -1;
89         mSequenceNumber = getIndexOffset();     // 0 or 1, depending on type
90         mMessageCount = 1;
91     }
92 
93     /**
94      * Create a tracker for a multi-part SMS. Sequence numbers start at 1 for 3GPP and regular
95      * concatenated 3GPP2 messages, but CDMA WAP push sequence numbers start at 0. The caller will
96      * subtract 1 if necessary so that the sequence number is always 0-based. When loading and
97      * saving to the raw table, the sequence number is adjusted if necessary for backwards
98      * compatibility.
99      *
100      * @param pdu the message PDU
101      * @param timestamp the message timestamp
102      * @param destPort the destination port
103      * @param is3gpp2 true for 3GPP2 format; false for 3GPP format
104      * @param address originating address, or email if this message was from an email gateway
105      * @param referenceNumber the concatenated reference number
106      * @param sequenceNumber the sequence number of this segment (0-based)
107      * @param messageCount the total number of segments
108      * @param is3gpp2WapPdu true for 3GPP2 format WAP PDU; false otherwise
109      */
InboundSmsTracker(byte[] pdu, long timestamp, int destPort, boolean is3gpp2, String address, int referenceNumber, int sequenceNumber, int messageCount, boolean is3gpp2WapPdu, String messageBody)110     public InboundSmsTracker(byte[] pdu, long timestamp, int destPort, boolean is3gpp2,
111             String address, int referenceNumber, int sequenceNumber, int messageCount,
112             boolean is3gpp2WapPdu, String messageBody) {
113         mPdu = pdu;
114         mTimestamp = timestamp;
115         mDestPort = destPort;
116         mIs3gpp2 = is3gpp2;
117         mIs3gpp2WapPdu = is3gpp2WapPdu;
118         mMessageBody = messageBody;
119         mAddress = address;
120         // fields for multi-part SMS
121         mReferenceNumber = referenceNumber;
122         mSequenceNumber = sequenceNumber;
123         mMessageCount = messageCount;
124     }
125 
126     /**
127      * Create a new tracker from the row of the raw table pointed to by Cursor.
128      * Since this constructor is used only for recovery during startup, the Dispatcher is null.
129      * @param cursor a Cursor pointing to the row to construct this SmsTracker for
130      */
InboundSmsTracker(Cursor cursor, boolean isCurrentFormat3gpp2)131     public InboundSmsTracker(Cursor cursor, boolean isCurrentFormat3gpp2) {
132         mPdu = HexDump.hexStringToByteArray(cursor.getString(InboundSmsHandler.PDU_COLUMN));
133 
134         if (cursor.isNull(InboundSmsHandler.DESTINATION_PORT_COLUMN)) {
135             mDestPort = -1;
136             mIs3gpp2 = isCurrentFormat3gpp2;
137             mIs3gpp2WapPdu = false;
138         } else {
139             int destPort = cursor.getInt(InboundSmsHandler.DESTINATION_PORT_COLUMN);
140             if ((destPort & DEST_PORT_FLAG_3GPP) != 0) {
141                 mIs3gpp2 = false;
142             } else if ((destPort & DEST_PORT_FLAG_3GPP2) != 0) {
143                 mIs3gpp2 = true;
144             } else {
145                 mIs3gpp2 = isCurrentFormat3gpp2;
146             }
147             mIs3gpp2WapPdu = ((destPort & DEST_PORT_FLAG_3GPP2_WAP_PDU) != 0);
148             mDestPort = getRealDestPort(destPort);
149         }
150 
151         mTimestamp = cursor.getLong(InboundSmsHandler.DATE_COLUMN);
152         mAddress = cursor.getString(InboundSmsHandler.ADDRESS_COLUMN);
153 
154         if (cursor.isNull(InboundSmsHandler.COUNT_COLUMN)) {
155             // single-part message
156             long rowId = cursor.getLong(InboundSmsHandler.ID_COLUMN);
157             mReferenceNumber = -1;
158             mSequenceNumber = getIndexOffset();     // 0 or 1, depending on type
159             mMessageCount = 1;
160             mDeleteWhere = InboundSmsHandler.SELECT_BY_ID;
161             mDeleteWhereArgs = new String[]{Long.toString(rowId)};
162         } else {
163             // multi-part message
164             mReferenceNumber = cursor.getInt(InboundSmsHandler.REFERENCE_NUMBER_COLUMN);
165             mMessageCount = cursor.getInt(InboundSmsHandler.COUNT_COLUMN);
166 
167             // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
168             mSequenceNumber = cursor.getInt(InboundSmsHandler.SEQUENCE_COLUMN);
169             int index = mSequenceNumber - getIndexOffset();
170 
171             if (index < 0 || index >= mMessageCount) {
172                 throw new IllegalArgumentException("invalid PDU sequence " + mSequenceNumber
173                         + " of " + mMessageCount);
174             }
175 
176             mDeleteWhere = InboundSmsHandler.SELECT_BY_REFERENCE;
177             mDeleteWhereArgs = new String[]{mAddress,
178                     Integer.toString(mReferenceNumber), Integer.toString(mMessageCount)};
179         }
180         mMessageBody = cursor.getString(InboundSmsHandler.MESSAGE_BODY_COLUMN);
181     }
182 
getContentValues()183     public ContentValues getContentValues() {
184         ContentValues values = new ContentValues();
185         values.put("pdu", HexDump.toHexString(mPdu));
186         values.put("date", mTimestamp);
187         // Always set the destination port, since it now contains message format flags.
188         // Port is a 16-bit value, or -1, so clear the upper bits before setting flags.
189         int destPort;
190         if (mDestPort == -1) {
191             destPort = DEST_PORT_FLAG_NO_PORT;
192         } else {
193             destPort = mDestPort & DEST_PORT_MASK;
194         }
195         if (mIs3gpp2) {
196             destPort |= DEST_PORT_FLAG_3GPP2;
197         } else {
198             destPort |= DEST_PORT_FLAG_3GPP;
199         }
200         if (mIs3gpp2WapPdu) {
201             destPort |= DEST_PORT_FLAG_3GPP2_WAP_PDU;
202         }
203         values.put("destination_port", destPort);
204         if (mAddress != null) {
205             values.put("address", mAddress);
206             values.put("reference_number", mReferenceNumber);
207             values.put("sequence", mSequenceNumber);
208             values.put("count", mMessageCount);
209         }
210         values.put("message_body", mMessageBody);
211         return values;
212     }
213 
214     /**
215      * Get the port number, or -1 if there is no destination port.
216      * @param destPort the destination port value, with flags
217      * @return the real destination port, or -1 for no port
218      */
getRealDestPort(int destPort)219     public static int getRealDestPort(int destPort) {
220         if ((destPort & DEST_PORT_FLAG_NO_PORT) != 0) {
221             return -1;
222         } else {
223            return destPort & DEST_PORT_MASK;
224         }
225     }
226 
227     /**
228      * Update the values to delete all rows of the message from raw table.
229      * @param deleteWhere the selection to use
230      * @param deleteWhereArgs the selection args to use
231      */
setDeleteWhere(String deleteWhere, String[] deleteWhereArgs)232     public void setDeleteWhere(String deleteWhere, String[] deleteWhereArgs) {
233         mDeleteWhere = deleteWhere;
234         mDeleteWhereArgs = deleteWhereArgs;
235     }
236 
toString()237     public String toString() {
238         StringBuilder builder = new StringBuilder("SmsTracker{timestamp=");
239         builder.append(new Date(mTimestamp));
240         builder.append(" destPort=").append(mDestPort);
241         builder.append(" is3gpp2=").append(mIs3gpp2);
242         if (mAddress != null) {
243             builder.append(" address=").append(mAddress);
244             builder.append(" refNumber=").append(mReferenceNumber);
245             builder.append(" seqNumber=").append(mSequenceNumber);
246             builder.append(" msgCount=").append(mMessageCount);
247         }
248         if (mDeleteWhere != null) {
249             builder.append(" deleteWhere(").append(mDeleteWhere);
250             builder.append(") deleteArgs=(").append(Arrays.toString(mDeleteWhereArgs));
251             builder.append(')');
252         }
253         builder.append('}');
254         return builder.toString();
255     }
256 
getPdu()257     public byte[] getPdu() {
258         return mPdu;
259     }
260 
getTimestamp()261     public long getTimestamp() {
262         return mTimestamp;
263     }
264 
getDestPort()265     public int getDestPort() {
266         return mDestPort;
267     }
268 
is3gpp2()269     public boolean is3gpp2() {
270         return mIs3gpp2;
271     }
272 
getFormat()273     public String getFormat() {
274         return mIs3gpp2 ? SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
275     }
276 
277     /**
278      * Sequence numbers for concatenated messages start at 1. The exception is CDMA WAP PDU
279      * messages, which use a 0-based index.
280      * @return the offset to use to convert between mIndex and the sequence number
281      */
getIndexOffset()282     public int getIndexOffset() {
283         return (mIs3gpp2 && mIs3gpp2WapPdu) ? 0 : 1;
284     }
285 
getAddress()286     public String getAddress() {
287         return mAddress;
288     }
289 
getMessageBody()290     public String getMessageBody() {
291         return mMessageBody;
292     }
293 
getReferenceNumber()294     public int getReferenceNumber() {
295         return mReferenceNumber;
296     }
297 
getSequenceNumber()298     public int getSequenceNumber() {
299         return mSequenceNumber;
300     }
301 
getMessageCount()302     public int getMessageCount() {
303         return mMessageCount;
304     }
305 
getDeleteWhere()306     public String getDeleteWhere() {
307         return mDeleteWhere;
308     }
309 
getDeleteWhereArgs()310     public String[] getDeleteWhereArgs() {
311         return mDeleteWhereArgs;
312     }
313 }
314