Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2006 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.PendingIntent;
     21 import android.app.AlertDialog;
     22 import android.app.PendingIntent.CanceledException;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ContentResolver;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.DialogInterface;
     29 import android.content.IntentFilter;
     30 import android.content.res.Resources;
     31 import android.database.Cursor;
     32 import android.database.SQLException;
     33 import android.net.Uri;
     34 import android.os.AsyncResult;
     35 import android.os.Handler;
     36 import android.os.Message;
     37 import android.os.PowerManager;
     38 import android.os.SystemProperties;
     39 import android.provider.Telephony;
     40 import android.provider.Telephony.Sms.Intents;
     41 import android.provider.Settings;
     42 import android.telephony.SmsMessage;
     43 import android.telephony.ServiceState;
     44 import android.util.Log;
     45 import android.view.WindowManager;
     46 
     47 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
     48 import com.android.internal.util.HexDump;
     49 
     50 import java.io.ByteArrayOutputStream;
     51 import java.util.ArrayList;
     52 import java.util.Arrays;
     53 import java.util.HashMap;
     54 import java.util.Random;
     55 
     56 import com.android.internal.R;
     57 
     58 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
     59 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
     60 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
     61 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
     62 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
     63 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
     64 
     65 public abstract class SMSDispatcher extends Handler {
     66     static final String TAG = "SMS";    // accessed from inner class
     67     private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
     68 
     69     /** Default timeout for SMS sent query */
     70     private static final int DEFAULT_SMS_TIMEOUT = 6000;
     71 
     72     /** Permission required to receive SMS and SMS-CB messages. */
     73     public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS";
     74 
     75     /** Permission required to receive ETWS and CMAS emergency broadcasts. */
     76     public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION =
     77             "android.permission.RECEIVE_EMERGENCY_BROADCAST";
     78 
     79     /** Query projection for checking for duplicate message segments. */
     80     private static final String[] PDU_PROJECTION = new String[] {
     81             "pdu"
     82     };
     83 
     84     /** Query projection for combining concatenated message segments. */
     85     private static final String[] PDU_SEQUENCE_PORT_PROJECTION = new String[] {
     86             "pdu",
     87             "sequence",
     88             "destination_port"
     89     };
     90 
     91     private static final int PDU_COLUMN = 0;
     92     private static final int SEQUENCE_COLUMN = 1;
     93     private static final int DESTINATION_PORT_COLUMN = 2;
     94 
     95     /** New SMS received. */
     96     protected static final int EVENT_NEW_SMS = 1;
     97 
     98     /** SMS send complete. */
     99     protected static final int EVENT_SEND_SMS_COMPLETE = 2;
    100 
    101     /** Retry sending a previously failed SMS message */
    102     private static final int EVENT_SEND_RETRY = 3;
    103 
    104     /** SMS confirm required */
    105     private static final int EVENT_POST_ALERT = 4;
    106 
    107     /** Send the user confirmed SMS */
    108     static final int EVENT_SEND_CONFIRMED_SMS = 5;  // accessed from inner class
    109 
    110     /** Alert is timeout */
    111     private static final int EVENT_ALERT_TIMEOUT = 6;
    112 
    113     /** Stop the sending */
    114     static final int EVENT_STOP_SENDING = 7;        // accessed from inner class
    115 
    116     protected final Phone mPhone;
    117     protected final Context mContext;
    118     protected final ContentResolver mResolver;
    119     protected final CommandsInterface mCm;
    120     protected final SmsStorageMonitor mStorageMonitor;
    121 
    122     protected final WapPushOverSms mWapPush;
    123 
    124     protected static final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
    125 
    126     /** Maximum number of times to retry sending a failed SMS. */
    127     private static final int MAX_SEND_RETRIES = 3;
    128     /** Delay before next send attempt on a failed SMS, in milliseconds. */
    129     private static final int SEND_RETRY_DELAY = 2000;
    130     /** single part SMS */
    131     private static final int SINGLE_PART_SMS = 1;
    132     /** Message sending queue limit */
    133     private static final int MO_MSG_QUEUE_LIMIT = 5;
    134 
    135     /**
    136      * Message reference for a CONCATENATED_8_BIT_REFERENCE or
    137      * CONCATENATED_16_BIT_REFERENCE message set.  Should be
    138      * incremented for each set of concatenated messages.
    139      * Static field shared by all dispatcher objects.
    140      */
    141     private static int sConcatenatedRef = new Random().nextInt(256);
    142 
    143     /** Outgoing message counter. Shared by all dispatchers. */
    144     private final SmsUsageMonitor mUsageMonitor;
    145 
    146     private final ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
    147 
    148     /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
    149     private PowerManager.WakeLock mWakeLock;
    150 
    151     /**
    152      * Hold the wake lock for 5 seconds, which should be enough time for
    153      * any receiver(s) to grab its own wake lock.
    154      */
    155     private static final int WAKE_LOCK_TIMEOUT = 5000;
    156 
    157     /* Flags indicating whether the current device allows sms service */
    158     protected boolean mSmsCapable = true;
    159     protected boolean mSmsReceiveDisabled;
    160     protected boolean mSmsSendDisabled;
    161 
    162     protected int mRemainingMessages = -1;
    163 
    164     protected static int getNextConcatenatedRef() {
    165         sConcatenatedRef += 1;
    166         return sConcatenatedRef;
    167     }
    168 
    169     /**
    170      * Create a new SMS dispatcher.
    171      * @param phone the Phone to use
    172      * @param storageMonitor the SmsStorageMonitor to use
    173      * @param usageMonitor the SmsUsageMonitor to use
    174      */
    175     protected SMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
    176             SmsUsageMonitor usageMonitor) {
    177         mPhone = phone;
    178         mWapPush = new WapPushOverSms(phone, this);
    179         mContext = phone.getContext();
    180         mResolver = mContext.getContentResolver();
    181         mCm = phone.mCM;
    182         mStorageMonitor = storageMonitor;
    183         mUsageMonitor = usageMonitor;
    184 
    185         createWakelock();
    186 
    187         mSmsCapable = mContext.getResources().getBoolean(
    188                 com.android.internal.R.bool.config_sms_capable);
    189         mSmsReceiveDisabled = !SystemProperties.getBoolean(
    190                                 TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable);
    191         mSmsSendDisabled = !SystemProperties.getBoolean(
    192                                 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable);
    193         Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
    194                 + " mSmsReceiveDisabled=" + mSmsReceiveDisabled
    195                 + " mSmsSendDisabled=" + mSmsSendDisabled);
    196     }
    197 
    198     /** Unregister for incoming SMS events. */
    199     public abstract void dispose();
    200 
    201     /**
    202      * The format of the message PDU in the associated broadcast intent.
    203      * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
    204      * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
    205      *
    206      * Note: All applications which handle incoming SMS messages by processing the
    207      * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent
    208      * into the new methods in {@link android.telephony.SmsMessage} which take an
    209      * extra format parameter. This is required in order to correctly decode the PDU on
    210      * devices which require support for both 3GPP and 3GPP2 formats at the same time,
    211      * such as CDMA/LTE devices and GSM/CDMA world phones.
    212      *
    213      * @return the format of the message PDU
    214      */
    215     protected abstract String getFormat();
    216 
    217     @Override
    218     protected void finalize() {
    219         Log.d(TAG, "SMSDispatcher finalized");
    220     }
    221 
    222 
    223     /* TODO: Need to figure out how to keep track of status report routing in a
    224      *       persistent manner. If the phone process restarts (reboot or crash),
    225      *       we will lose this list and any status reports that come in after
    226      *       will be dropped.
    227      */
    228     /** Sent messages awaiting a delivery status report. */
    229     protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
    230 
    231     /**
    232      * Handles events coming from the phone stack. Overridden from handler.
    233      *
    234      * @param msg the message to handle
    235      */
    236     @Override
    237     public void handleMessage(Message msg) {
    238         AsyncResult ar;
    239 
    240         switch (msg.what) {
    241         case EVENT_NEW_SMS:
    242             // A new SMS has been received by the device
    243             if (false) {
    244                 Log.d(TAG, "New SMS Message Received");
    245             }
    246 
    247             SmsMessage sms;
    248 
    249             ar = (AsyncResult) msg.obj;
    250 
    251             if (ar.exception != null) {
    252                 Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception);
    253                 return;
    254             }
    255 
    256             sms = (SmsMessage) ar.result;
    257             try {
    258                 int result = dispatchMessage(sms.mWrappedSmsMessage);
    259                 if (result != Activity.RESULT_OK) {
    260                     // RESULT_OK means that message was broadcast for app(s) to handle.
    261                     // Any other result, we should ack here.
    262                     boolean handled = (result == Intents.RESULT_SMS_HANDLED);
    263                     notifyAndAcknowledgeLastIncomingSms(handled, result, null);
    264                 }
    265             } catch (RuntimeException ex) {
    266                 Log.e(TAG, "Exception dispatching message", ex);
    267                 notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
    268             }
    269 
    270             break;
    271 
    272         case EVENT_SEND_SMS_COMPLETE:
    273             // An outbound SMS has been successfully transferred, or failed.
    274             handleSendComplete((AsyncResult) msg.obj);
    275             break;
    276 
    277         case EVENT_SEND_RETRY:
    278             sendSms((SmsTracker) msg.obj);
    279             break;
    280 
    281         case EVENT_POST_ALERT:
    282             handleReachSentLimit((SmsTracker)(msg.obj));
    283             break;
    284 
    285         case EVENT_ALERT_TIMEOUT:
    286             ((AlertDialog)(msg.obj)).dismiss();
    287             msg.obj = null;
    288             if (mSTrackers.isEmpty() == false) {
    289                 try {
    290                     SmsTracker sTracker = mSTrackers.remove(0);
    291                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
    292                 } catch (CanceledException ex) {
    293                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
    294                 }
    295             }
    296             if (false) {
    297                 Log.d(TAG, "EVENT_ALERT_TIMEOUT, message stop sending");
    298             }
    299             break;
    300 
    301         case EVENT_SEND_CONFIRMED_SMS:
    302             if (mSTrackers.isEmpty() == false) {
    303                 SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
    304                 if (sTracker.isMultipart()) {
    305                     sendMultipartSms(sTracker);
    306                 } else {
    307                     sendSms(sTracker);
    308                 }
    309                 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
    310             }
    311             break;
    312 
    313         case EVENT_STOP_SENDING:
    314             if (mSTrackers.isEmpty() == false) {
    315                 // Remove the latest one.
    316                 try {
    317                     SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
    318                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
    319                 } catch (CanceledException ex) {
    320                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
    321                 }
    322                 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
    323             }
    324             break;
    325         }
    326     }
    327 
    328     private void createWakelock() {
    329         PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
    330         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher");
    331         mWakeLock.setReferenceCounted(true);
    332     }
    333 
    334     /**
    335      * Grabs a wake lock and sends intent as an ordered broadcast.
    336      * The resultReceiver will check for errors and ACK/NACK back
    337      * to the RIL.
    338      *
    339      * @param intent intent to broadcast
    340      * @param permission Receivers are required to have this permission
    341      */
    342     void dispatch(Intent intent, String permission) {
    343         // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any
    344         // receivers time to take their own wake locks.
    345         mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
    346         mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,
    347                 this, Activity.RESULT_OK, null, null);
    348     }
    349 
    350     /**
    351      * Called when SMS send completes. Broadcasts a sentIntent on success.
    352      * On failure, either sets up retries or broadcasts a sentIntent with
    353      * the failure in the result code.
    354      *
    355      * @param ar AsyncResult passed into the message handler.  ar.result should
    356      *           an SmsResponse instance if send was successful.  ar.userObj
    357      *           should be an SmsTracker instance.
    358      */
    359     protected void handleSendComplete(AsyncResult ar) {
    360         SmsTracker tracker = (SmsTracker) ar.userObj;
    361         PendingIntent sentIntent = tracker.mSentIntent;
    362 
    363         if (ar.exception == null) {
    364             if (false) {
    365                 Log.d(TAG, "SMS send complete. Broadcasting "
    366                         + "intent: " + sentIntent);
    367             }
    368 
    369             if (tracker.mDeliveryIntent != null) {
    370                 // Expecting a status report.  Add it to the list.
    371                 int messageRef = ((SmsResponse)ar.result).messageRef;
    372                 tracker.mMessageRef = messageRef;
    373                 deliveryPendingList.add(tracker);
    374             }
    375 
    376             if (sentIntent != null) {
    377                 try {
    378                     if (mRemainingMessages > -1) {
    379                         mRemainingMessages--;
    380                     }
    381 
    382                     if (mRemainingMessages == 0) {
    383                         Intent sendNext = new Intent();
    384                         sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
    385                         sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
    386                     } else {
    387                         sentIntent.send(Activity.RESULT_OK);
    388                     }
    389                 } catch (CanceledException ex) {}
    390             }
    391         } else {
    392             if (false) {
    393                 Log.d(TAG, "SMS send failed");
    394             }
    395 
    396             int ss = mPhone.getServiceState().getState();
    397 
    398             if (ss != ServiceState.STATE_IN_SERVICE) {
    399                 handleNotInService(ss, tracker);
    400             } else if ((((CommandException)(ar.exception)).getCommandError()
    401                     == CommandException.Error.SMS_FAIL_RETRY) &&
    402                    tracker.mRetryCount < MAX_SEND_RETRIES) {
    403                 // Retry after a delay if needed.
    404                 // TODO: According to TS 23.040, 9.2.3.6, we should resend
    405                 //       with the same TP-MR as the failed message, and
    406                 //       TP-RD set to 1.  However, we don't have a means of
    407                 //       knowing the MR for the failed message (EF_SMSstatus
    408                 //       may or may not have the MR corresponding to this
    409                 //       message, depending on the failure).  Also, in some
    410                 //       implementations this retry is handled by the baseband.
    411                 tracker.mRetryCount++;
    412                 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
    413                 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
    414             } else if (tracker.mSentIntent != null) {
    415                 int error = RESULT_ERROR_GENERIC_FAILURE;
    416 
    417                 if (((CommandException)(ar.exception)).getCommandError()
    418                         == CommandException.Error.FDN_CHECK_FAILURE) {
    419                     error = RESULT_ERROR_FDN_CHECK_FAILURE;
    420                 }
    421                 // Done retrying; return an error to the app.
    422                 try {
    423                     Intent fillIn = new Intent();
    424                     if (ar.result != null) {
    425                         fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
    426                     }
    427                     if (mRemainingMessages > -1) {
    428                         mRemainingMessages--;
    429                     }
    430 
    431                     if (mRemainingMessages == 0) {
    432                         fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
    433                     }
    434 
    435                     tracker.mSentIntent.send(mContext, error, fillIn);
    436                 } catch (CanceledException ex) {}
    437             }
    438         }
    439     }
    440 
    441     /**
    442      * Handles outbound message when the phone is not in service.
    443      *
    444      * @param ss     Current service state.  Valid values are:
    445      *                  OUT_OF_SERVICE
    446      *                  EMERGENCY_ONLY
    447      *                  POWER_OFF
    448      * @param tracker   An SmsTracker for the current message.
    449      */
    450     protected static void handleNotInService(int ss, SmsTracker tracker) {
    451         if (tracker.mSentIntent != null) {
    452             try {
    453                 if (ss == ServiceState.STATE_POWER_OFF) {
    454                     tracker.mSentIntent.send(RESULT_ERROR_RADIO_OFF);
    455                 } else {
    456                     tracker.mSentIntent.send(RESULT_ERROR_NO_SERVICE);
    457                 }
    458             } catch (CanceledException ex) {}
    459         }
    460     }
    461 
    462     /**
    463      * Dispatches an incoming SMS messages.
    464      *
    465      * @param sms the incoming message from the phone
    466      * @return a result code from {@link Telephony.Sms.Intents}, or
    467      *         {@link Activity#RESULT_OK} if the message has been broadcast
    468      *         to applications
    469      */
    470     public abstract int dispatchMessage(SmsMessageBase sms);
    471 
    472     /**
    473      * Dispatch a normal incoming SMS. This is called from the format-specific
    474      * {@link #dispatchMessage(SmsMessageBase)} if no format-specific handling is required.
    475      *
    476      * @param sms
    477      * @return
    478      */
    479     protected int dispatchNormalMessage(SmsMessageBase sms) {
    480         SmsHeader smsHeader = sms.getUserDataHeader();
    481 
    482         // See if message is partial or port addressed.
    483         if ((smsHeader == null) || (smsHeader.concatRef == null)) {
    484             // Message is not partial (not part of concatenated sequence).
    485             byte[][] pdus = new byte[1][];
    486             pdus[0] = sms.getPdu();
    487 
    488             if (smsHeader != null && smsHeader.portAddrs != null) {
    489                 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
    490                     // GSM-style WAP indication
    491                     return mWapPush.dispatchWapPdu(sms.getUserData());
    492                 } else {
    493                     // The message was sent to a port, so concoct a URI for it.
    494                     dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
    495                 }
    496             } else {
    497                 // Normal short and non-port-addressed message, dispatch it.
    498                 dispatchPdus(pdus);
    499             }
    500             return Activity.RESULT_OK;
    501         } else {
    502             // Process the message part.
    503             SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
    504             SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
    505             return processMessagePart(sms.getPdu(), sms.getOriginatingAddress(),
    506                     concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount,
    507                     sms.getTimestampMillis(), (portAddrs != null ? portAddrs.destPort : -1), false);
    508         }
    509     }
    510 
    511     /**
    512      * If this is the last part send the parts out to the application, otherwise
    513      * the part is stored for later processing. Handles both 3GPP concatenated messages
    514      * as well as 3GPP2 format WAP push messages processed by
    515      * {@link com.android.internal.telephony.cdma.CdmaSMSDispatcher#processCdmaWapPdu}.
    516      *
    517      * @param pdu the message PDU, or the datagram portion of a CDMA WDP datagram segment
    518      * @param address the originating address
    519      * @param referenceNumber distinguishes concatenated messages from the same sender
    520      * @param sequenceNumber the order of this segment in the message
    521      *          (starting at 0 for CDMA WDP datagrams and 1 for concatenated messages).
    522      * @param messageCount the number of segments in the message
    523      * @param timestamp the service center timestamp in millis
    524      * @param destPort the destination port for the message, or -1 for no destination port
    525      * @param isCdmaWapPush true if pdu is a CDMA WDP datagram segment and not an SM PDU
    526      *
    527      * @return a result code from {@link Telephony.Sms.Intents}, or
    528      *         {@link Activity#RESULT_OK} if the message has been broadcast
    529      *         to applications
    530      */
    531     protected int processMessagePart(byte[] pdu, String address, int referenceNumber,
    532             int sequenceNumber, int messageCount, long timestamp, int destPort,
    533             boolean isCdmaWapPush) {
    534         byte[][] pdus = null;
    535         Cursor cursor = null;
    536         try {
    537             // used by several query selection arguments
    538             String refNumber = Integer.toString(referenceNumber);
    539             String seqNumber = Integer.toString(sequenceNumber);
    540 
    541             // Check for duplicate message segment
    542             cursor = mResolver.query(mRawUri, PDU_PROJECTION,
    543                     "address=? AND reference_number=? AND sequence=?",
    544                     new String[] {address, refNumber, seqNumber}, null);
    545 
    546             // moveToNext() returns false if no duplicates were found
    547             if (cursor.moveToNext()) {
    548                 Log.w(TAG, "Discarding duplicate message segment from address=" + address
    549                         + " refNumber=" + refNumber + " seqNumber=" + seqNumber);
    550                 String oldPduString = cursor.getString(PDU_COLUMN);
    551                 byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
    552                 if (!Arrays.equals(oldPdu, pdu)) {
    553                     Log.e(TAG, "Warning: dup message segment PDU of length " + pdu.length
    554                             + " is different from existing PDU of length " + oldPdu.length);
    555                 }
    556                 return Intents.RESULT_SMS_HANDLED;
    557             }
    558             cursor.close();
    559 
    560             // not a dup, query for all other segments of this concatenated message
    561             String where = "address=? AND reference_number=?";
    562             String[] whereArgs = new String[] {address, refNumber};
    563             cursor = mResolver.query(mRawUri, PDU_SEQUENCE_PORT_PROJECTION, where, whereArgs, null);
    564 
    565             int cursorCount = cursor.getCount();
    566             if (cursorCount != messageCount - 1) {
    567                 // We don't have all the parts yet, store this one away
    568                 ContentValues values = new ContentValues();
    569                 values.put("date", timestamp);
    570                 values.put("pdu", HexDump.toHexString(pdu));
    571                 values.put("address", address);
    572                 values.put("reference_number", referenceNumber);
    573                 values.put("count", messageCount);
    574                 values.put("sequence", sequenceNumber);
    575                 if (destPort != -1) {
    576                     values.put("destination_port", destPort);
    577                 }
    578                 mResolver.insert(mRawUri, values);
    579                 return Intents.RESULT_SMS_HANDLED;
    580             }
    581 
    582             // All the parts are in place, deal with them
    583             pdus = new byte[messageCount][];
    584             for (int i = 0; i < cursorCount; i++) {
    585                 cursor.moveToNext();
    586                 int cursorSequence = cursor.getInt(SEQUENCE_COLUMN);
    587                 // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
    588                 if (!isCdmaWapPush) {
    589                     cursorSequence--;
    590                 }
    591                 pdus[cursorSequence] = HexDump.hexStringToByteArray(
    592                         cursor.getString(PDU_COLUMN));
    593 
    594                 // Read the destination port from the first segment (needed for CDMA WAP PDU).
    595                 // It's not a bad idea to prefer the port from the first segment for 3GPP as well.
    596                 if (cursorSequence == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
    597                     destPort = cursor.getInt(DESTINATION_PORT_COLUMN);
    598                 }
    599             }
    600             // This one isn't in the DB, so add it
    601             // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0
    602             if (isCdmaWapPush) {
    603                 pdus[sequenceNumber] = pdu;
    604             } else {
    605                 pdus[sequenceNumber - 1] = pdu;
    606             }
    607 
    608             // Remove the parts from the database
    609             mResolver.delete(mRawUri, where, whereArgs);
    610         } catch (SQLException e) {
    611             Log.e(TAG, "Can't access multipart SMS database", e);
    612             return Intents.RESULT_SMS_GENERIC_ERROR;
    613         } finally {
    614             if (cursor != null) cursor.close();
    615         }
    616 
    617         // Special handling for CDMA WDP datagrams
    618         if (isCdmaWapPush) {
    619             // Build up the data stream
    620             ByteArrayOutputStream output = new ByteArrayOutputStream();
    621             for (int i = 0; i < messageCount; i++) {
    622                 // reassemble the (WSP-)pdu
    623                 output.write(pdus[i], 0, pdus[i].length);
    624             }
    625             byte[] datagram = output.toByteArray();
    626 
    627             // Dispatch the PDU to applications
    628             if (destPort == SmsHeader.PORT_WAP_PUSH) {
    629                 // Handle the PUSH
    630                 return mWapPush.dispatchWapPdu(datagram);
    631             } else {
    632                 pdus = new byte[1][];
    633                 pdus[0] = datagram;
    634                 // The messages were sent to any other WAP port
    635                 dispatchPortAddressedPdus(pdus, destPort);
    636                 return Activity.RESULT_OK;
    637             }
    638         }
    639 
    640         // Dispatch the PDUs to applications
    641         if (destPort != -1) {
    642             if (destPort == SmsHeader.PORT_WAP_PUSH) {
    643                 // Build up the data stream
    644                 ByteArrayOutputStream output = new ByteArrayOutputStream();
    645                 for (int i = 0; i < messageCount; i++) {
    646                     SmsMessage msg = SmsMessage.createFromPdu(pdus[i], getFormat());
    647                     byte[] data = msg.getUserData();
    648                     output.write(data, 0, data.length);
    649                 }
    650                 // Handle the PUSH
    651                 return mWapPush.dispatchWapPdu(output.toByteArray());
    652             } else {
    653                 // The messages were sent to a port, so concoct a URI for it
    654                 dispatchPortAddressedPdus(pdus, destPort);
    655             }
    656         } else {
    657             // The messages were not sent to a port
    658             dispatchPdus(pdus);
    659         }
    660         return Activity.RESULT_OK;
    661     }
    662 
    663     /**
    664      * Dispatches standard PDUs to interested applications
    665      *
    666      * @param pdus The raw PDUs making up the message
    667      */
    668     protected void dispatchPdus(byte[][] pdus) {
    669         Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
    670         intent.putExtra("pdus", pdus);
    671         intent.putExtra("format", getFormat());
    672         dispatch(intent, RECEIVE_SMS_PERMISSION);
    673     }
    674 
    675     /**
    676      * Dispatches port addressed PDUs to interested applications
    677      *
    678      * @param pdus The raw PDUs making up the message
    679      * @param port The destination port of the messages
    680      */
    681     protected void dispatchPortAddressedPdus(byte[][] pdus, int port) {
    682         Uri uri = Uri.parse("sms://localhost:" + port);
    683         Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
    684         intent.putExtra("pdus", pdus);
    685         intent.putExtra("format", getFormat());
    686         dispatch(intent, RECEIVE_SMS_PERMISSION);
    687     }
    688 
    689     /**
    690      * Send a data based SMS to a specific application port.
    691      *
    692      * @param destAddr the address to send the message to
    693      * @param scAddr is the service center address or null to use
    694      *  the current default SMSC
    695      * @param destPort the port to deliver the message to
    696      * @param data the body of the message to send
    697      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    698      *  broadcast when the message is successfully sent, or failed.
    699      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    700      *  or one of these errors:<br>
    701      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    702      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    703      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    704      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
    705      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    706      *  the extra "errorCode" containing a radio technology specific value,
    707      *  generally only useful for troubleshooting.<br>
    708      *  The per-application based SMS control checks sentIntent. If sentIntent
    709      *  is NULL the caller will be checked against all unknown applications,
    710      *  which cause smaller number of SMS to be sent in checking period.
    711      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    712      *  broadcast when the message is delivered to the recipient.  The
    713      *  raw pdu of the status report is in the extended data ("pdu").
    714      */
    715     protected abstract void sendData(String destAddr, String scAddr, int destPort,
    716             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
    717 
    718     /**
    719      * Send a text based SMS.
    720      *
    721      * @param destAddr the address to send the message to
    722      * @param scAddr is the service center address or null to use
    723      *  the current default SMSC
    724      * @param text the body of the message to send
    725      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    726      *  broadcast when the message is successfully sent, or failed.
    727      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    728      *  or one of these errors:<br>
    729      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    730      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    731      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    732      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
    733      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    734      *  the extra "errorCode" containing a radio technology specific value,
    735      *  generally only useful for troubleshooting.<br>
    736      *  The per-application based SMS control checks sentIntent. If sentIntent
    737      *  is NULL the caller will be checked against all unknown applications,
    738      *  which cause smaller number of SMS to be sent in checking period.
    739      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    740      *  broadcast when the message is delivered to the recipient.  The
    741      *  raw pdu of the status report is in the extended data ("pdu").
    742      */
    743     protected abstract void sendText(String destAddr, String scAddr,
    744             String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
    745 
    746     /**
    747      * Calculate the number of septets needed to encode the message.
    748      *
    749      * @param messageBody the message to encode
    750      * @param use7bitOnly ignore (but still count) illegal characters if true
    751      * @return TextEncodingDetails
    752      */
    753     protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
    754             boolean use7bitOnly);
    755 
    756     /**
    757      * Send a multi-part text based SMS.
    758      *
    759      * @param destAddr the address to send the message to
    760      * @param scAddr is the service center address or null to use
    761      *   the current default SMSC
    762      * @param parts an <code>ArrayList</code> of strings that, in order,
    763      *   comprise the original message
    764      * @param sentIntents if not null, an <code>ArrayList</code> of
    765      *   <code>PendingIntent</code>s (one for each message part) that is
    766      *   broadcast when the corresponding message part has been sent.
    767      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    768      *   or one of these errors:
    769      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
    770      *   <code>RESULT_ERROR_RADIO_OFF</code>
    771      *   <code>RESULT_ERROR_NULL_PDU</code>
    772      *   <code>RESULT_ERROR_NO_SERVICE</code>.
    773      *  The per-application based SMS control checks sentIntent. If sentIntent
    774      *  is NULL the caller will be checked against all unknown applications,
    775      *  which cause smaller number of SMS to be sent in checking period.
    776      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    777      *   <code>PendingIntent</code>s (one for each message part) that is
    778      *   broadcast when the corresponding message part has been delivered
    779      *   to the recipient.  The raw pdu of the status report is in the
    780      *   extended data ("pdu").
    781      */
    782     protected void sendMultipartText(String destAddr, String scAddr,
    783             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
    784             ArrayList<PendingIntent> deliveryIntents) {
    785 
    786         int refNumber = getNextConcatenatedRef() & 0x00FF;
    787         int msgCount = parts.size();
    788         int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN;
    789 
    790         mRemainingMessages = msgCount;
    791 
    792         TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
    793         for (int i = 0; i < msgCount; i++) {
    794             TextEncodingDetails details = calculateLength(parts.get(i), false);
    795             if (encoding != details.codeUnitSize
    796                     && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
    797                             || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
    798                 encoding = details.codeUnitSize;
    799             }
    800             encodingForParts[i] = details;
    801         }
    802 
    803         for (int i = 0; i < msgCount; i++) {
    804             SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
    805             concatRef.refNumber = refNumber;
    806             concatRef.seqNumber = i + 1;  // 1-based sequence
    807             concatRef.msgCount = msgCount;
    808             // TODO: We currently set this to true since our messaging app will never
    809             // send more than 255 parts (it converts the message to MMS well before that).
    810             // However, we should support 3rd party messaging apps that might need 16-bit
    811             // references
    812             // Note:  It's not sufficient to just flip this bit to true; it will have
    813             // ripple effects (several calculations assume 8-bit ref).
    814             concatRef.isEightBits = true;
    815             SmsHeader smsHeader = new SmsHeader();
    816             smsHeader.concatRef = concatRef;
    817 
    818             // Set the national language tables for 3GPP 7-bit encoding, if enabled.
    819             if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
    820                 smsHeader.languageTable = encodingForParts[i].languageTable;
    821                 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
    822             }
    823 
    824             PendingIntent sentIntent = null;
    825             if (sentIntents != null && sentIntents.size() > i) {
    826                 sentIntent = sentIntents.get(i);
    827             }
    828 
    829             PendingIntent deliveryIntent = null;
    830             if (deliveryIntents != null && deliveryIntents.size() > i) {
    831                 deliveryIntent = deliveryIntents.get(i);
    832             }
    833 
    834             sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
    835                     sentIntent, deliveryIntent, (i == (msgCount - 1)));
    836         }
    837 
    838     }
    839 
    840     /**
    841      * Create a new SubmitPdu and send it.
    842      */
    843     protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress,
    844             String message, SmsHeader smsHeader, int encoding,
    845             PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart);
    846 
    847     /**
    848      * Send a SMS
    849      *
    850      * @param smsc the SMSC to send the message through, or NULL for the
    851      *  default SMSC
    852      * @param pdu the raw PDU to send
    853      * @param sentIntent if not NULL this <code>Intent</code> is
    854      *  broadcast when the message is successfully sent, or failed.
    855      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    856      *  or one of these errors:
    857      *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
    858      *  <code>RESULT_ERROR_RADIO_OFF</code>
    859      *  <code>RESULT_ERROR_NULL_PDU</code>
    860      *  <code>RESULT_ERROR_NO_SERVICE</code>.
    861      *  The per-application based SMS control checks sentIntent. If sentIntent
    862      *  is NULL the caller will be checked against all unknown applications,
    863      *  which cause smaller number of SMS to be sent in checking period.
    864      * @param deliveryIntent if not NULL this <code>Intent</code> is
    865      *  broadcast when the message is delivered to the recipient.  The
    866      *  raw pdu of the status report is in the extended data ("pdu").
    867      */
    868     protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
    869             PendingIntent deliveryIntent) {
    870         if (mSmsSendDisabled) {
    871             if (sentIntent != null) {
    872                 try {
    873                     sentIntent.send(RESULT_ERROR_NO_SERVICE);
    874                 } catch (CanceledException ex) {}
    875             }
    876             Log.d(TAG, "Device does not support sending sms.");
    877             return;
    878         }
    879 
    880         if (pdu == null) {
    881             if (sentIntent != null) {
    882                 try {
    883                     sentIntent.send(RESULT_ERROR_NULL_PDU);
    884                 } catch (CanceledException ex) {}
    885             }
    886             return;
    887         }
    888 
    889         HashMap<String, Object> map = new HashMap<String, Object>();
    890         map.put("smsc", smsc);
    891         map.put("pdu", pdu);
    892 
    893         SmsTracker tracker = new SmsTracker(map, sentIntent,
    894                 deliveryIntent);
    895         int ss = mPhone.getServiceState().getState();
    896 
    897         if (ss != ServiceState.STATE_IN_SERVICE) {
    898             handleNotInService(ss, tracker);
    899         } else {
    900             String appName = getAppNameByIntent(sentIntent);
    901             if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
    902                 sendSms(tracker);
    903             } else {
    904                 sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
    905             }
    906         }
    907     }
    908 
    909     /**
    910      * Post an alert while SMS needs user confirm.
    911      *
    912      * An SmsTracker for the current message.
    913      */
    914     protected void handleReachSentLimit(SmsTracker tracker) {
    915         if (mSTrackers.size() >= MO_MSG_QUEUE_LIMIT) {
    916             // Deny the sending when the queue limit is reached.
    917             try {
    918                 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
    919             } catch (CanceledException ex) {
    920                 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
    921             }
    922             return;
    923         }
    924 
    925         Resources r = Resources.getSystem();
    926 
    927         String appName = getAppNameByIntent(tracker.mSentIntent);
    928 
    929         AlertDialog d = new AlertDialog.Builder(mContext)
    930                 .setTitle(r.getString(R.string.sms_control_title))
    931                 .setMessage(appName + " " + r.getString(R.string.sms_control_message))
    932                 .setPositiveButton(r.getString(R.string.sms_control_yes), mListener)
    933                 .setNegativeButton(r.getString(R.string.sms_control_no), mListener)
    934                 .create();
    935 
    936         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    937         d.show();
    938 
    939         mSTrackers.add(tracker);
    940         sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
    941                 DEFAULT_SMS_TIMEOUT);
    942     }
    943 
    944     protected static String getAppNameByIntent(PendingIntent intent) {
    945         Resources r = Resources.getSystem();
    946         return (intent != null) ? intent.getTargetPackage()
    947             : r.getString(R.string.sms_control_default_app_name);
    948     }
    949 
    950     /**
    951      * Send the message along to the radio.
    952      *
    953      * @param tracker holds the SMS message to send
    954      */
    955     protected abstract void sendSms(SmsTracker tracker);
    956 
    957     /**
    958      * Send the multi-part SMS based on multipart Sms tracker
    959      *
    960      * @param tracker holds the multipart Sms tracker ready to be sent
    961      */
    962     private void sendMultipartSms(SmsTracker tracker) {
    963         ArrayList<String> parts;
    964         ArrayList<PendingIntent> sentIntents;
    965         ArrayList<PendingIntent> deliveryIntents;
    966 
    967         HashMap<String, Object> map = tracker.mData;
    968 
    969         String destinationAddress = (String) map.get("destination");
    970         String scAddress = (String) map.get("scaddress");
    971 
    972         parts = (ArrayList<String>) map.get("parts");
    973         sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
    974         deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
    975 
    976         // check if in service
    977         int ss = mPhone.getServiceState().getState();
    978         if (ss != ServiceState.STATE_IN_SERVICE) {
    979             for (int i = 0, count = parts.size(); i < count; i++) {
    980                 PendingIntent sentIntent = null;
    981                 if (sentIntents != null && sentIntents.size() > i) {
    982                     sentIntent = sentIntents.get(i);
    983                 }
    984                 handleNotInService(ss, new SmsTracker(null, sentIntent, null));
    985             }
    986             return;
    987         }
    988 
    989         sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents);
    990     }
    991 
    992     /**
    993      * Send an acknowledge message.
    994      * @param success indicates that last message was successfully received.
    995      * @param result result code indicating any error
    996      * @param response callback message sent when operation completes.
    997      */
    998     protected abstract void acknowledgeLastIncomingSms(boolean success,
    999             int result, Message response);
   1000 
   1001     /**
   1002      * Notify interested apps if the framework has rejected an incoming SMS,
   1003      * and send an acknowledge message to the network.
   1004      * @param success indicates that last message was successfully received.
   1005      * @param result result code indicating any error
   1006      * @param response callback message sent when operation completes.
   1007      */
   1008     private void notifyAndAcknowledgeLastIncomingSms(boolean success,
   1009             int result, Message response) {
   1010         if (!success) {
   1011             // broadcast SMS_REJECTED_ACTION intent
   1012             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
   1013             intent.putExtra("result", result);
   1014             mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
   1015             mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
   1016         }
   1017         acknowledgeLastIncomingSms(success, result, response);
   1018     }
   1019 
   1020     /**
   1021      * Keeps track of an SMS that has been sent to the RIL, until it has
   1022      * successfully been sent, or we're done trying.
   1023      *
   1024      */
   1025     protected static final class SmsTracker {
   1026         // fields need to be public for derived SmsDispatchers
   1027         public final HashMap<String, Object> mData;
   1028         public int mRetryCount;
   1029         public int mMessageRef;
   1030 
   1031         public final PendingIntent mSentIntent;
   1032         public final PendingIntent mDeliveryIntent;
   1033 
   1034         public SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
   1035                 PendingIntent deliveryIntent) {
   1036             mData = data;
   1037             mSentIntent = sentIntent;
   1038             mDeliveryIntent = deliveryIntent;
   1039             mRetryCount = 0;
   1040         }
   1041 
   1042         /**
   1043          * Returns whether this tracker holds a multi-part SMS.
   1044          * @return true if the tracker holds a multi-part SMS; false otherwise
   1045          */
   1046         protected boolean isMultipart() {
   1047             HashMap map = mData;
   1048             return map.containsKey("parts");
   1049         }
   1050     }
   1051 
   1052     private final DialogInterface.OnClickListener mListener =
   1053         new DialogInterface.OnClickListener() {
   1054 
   1055             public void onClick(DialogInterface dialog, int which) {
   1056                 if (which == DialogInterface.BUTTON_POSITIVE) {
   1057                     Log.d(TAG, "click YES to send out sms");
   1058                     sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS));
   1059                 } else if (which == DialogInterface.BUTTON_NEGATIVE) {
   1060                     Log.d(TAG, "click NO to stop sending");
   1061                     sendMessage(obtainMessage(EVENT_STOP_SENDING));
   1062                 }
   1063             }
   1064         };
   1065 
   1066     private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
   1067         @Override
   1068         public void onReceive(Context context, Intent intent) {
   1069             // Assume the intent is one of the SMS receive intents that
   1070             // was sent as an ordered broadcast.  Check result and ACK.
   1071             int rc = getResultCode();
   1072             boolean success = (rc == Activity.RESULT_OK)
   1073                     || (rc == Intents.RESULT_SMS_HANDLED);
   1074 
   1075             // For a multi-part message, this only ACKs the last part.
   1076             // Previous parts were ACK'd as they were received.
   1077             acknowledgeLastIncomingSms(success, rc, null);
   1078         }
   1079     };
   1080 
   1081     protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) {
   1082         if (isEmergencyMessage) {
   1083             Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
   1084             intent.putExtra("pdus", pdus);
   1085             Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus");
   1086             dispatch(intent, RECEIVE_EMERGENCY_BROADCAST_PERMISSION);
   1087         } else {
   1088             Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
   1089             intent.putExtra("pdus", pdus);
   1090             Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
   1091             dispatch(intent, RECEIVE_SMS_PERMISSION);
   1092         }
   1093     }
   1094 }
   1095