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