Home | History | Annotate | Download | only in telephony
      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      */
     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      */
    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      */
    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 
    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      */
    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      */
    225     void setDeleteWhere(String deleteWhere, String[] deleteWhereArgs) {
    226         mDeleteWhere = deleteWhere;
    227         mDeleteWhereArgs = deleteWhereArgs;
    228     }
    229 
    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 
    250     byte[] getPdu() {
    251         return mPdu;
    252     }
    253 
    254     long getTimestamp() {
    255         return mTimestamp;
    256     }
    257 
    258     int getDestPort() {
    259         return mDestPort;
    260     }
    261 
    262     boolean is3gpp2() {
    263         return mIs3gpp2;
    264     }
    265 
    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      */
    275     int getIndexOffset() {
    276         return (mIs3gpp2 && mIs3gpp2WapPdu) ? 0 : 1;
    277     }
    278 
    279     String getAddress() {
    280         return mAddress;
    281     }
    282 
    283     int getReferenceNumber() {
    284         return mReferenceNumber;
    285     }
    286 
    287     int getSequenceNumber() {
    288         return mSequenceNumber;
    289     }
    290 
    291     int getMessageCount() {
    292         return mMessageCount;
    293     }
    294 
    295     String getDeleteWhere() {
    296         return mDeleteWhere;
    297     }
    298 
    299     String[] getDeleteWhereArgs() {
    300         return mDeleteWhereArgs;
    301     }
    302 }
    303