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