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