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.app.Activity;
     20 import android.app.AppOpsManager;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.ContentResolver;
     24 import android.content.ContentUris;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.database.Cursor;
     29 import android.database.SQLException;
     30 import android.net.Uri;
     31 import android.os.AsyncResult;
     32 import android.os.Build;
     33 import android.os.Message;
     34 import android.os.PowerManager;
     35 import android.os.SystemProperties;
     36 import android.provider.Telephony;
     37 import android.provider.Telephony.Sms.Intents;
     38 import android.telephony.Rlog;
     39 import android.telephony.SmsMessage;
     40 import android.telephony.TelephonyManager;
     41 
     42 import com.android.internal.util.HexDump;
     43 import com.android.internal.util.State;
     44 import com.android.internal.util.StateMachine;
     45 
     46 import java.io.ByteArrayOutputStream;
     47 import java.util.Arrays;
     48 
     49 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
     50 
     51 /**
     52  * This class broadcasts incoming SMS messages to interested apps after storing them in
     53  * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
     54  * broadcast, its parts are removed from the raw table. If the device crashes after ACKing
     55  * but before the broadcast completes, the pending messages will be rebroadcast on the next boot.
     56  *
     57  * <p>The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a
     58  * new SMS from the radio, it calls {@link #dispatchNormalMessage},
     59  * which sends a message to the state machine, causing the wakelock to be acquired in
     60  * {@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message
     61  * is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us.
     62  *
     63  * <p>After saving the SMS, if the message is complete (either single-part or the final segment
     64  * of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
     65  * {@link WaitingState} state to wait for the broadcast to complete. When the local
     66  * {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE}
     67  * to the state machine, causing us to either broadcast the next pending message (if one has
     68  * arrived while waiting for the broadcast to complete), or to transition back to the halted state
     69  * after all messages are processed. Then the wakelock is released and we wait for the next SMS.
     70  */
     71 public abstract class InboundSmsHandler extends StateMachine {
     72     protected static final boolean DBG = true;
     73     private static final boolean VDBG = false;  // STOPSHIP if true, logs user data
     74 
     75     /** Query projection for checking for duplicate message segments. */
     76     private static final String[] PDU_PROJECTION = {
     77             "pdu"
     78     };
     79 
     80     /** Query projection for combining concatenated message segments. */
     81     private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
     82             "pdu",
     83             "sequence",
     84             "destination_port"
     85     };
     86 
     87     static final int PDU_COLUMN = 0;
     88     static final int SEQUENCE_COLUMN = 1;
     89     static final int DESTINATION_PORT_COLUMN = 2;
     90     static final int DATE_COLUMN = 3;
     91     static final int REFERENCE_NUMBER_COLUMN = 4;
     92     static final int COUNT_COLUMN = 5;
     93     static final int ADDRESS_COLUMN = 6;
     94     static final int ID_COLUMN = 7;
     95 
     96     static final String SELECT_BY_ID = "_id=?";
     97     static final String SELECT_BY_REFERENCE = "address=? AND reference_number=? AND count=?";
     98 
     99     /** New SMS received as an AsyncResult. */
    100     public static final int EVENT_NEW_SMS = 1;
    101 
    102     /** Message type containing a {@link InboundSmsTracker} ready to broadcast to listeners. */
    103     static final int EVENT_BROADCAST_SMS = 2;
    104 
    105     /** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */
    106     static final int EVENT_BROADCAST_COMPLETE = 3;
    107 
    108     /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
    109     static final int EVENT_RETURN_TO_IDLE = 4;
    110 
    111     /** Release wakelock after a short timeout when returning to idle state. */
    112     static final int EVENT_RELEASE_WAKELOCK = 5;
    113 
    114     /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */
    115     static final int EVENT_START_ACCEPTING_SMS = 6;
    116 
    117     /** Update phone object */
    118     static final int EVENT_UPDATE_PHONE_OBJECT = 7;
    119 
    120     /** Wakelock release delay when returning to idle state. */
    121     private static final int WAKELOCK_TIMEOUT = 3000;
    122 
    123     /** URI for raw table of SMS provider. */
    124     private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
    125 
    126     protected final Context mContext;
    127     private final ContentResolver mResolver;
    128 
    129     /** Special handler for WAP push messages. */
    130     private final WapPushOverSms mWapPush;
    131 
    132     /** Wake lock to ensure device stays awake while dispatching the SMS intents. */
    133     final PowerManager.WakeLock mWakeLock;
    134 
    135     /** DefaultState throws an exception or logs an error for unhandled message types. */
    136     final DefaultState mDefaultState = new DefaultState();
    137 
    138     /** Startup state. Waiting for {@link SmsBroadcastUndelivered} to complete. */
    139     final StartupState mStartupState = new StartupState();
    140 
    141     /** Idle state. Waiting for messages to process. */
    142     final IdleState mIdleState = new IdleState();
    143 
    144     /** Delivering state. Saves the PDU in the raw table and acknowledges to SMSC. */
    145     final DeliveringState mDeliveringState = new DeliveringState();
    146 
    147     /** Broadcasting state. Waits for current broadcast to complete before delivering next. */
    148     final WaitingState mWaitingState = new WaitingState();
    149 
    150     /** Helper class to check whether storage is available for incoming messages. */
    151     protected SmsStorageMonitor mStorageMonitor;
    152 
    153     private final boolean mSmsReceiveDisabled;
    154 
    155     protected PhoneBase mPhone;
    156 
    157     protected CellBroadcastHandler mCellBroadcastHandler;
    158 
    159 
    160     /**
    161      * Create a new SMS broadcast helper.
    162      * @param name the class name for logging
    163      * @param context the context of the phone app
    164      * @param storageMonitor the SmsStorageMonitor to check for storage availability
    165      */
    166     protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor,
    167             PhoneBase phone, CellBroadcastHandler cellBroadcastHandler) {
    168         super(name);
    169 
    170         mContext = context;
    171         mStorageMonitor = storageMonitor;
    172         mPhone = phone;
    173         mCellBroadcastHandler = cellBroadcastHandler;
    174         mResolver = context.getContentResolver();
    175         mWapPush = new WapPushOverSms(context);
    176 
    177         boolean smsCapable = mContext.getResources().getBoolean(
    178                 com.android.internal.R.bool.config_sms_capable);
    179         mSmsReceiveDisabled = !SystemProperties.getBoolean(
    180                 TelephonyProperties.PROPERTY_SMS_RECEIVE, smsCapable);
    181 
    182         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    183         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
    184         mWakeLock.acquire();    // wake lock released after we enter idle state
    185 
    186         addState(mDefaultState);
    187         addState(mStartupState, mDefaultState);
    188         addState(mIdleState, mDefaultState);
    189         addState(mDeliveringState, mDefaultState);
    190             addState(mWaitingState, mDeliveringState);
    191 
    192         setInitialState(mStartupState);
    193         if (DBG) log("created InboundSmsHandler");
    194     }
    195 
    196     /**
    197      * Tell the state machine to quit after processing all messages.
    198      */
    199     public void dispose() {
    200         quit();
    201     }
    202 
    203     /**
    204      * Update the phone object when it changes.
    205      */
    206     public void updatePhoneObject(PhoneBase phone) {
    207         sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
    208     }
    209 
    210     /**
    211      * Dispose of the WAP push object and release the wakelock.
    212      */
    213     @Override
    214     protected void onQuitting() {
    215         mWapPush.dispose();
    216 
    217         while (mWakeLock.isHeld()) {
    218             mWakeLock.release();
    219         }
    220     }
    221 
    222     /**
    223      * This parent state throws an exception (for debug builds) or prints an error for unhandled
    224      * message types.
    225      */
    226     class DefaultState extends State {
    227         @Override
    228         public boolean processMessage(Message msg) {
    229             switch (msg.what) {
    230                 case EVENT_UPDATE_PHONE_OBJECT: {
    231                     onUpdatePhoneObject((PhoneBase) msg.obj);
    232                     break;
    233                 }
    234                 default: {
    235                     String errorText = "processMessage: unhandled message type " + msg.what;
    236                     if (Build.IS_DEBUGGABLE) {
    237                         throw new RuntimeException(errorText);
    238                     } else {
    239                         loge(errorText);
    240                     }
    241                     break;
    242                 }
    243             }
    244             return HANDLED;
    245         }
    246     }
    247 
    248     /**
    249      * The Startup state waits for {@link SmsBroadcastUndelivered} to process the raw table and
    250      * notify the state machine to broadcast any complete PDUs that might not have been broadcast.
    251      */
    252     class StartupState extends State {
    253         @Override
    254         public boolean processMessage(Message msg) {
    255             switch (msg.what) {
    256                 case EVENT_NEW_SMS:
    257                 case EVENT_BROADCAST_SMS:
    258                     deferMessage(msg);
    259                     return HANDLED;
    260 
    261                 case EVENT_START_ACCEPTING_SMS:
    262                     transitionTo(mIdleState);
    263                     return HANDLED;
    264 
    265                 case EVENT_BROADCAST_COMPLETE:
    266                 case EVENT_RETURN_TO_IDLE:
    267                 case EVENT_RELEASE_WAKELOCK:
    268                 default:
    269                     // let DefaultState handle these unexpected message types
    270                     return NOT_HANDLED;
    271             }
    272         }
    273     }
    274 
    275     /**
    276      * In the idle state the wakelock is released until a new SM arrives, then we transition
    277      * to Delivering mode to handle it, acquiring the wakelock on exit.
    278      */
    279     class IdleState extends State {
    280         @Override
    281         public void enter() {
    282             if (DBG) log("entering Idle state");
    283             sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT);
    284         }
    285 
    286         @Override
    287         public void exit() {
    288             mWakeLock.acquire();
    289             if (DBG) log("acquired wakelock, leaving Idle state");
    290         }
    291 
    292         @Override
    293         public boolean processMessage(Message msg) {
    294             if (DBG) log("Idle state processing message type " + msg.what);
    295             switch (msg.what) {
    296                 case EVENT_NEW_SMS:
    297                 case EVENT_BROADCAST_SMS:
    298                     deferMessage(msg);
    299                     transitionTo(mDeliveringState);
    300                     return HANDLED;
    301 
    302                 case EVENT_RELEASE_WAKELOCK:
    303                     mWakeLock.release();
    304                     if (DBG) {
    305                         if (mWakeLock.isHeld()) {
    306                             // this is okay as long as we call release() for every acquire()
    307                             log("mWakeLock is still held after release");
    308                         } else {
    309                             log("mWakeLock released");
    310                         }
    311                     }
    312                     return HANDLED;
    313 
    314                 case EVENT_RETURN_TO_IDLE:
    315                     // already in idle state; ignore
    316                     return HANDLED;
    317 
    318                 case EVENT_BROADCAST_COMPLETE:
    319                 case EVENT_START_ACCEPTING_SMS:
    320                 default:
    321                     // let DefaultState handle these unexpected message types
    322                     return NOT_HANDLED;
    323             }
    324         }
    325     }
    326 
    327     /**
    328      * In the delivering state, the inbound SMS is processed and stored in the raw table.
    329      * The message is acknowledged before we exit this state. If there is a message to broadcast,
    330      * transition to {@link WaitingState} state to send the ordered broadcast and wait for the
    331      * results. When all messages have been processed, the halting state will release the wakelock.
    332      */
    333     class DeliveringState extends State {
    334         @Override
    335         public void enter() {
    336             if (DBG) log("entering Delivering state");
    337         }
    338 
    339         @Override
    340         public void exit() {
    341             if (DBG) log("leaving Delivering state");
    342         }
    343 
    344         @Override
    345         public boolean processMessage(Message msg) {
    346             switch (msg.what) {
    347                 case EVENT_NEW_SMS:
    348                     // handle new SMS from RIL
    349                     handleNewSms((AsyncResult) msg.obj);
    350                     sendMessage(EVENT_RETURN_TO_IDLE);
    351                     return HANDLED;
    352 
    353                 case EVENT_BROADCAST_SMS:
    354                     // if any broadcasts were sent, transition to waiting state
    355                     if (processMessagePart((InboundSmsTracker) msg.obj)) {
    356                         transitionTo(mWaitingState);
    357                     }
    358                     return HANDLED;
    359 
    360                 case EVENT_RETURN_TO_IDLE:
    361                     // return to idle after processing all other messages
    362                     transitionTo(mIdleState);
    363                     return HANDLED;
    364 
    365                 case EVENT_RELEASE_WAKELOCK:
    366                     mWakeLock.release();    // decrement wakelock from previous entry to Idle
    367                     if (!mWakeLock.isHeld()) {
    368                         // wakelock should still be held until 3 seconds after we enter Idle
    369                         loge("mWakeLock released while delivering/broadcasting!");
    370                     }
    371                     return HANDLED;
    372 
    373                 // we shouldn't get this message type in this state, log error and halt.
    374                 case EVENT_BROADCAST_COMPLETE:
    375                 case EVENT_START_ACCEPTING_SMS:
    376                 default:
    377                     // let DefaultState handle these unexpected message types
    378                     return NOT_HANDLED;
    379             }
    380         }
    381     }
    382 
    383     /**
    384      * The waiting state delegates handling of new SMS to parent {@link DeliveringState}, but
    385      * defers handling of the {@link #EVENT_BROADCAST_SMS} phase until after the current
    386      * result receiver sends {@link #EVENT_BROADCAST_COMPLETE}. Before transitioning to
    387      * {@link DeliveringState}, {@link #EVENT_RETURN_TO_IDLE} is sent to transition to
    388      * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled.
    389      */
    390     class WaitingState extends State {
    391         @Override
    392         public boolean processMessage(Message msg) {
    393             switch (msg.what) {
    394                 case EVENT_BROADCAST_SMS:
    395                     // defer until the current broadcast completes
    396                     deferMessage(msg);
    397                     return HANDLED;
    398 
    399                 case EVENT_BROADCAST_COMPLETE:
    400                     // return to idle after handling all deferred messages
    401                     sendMessage(EVENT_RETURN_TO_IDLE);
    402                     transitionTo(mDeliveringState);
    403                     return HANDLED;
    404 
    405                 case EVENT_RETURN_TO_IDLE:
    406                     // not ready to return to idle; ignore
    407                     return HANDLED;
    408 
    409                 default:
    410                     // parent state handles the other message types
    411                     return NOT_HANDLED;
    412             }
    413         }
    414     }
    415 
    416     void handleNewSms(AsyncResult ar) {
    417         if (ar.exception != null) {
    418             loge("Exception processing incoming SMS: " + ar.exception);
    419             return;
    420         }
    421 
    422         int result;
    423         try {
    424             SmsMessage sms = (SmsMessage) ar.result;
    425             result = dispatchMessage(sms.mWrappedSmsMessage);
    426         } catch (RuntimeException ex) {
    427             loge("Exception dispatching message", ex);
    428             result = Intents.RESULT_SMS_GENERIC_ERROR;
    429         }
    430 
    431         // RESULT_OK means that the SMS will be acknowledged by special handling,
    432         // e.g. for SMS-PP data download. Any other result, we should ack here.
    433         if (result != Activity.RESULT_OK) {
    434             boolean handled = (result == Intents.RESULT_SMS_HANDLED);
    435             notifyAndAcknowledgeLastIncomingSms(handled, result, null);
    436         }
    437     }
    438 
    439     /**
    440      * Process an SMS message from the RIL, calling subclass methods to handle 3GPP and
    441      * 3GPP2-specific message types.
    442      *
    443      * @param smsb the SmsMessageBase object from the RIL
    444      * @return a result code from {@link android.provider.Telephony.Sms.Intents},
    445      *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
    446      */
    447     public int dispatchMessage(SmsMessageBase smsb) {
    448         // If sms is null, there was a parsing error.
    449         if (smsb == null) {
    450             loge("dispatchSmsMessage: message is null");
    451             return Intents.RESULT_SMS_GENERIC_ERROR;
    452         }
    453 
    454         if (mSmsReceiveDisabled) {
    455             // Device doesn't support receiving SMS,
    456             log("Received short message on device which doesn't support "
    457                     + "receiving SMS. Ignored.");
    458             return Intents.RESULT_SMS_HANDLED;
    459         }
    460 
    461         return dispatchMessageRadioSpecific(smsb);
    462     }
    463 
    464     /**
    465      * Process voicemail notification, SMS-PP data download, CDMA CMAS, CDMA WAP push, and other
    466      * 3GPP/3GPP2-specific messages. Regular SMS messages are handled by calling the shared
    467      * {@link #dispatchNormalMessage} from this class.
    468      *
    469      * @param smsb the SmsMessageBase object from the RIL
    470      * @return a result code from {@link android.provider.Telephony.Sms.Intents},
    471      *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
    472      */
    473     protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb);
    474 
    475     /**
    476      * Send an acknowledge message to the SMSC.
    477      * @param success indicates that last message was successfully received.
    478      * @param result result code indicating any error
    479      * @param response callback message sent when operation completes.
    480      */
    481     protected abstract void acknowledgeLastIncomingSms(boolean success,
    482             int result, Message response);
    483 
    484     /**
    485      * Called when the phone changes the default method updates mPhone
    486      * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
    487      * Override if different or other behavior is desired.
    488      *
    489      * @param phone
    490      */
    491     protected void onUpdatePhoneObject(PhoneBase phone) {
    492         mPhone = phone;
    493         mStorageMonitor = mPhone.mSmsStorageMonitor;
    494         log("onUpdatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
    495     }
    496 
    497     /**
    498      * Notify interested apps if the framework has rejected an incoming SMS,
    499      * and send an acknowledge message to the network.
    500      * @param success indicates that last message was successfully received.
    501      * @param result result code indicating any error
    502      * @param response callback message sent when operation completes.
    503      */
    504     void notifyAndAcknowledgeLastIncomingSms(boolean success,
    505             int result, Message response) {
    506         if (!success) {
    507             // broadcast SMS_REJECTED_ACTION intent
    508             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
    509             intent.putExtra("result", result);
    510             mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
    511         }
    512         acknowledgeLastIncomingSms(success, result, response);
    513     }
    514 
    515     /**
    516      * Return true if this handler is for 3GPP2 messages; false for 3GPP format.
    517      * @return true for the 3GPP2 handler; false for the 3GPP handler
    518      */
    519     protected abstract boolean is3gpp2();
    520 
    521     /**
    522      * Dispatch a normal incoming SMS. This is called from {@link #dispatchMessageRadioSpecific}
    523      * if no format-specific handling was required. Saves the PDU to the SMS provider raw table,
    524      * creates an {@link InboundSmsTracker}, then sends it to the state machine as an
    525      * {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value.
    526      *
    527      * @param sms the message to dispatch
    528      * @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status
    529      */
    530     protected int dispatchNormalMessage(SmsMessageBase sms) {
    531         SmsHeader smsHeader = sms.getUserDataHeader();
    532         InboundSmsTracker tracker;
    533 
    534         if ((smsHeader == null) || (smsHeader.concatRef == null)) {
    535             // Message is not concatenated.
    536             int destPort = -1;
    537             if (smsHeader != null && smsHeader.portAddrs != null) {
    538                 // The message was sent to a port.
    539                 destPort = smsHeader.portAddrs.destPort;
    540                 if (DBG) log("destination port: " + destPort);
    541             }
    542 
    543             tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
    544                     is3gpp2(), false);
    545         } else {
    546             // Create a tracker for this message segment.
    547             SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
    548             SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
    549             int destPort = (portAddrs != null ? portAddrs.destPort : -1);
    550 
    551             tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
    552                     is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber,
    553                     concatRef.seqNumber, concatRef.msgCount, false);
    554         }
    555 
    556         if (VDBG) log("created tracker: " + tracker);
    557         return addTrackerToRawTableAndSendMessage(tracker);
    558     }
    559 
    560     /**
    561      * Helper to add the tracker to the raw table and then send a message to broadcast it, if
    562      * successful. Returns the SMS intent status to return to the SMSC.
    563      * @param tracker the tracker to save to the raw table and then deliver
    564      * @return {@link Intents#RESULT_SMS_HANDLED} or {@link Intents#RESULT_SMS_GENERIC_ERROR}
    565      * or {@link Intents#RESULT_SMS_DUPLICATED}
    566      */
    567     protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) {
    568         switch(addTrackerToRawTable(tracker)) {
    569         case Intents.RESULT_SMS_HANDLED:
    570             sendMessage(EVENT_BROADCAST_SMS, tracker);
    571             return Intents.RESULT_SMS_HANDLED;
    572 
    573         case Intents.RESULT_SMS_DUPLICATED:
    574             return Intents.RESULT_SMS_HANDLED;
    575 
    576         case Intents.RESULT_SMS_GENERIC_ERROR:
    577         default:
    578             return Intents.RESULT_SMS_GENERIC_ERROR;
    579         }
    580     }
    581 
    582     /**
    583      * Process the inbound SMS segment. If the message is complete, send it as an ordered
    584      * broadcast to interested receivers and return true. If the message is a segment of an
    585      * incomplete multi-part SMS, return false.
    586      * @param tracker the tracker containing the message segment to process
    587      * @return true if an ordered broadcast was sent; false if waiting for more message segments
    588      */
    589     boolean processMessagePart(InboundSmsTracker tracker) {
    590         int messageCount = tracker.getMessageCount();
    591         byte[][] pdus;
    592         int destPort = tracker.getDestPort();
    593 
    594         if (messageCount == 1) {
    595             // single-part message
    596             pdus = new byte[][]{tracker.getPdu()};
    597         } else {
    598             // multi-part message
    599             Cursor cursor = null;
    600             try {
    601                 // used by several query selection arguments
    602                 String address = tracker.getAddress();
    603                 String refNumber = Integer.toString(tracker.getReferenceNumber());
    604                 String count = Integer.toString(tracker.getMessageCount());
    605 
    606                 // query for all segments and broadcast message if we have all the parts
    607                 String[] whereArgs = {address, refNumber, count};
    608                 cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION,
    609                         SELECT_BY_REFERENCE, whereArgs, null);
    610 
    611                 int cursorCount = cursor.getCount();
    612                 if (cursorCount < messageCount) {
    613                     // Wait for the other message parts to arrive. It's also possible for the last
    614                     // segment to arrive before processing the EVENT_BROADCAST_SMS for one of the
    615                     // earlier segments. In that case, the broadcast will be sent as soon as all
    616                     // segments are in the table, and any later EVENT_BROADCAST_SMS messages will
    617                     // get a row count of 0 and return.
    618                     return false;
    619                 }
    620 
    621                 // All the parts are in place, deal with them
    622                 pdus = new byte[messageCount][];
    623                 while (cursor.moveToNext()) {
    624                     // subtract offset to convert sequence to 0-based array index
    625                     int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset();
    626 
    627                     pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN));
    628 
    629                     // Read the destination port from the first segment (needed for CDMA WAP PDU).
    630                     // It's not a bad idea to prefer the port from the first segment in other cases.
    631                     if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
    632                         int port = cursor.getInt(DESTINATION_PORT_COLUMN);
    633                         // strip format flags and convert to real port number, or -1
    634                         port = InboundSmsTracker.getRealDestPort(port);
    635                         if (port != -1) {
    636                             destPort = port;
    637                         }
    638                     }
    639                 }
    640             } catch (SQLException e) {
    641                 loge("Can't access multipart SMS database", e);
    642                 return false;
    643             } finally {
    644                 if (cursor != null) {
    645                     cursor.close();
    646                 }
    647             }
    648         }
    649 
    650         BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);
    651 
    652         if (destPort == SmsHeader.PORT_WAP_PUSH) {
    653             // Build up the data stream
    654             ByteArrayOutputStream output = new ByteArrayOutputStream();
    655             for (byte[] pdu : pdus) {
    656                 // 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this
    657                 if (!tracker.is3gpp2()) {
    658                     SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
    659                     pdu = msg.getUserData();
    660                 }
    661                 output.write(pdu, 0, pdu.length);
    662             }
    663             int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
    664             if (DBG) log("dispatchWapPdu() returned " + result);
    665             // result is Activity.RESULT_OK if an ordered broadcast was sent
    666             return (result == Activity.RESULT_OK);
    667         }
    668 
    669         Intent intent;
    670         if (destPort == -1) {
    671             intent = new Intent(Intents.SMS_DELIVER_ACTION);
    672 
    673             // Direct the intent to only the default SMS app. If we can't find a default SMS app
    674             // then sent it to all broadcast receivers.
    675             ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
    676             if (componentName != null) {
    677                 // Deliver SMS message only to this receiver
    678                 intent.setComponent(componentName);
    679                 log("Delivering SMS to: " + componentName.getPackageName() +
    680                         " " + componentName.getClassName());
    681             }
    682         } else {
    683             Uri uri = Uri.parse("sms://localhost:" + destPort);
    684             intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
    685         }
    686 
    687         intent.putExtra("pdus", pdus);
    688         intent.putExtra("format", tracker.getFormat());
    689         dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
    690                 AppOpsManager.OP_RECEIVE_SMS, resultReceiver);
    691         return true;
    692     }
    693 
    694     /**
    695      * Dispatch the intent with the specified permission, appOp, and result receiver, using
    696      * this state machine's handler thread to run the result receiver.
    697      *
    698      * @param intent the intent to broadcast
    699      * @param permission receivers are required to have this permission
    700      * @param appOp app op that is being performed when dispatching to a receiver
    701      */
    702     void dispatchIntent(Intent intent, String permission, int appOp,
    703             BroadcastReceiver resultReceiver) {
    704         intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
    705         mContext.sendOrderedBroadcast(intent, permission, appOp, resultReceiver,
    706                 getHandler(), Activity.RESULT_OK, null, null);
    707     }
    708 
    709     /**
    710      * Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table.
    711      */
    712     void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs) {
    713         int rows = mResolver.delete(sRawUri, deleteWhere, deleteWhereArgs);
    714         if (rows == 0) {
    715             loge("No rows were deleted from raw table!");
    716         } else if (DBG) {
    717             log("Deleted " + rows + " rows from raw table.");
    718         }
    719     }
    720 
    721     /**
    722      * Insert a message PDU into the raw table so we can acknowledge it immediately.
    723      * If the device crashes before the broadcast to listeners completes, it will be delivered
    724      * from the raw table on the next device boot. For single-part messages, the deleteWhere
    725      * and deleteWhereArgs fields of the tracker will be set to delete the correct row after
    726      * the ordered broadcast completes.
    727      *
    728      * @param tracker the tracker to add to the raw table
    729      * @return true on success; false on failure to write to database
    730      */
    731     private int addTrackerToRawTable(InboundSmsTracker tracker) {
    732         if (tracker.getMessageCount() != 1) {
    733             // check for duplicate message segments
    734             Cursor cursor = null;
    735             try {
    736                 // sequence numbers are 1-based except for CDMA WAP, which is 0-based
    737                 int sequence = tracker.getSequenceNumber();
    738 
    739                 // convert to strings for query
    740                 String address = tracker.getAddress();
    741                 String refNumber = Integer.toString(tracker.getReferenceNumber());
    742                 String count = Integer.toString(tracker.getMessageCount());
    743 
    744                 String seqNumber = Integer.toString(sequence);
    745 
    746                 // set the delete selection args for multi-part message
    747                 String[] deleteWhereArgs = {address, refNumber, count};
    748                 tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs);
    749 
    750                 // Check for duplicate message segments
    751                 cursor = mResolver.query(sRawUri, PDU_PROJECTION,
    752                         "address=? AND reference_number=? AND count=? AND sequence=?",
    753                         new String[] {address, refNumber, count, seqNumber}, null);
    754 
    755                 // moveToNext() returns false if no duplicates were found
    756                 if (cursor.moveToNext()) {
    757                     loge("Discarding duplicate message segment, refNumber=" + refNumber
    758                             + " seqNumber=" + seqNumber);
    759                     String oldPduString = cursor.getString(PDU_COLUMN);
    760                     byte[] pdu = tracker.getPdu();
    761                     byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
    762                     if (!Arrays.equals(oldPdu, tracker.getPdu())) {
    763                         loge("Warning: dup message segment PDU of length " + pdu.length
    764                                 + " is different from existing PDU of length " + oldPdu.length);
    765                     }
    766                     return Intents.RESULT_SMS_DUPLICATED;   // reject message
    767                 }
    768                 cursor.close();
    769             } catch (SQLException e) {
    770                 loge("Can't access multipart SMS database", e);
    771                 return Intents.RESULT_SMS_GENERIC_ERROR;    // reject message
    772             } finally {
    773                 if (cursor != null) {
    774                     cursor.close();
    775                 }
    776             }
    777         }
    778 
    779         ContentValues values = tracker.getContentValues();
    780 
    781         if (VDBG) log("adding content values to raw table: " + values.toString());
    782         Uri newUri = mResolver.insert(sRawUri, values);
    783         if (DBG) log("URI of new row -> " + newUri);
    784 
    785         try {
    786             long rowId = ContentUris.parseId(newUri);
    787             if (tracker.getMessageCount() == 1) {
    788                 // set the delete selection args for single-part message
    789                 tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)});
    790             }
    791             return Intents.RESULT_SMS_HANDLED;
    792         } catch (Exception e) {
    793             loge("error parsing URI for new row: " + newUri, e);
    794             return Intents.RESULT_SMS_GENERIC_ERROR;
    795         }
    796     }
    797 
    798     /**
    799      * Returns whether the default message format for the current radio technology is 3GPP2.
    800      * @return true if the radio technology uses 3GPP2 format by default, false for 3GPP format
    801      */
    802     static boolean isCurrentFormat3gpp2() {
    803         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    804         return (PHONE_TYPE_CDMA == activePhone);
    805     }
    806 
    807     /**
    808      * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
    809      * logs the broadcast duration (as an error if the other receivers were especially slow).
    810      */
    811     private final class SmsBroadcastReceiver extends BroadcastReceiver {
    812         private final String mDeleteWhere;
    813         private final String[] mDeleteWhereArgs;
    814         private long mBroadcastTimeNano;
    815 
    816         SmsBroadcastReceiver(InboundSmsTracker tracker) {
    817             mDeleteWhere = tracker.getDeleteWhere();
    818             mDeleteWhereArgs = tracker.getDeleteWhereArgs();
    819             mBroadcastTimeNano = System.nanoTime();
    820         }
    821 
    822         @Override
    823         public void onReceive(Context context, Intent intent) {
    824             String action = intent.getAction();
    825             if (action.equals(Intents.SMS_DELIVER_ACTION)) {
    826                 // Now dispatch the notification only intent
    827                 intent.setAction(Intents.SMS_RECEIVED_ACTION);
    828                 intent.setComponent(null);
    829                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
    830                         AppOpsManager.OP_RECEIVE_SMS, this);
    831             } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
    832                 // Now dispatch the notification only intent
    833                 intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
    834                 intent.setComponent(null);
    835                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
    836                         AppOpsManager.OP_RECEIVE_SMS, this);
    837             } else {
    838                 // Now that the intents have been deleted we can clean up the PDU data.
    839                 if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
    840                         && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
    841                         && !Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
    842                     loge("unexpected BroadcastReceiver action: " + action);
    843                 }
    844 
    845                 int rc = getResultCode();
    846                 if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) {
    847                     loge("a broadcast receiver set the result code to " + rc
    848                             + ", deleting from raw table anyway!");
    849                 } else if (DBG) {
    850                     log("successful broadcast, deleting from raw table.");
    851                 }
    852 
    853                 deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs);
    854                 sendMessage(EVENT_BROADCAST_COMPLETE);
    855 
    856                 int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000);
    857                 if (durationMillis >= 5000) {
    858                     loge("Slow ordered broadcast completion time: " + durationMillis + " ms");
    859                 } else if (DBG) {
    860                     log("ordered broadcast completed in: " + durationMillis + " ms");
    861                 }
    862             }
    863         }
    864     }
    865 
    866     /**
    867      * Log with debug level.
    868      * @param s the string to log
    869      */
    870     @Override
    871     protected void log(String s) {
    872         Rlog.d(getName(), s);
    873     }
    874 
    875     /**
    876      * Log with error level.
    877      * @param s the string to log
    878      */
    879     @Override
    880     protected void loge(String s) {
    881         Rlog.e(getName(), s);
    882     }
    883 
    884     /**
    885      * Log with error level.
    886      * @param s the string to log
    887      * @param e is a Throwable which logs additional information.
    888      */
    889     @Override
    890     protected void loge(String s, Throwable e) {
    891         Rlog.e(getName(), s, e);
    892     }
    893 }
    894