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