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