SMSDispatcher.java revision f7de51cc4d623cb661e5bef2ea80f6bdc9609cbd
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.PendingIntent;
21import android.app.AlertDialog;
22import android.app.PendingIntent.CanceledException;
23import android.content.BroadcastReceiver;
24import android.content.ContentResolver;
25import android.content.ContentValues;
26import android.content.Context;
27import android.content.Intent;
28import android.content.DialogInterface;
29import android.content.IntentFilter;
30import android.content.res.Resources;
31import android.database.Cursor;
32import android.database.SQLException;
33import android.net.Uri;
34import android.os.AsyncResult;
35import android.os.Environment;
36import android.os.Handler;
37import android.os.Message;
38import android.os.PowerManager;
39import android.os.StatFs;
40import android.provider.Telephony;
41import android.provider.Telephony.Sms.Intents;
42import android.provider.Settings;
43import android.telephony.SmsMessage;
44import android.telephony.ServiceState;
45import android.util.Config;
46import android.util.Log;
47import android.view.WindowManager;
48
49import com.android.internal.util.HexDump;
50
51import java.io.ByteArrayOutputStream;
52import java.util.ArrayList;
53import java.util.HashMap;
54import java.util.Random;
55
56import com.android.internal.R;
57
58import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
59import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
60import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
61import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
62import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
63import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
64
65
66public abstract class SMSDispatcher extends Handler {
67    private static final String TAG = "SMS";
68    private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
69
70    /** Default checking period for SMS sent without user permit */
71    private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
72
73    /** Default number of SMS sent in checking period without user permit */
74    private static final int DEFAULT_SMS_MAX_COUNT = 100;
75
76    /** Default timeout for SMS sent query */
77    private static final int DEFAULT_SMS_TIMEOUT = 6000;
78
79    protected static final String[] RAW_PROJECTION = new String[] {
80        "pdu",
81        "sequence",
82        "destination_port",
83    };
84
85    static final protected int EVENT_NEW_SMS = 1;
86
87    static final protected int EVENT_SEND_SMS_COMPLETE = 2;
88
89    /** Retry sending a previously failed SMS message */
90    static final protected int EVENT_SEND_RETRY = 3;
91
92    /** Status report received */
93    static final protected int EVENT_NEW_SMS_STATUS_REPORT = 5;
94
95    /** SIM/RUIM storage is full */
96    static final protected int EVENT_ICC_FULL = 6;
97
98    /** SMS confirm required */
99    static final protected int EVENT_POST_ALERT = 7;
100
101    /** Send the user confirmed SMS */
102    static final protected int EVENT_SEND_CONFIRMED_SMS = 8;
103
104    /** Alert is timeout */
105    static final protected int EVENT_ALERT_TIMEOUT = 9;
106
107    /** Stop the sending */
108    static final protected int EVENT_STOP_SENDING = 10;
109
110    /** Memory status reporting is acknowledged by RIL */
111    static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11;
112
113    /** Radio is ON */
114    static final protected int EVENT_RADIO_ON = 12;
115
116    /** New broadcast SMS */
117    static final protected int EVENT_NEW_BROADCAST_SMS = 13;
118
119    protected Phone mPhone;
120    protected Context mContext;
121    protected ContentResolver mResolver;
122    protected CommandsInterface mCm;
123
124    protected final WapPushOverSms mWapPush;
125
126    protected final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
127
128    /** Maximum number of times to retry sending a failed SMS. */
129    private static final int MAX_SEND_RETRIES = 3;
130    /** Delay before next send attempt on a failed SMS, in milliseconds. */
131    private static final int SEND_RETRY_DELAY = 2000;
132    /** single part SMS */
133    private static final int SINGLE_PART_SMS = 1;
134    /** Message sending queue limit */
135    private static final int MO_MSG_QUEUE_LIMIT = 5;
136
137    /**
138     * Message reference for a CONCATENATED_8_BIT_REFERENCE or
139     * CONCATENATED_16_BIT_REFERENCE message set.  Should be
140     * incremented for each set of concatenated messages.
141     */
142    private static int sConcatenatedRef;
143
144    private SmsCounter mCounter;
145
146    private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
147
148    /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
149    private PowerManager.WakeLock mWakeLock;
150
151    /**
152     * Hold the wake lock for 5 seconds, which should be enough time for
153     * any receiver(s) to grab its own wake lock.
154     */
155    private final int WAKE_LOCK_TIMEOUT = 5000;
156
157    protected boolean mStorageAvailable = true;
158    protected boolean mReportMemoryStatusPending = false;
159
160    protected static int mRemainingMessages = -1;
161
162    protected static int getNextConcatenatedRef() {
163        sConcatenatedRef += 1;
164        return sConcatenatedRef;
165    }
166
167    /**
168     *  Implement the per-application based SMS control, which only allows
169     *  a limit on the number of SMS/MMS messages an app can send in checking
170     *  period.
171     */
172    private class SmsCounter {
173        private int mCheckPeriod;
174        private int mMaxAllowed;
175        private HashMap<String, ArrayList<Long>> mSmsStamp;
176
177        /**
178         * Create SmsCounter
179         * @param mMax is the number of SMS allowed without user permit
180         * @param mPeriod is the checking period
181         */
182        SmsCounter(int mMax, int mPeriod) {
183            mMaxAllowed = mMax;
184            mCheckPeriod = mPeriod;
185            mSmsStamp = new HashMap<String, ArrayList<Long>> ();
186        }
187
188        /**
189         * Check to see if an application allow to send new SMS messages
190         *
191         * @param appName is the application sending sms
192         * @param smsWaiting is the number of new sms wants to be sent
193         * @return true if application is allowed to send the requested number
194         *         of new sms messages
195         */
196        boolean check(String appName, int smsWaiting) {
197            if (!mSmsStamp.containsKey(appName)) {
198                mSmsStamp.put(appName, new ArrayList<Long>());
199            }
200
201            return isUnderLimit(mSmsStamp.get(appName), smsWaiting);
202        }
203
204        private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
205            Long ct =  System.currentTimeMillis();
206
207            Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct);
208
209            while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) {
210                    sent.remove(0);
211            }
212
213
214            if ( (sent.size() + smsWaiting) <= mMaxAllowed) {
215                for (int i = 0; i < smsWaiting; i++ ) {
216                    sent.add(ct);
217                }
218                return true;
219            }
220            return false;
221        }
222    }
223
224    protected SMSDispatcher(PhoneBase phone) {
225        mPhone = phone;
226        mWapPush = new WapPushOverSms(phone, this);
227        mContext = phone.getContext();
228        mResolver = mContext.getContentResolver();
229        mCm = phone.mCM;
230
231        createWakelock();
232
233        int check_period = Settings.Secure.getInt(mResolver,
234                Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
235                DEFAULT_SMS_CHECK_PERIOD);
236        int max_count = Settings.Secure.getInt(mResolver,
237                Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT,
238                DEFAULT_SMS_MAX_COUNT);
239        mCounter = new SmsCounter(max_count, check_period);
240
241        mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);
242        mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
243        mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
244        mCm.registerForOn(this, EVENT_RADIO_ON, null);
245
246        // Don't always start message ref at 0.
247        sConcatenatedRef = new Random().nextInt(256);
248
249        // Register for device storage intents.  Use these to notify the RIL
250        // that storage for SMS is or is not available.
251        IntentFilter filter = new IntentFilter();
252        filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
253        filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
254        mContext.registerReceiver(mResultReceiver, filter);
255    }
256
257    public void dispose() {
258        mCm.unSetOnNewSMS(this);
259        mCm.unSetOnSmsStatus(this);
260        mCm.unSetOnIccSmsFull(this);
261        mCm.unregisterForOn(this);
262    }
263
264    protected void finalize() {
265        Log.d(TAG, "SMSDispatcher finalized");
266    }
267
268
269    /* TODO: Need to figure out how to keep track of status report routing in a
270     *       persistent manner. If the phone process restarts (reboot or crash),
271     *       we will lose this list and any status reports that come in after
272     *       will be dropped.
273     */
274    /** Sent messages awaiting a delivery status report. */
275    protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
276
277    /**
278     * Handles events coming from the phone stack. Overridden from handler.
279     *
280     * @param msg the message to handle
281     */
282    @Override
283    public void handleMessage(Message msg) {
284        AsyncResult ar;
285
286        switch (msg.what) {
287        case EVENT_NEW_SMS:
288            // A new SMS has been received by the device
289            if (Config.LOGD) {
290                Log.d(TAG, "New SMS Message Received");
291            }
292
293            SmsMessage sms;
294
295            ar = (AsyncResult) msg.obj;
296
297            if (ar.exception != null) {
298                Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception);
299                return;
300            }
301
302            sms = (SmsMessage) ar.result;
303            try {
304                int result = dispatchMessage(sms.mWrappedSmsMessage);
305                if (result != Activity.RESULT_OK) {
306                    // RESULT_OK means that message was broadcast for app(s) to handle.
307                    // Any other result, we should ack here.
308                    boolean handled = (result == Intents.RESULT_SMS_HANDLED);
309                    notifyAndAcknowledgeLastIncomingSms(handled, result, null);
310                }
311            } catch (RuntimeException ex) {
312                Log.e(TAG, "Exception dispatching message", ex);
313                notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
314            }
315
316            break;
317
318        case EVENT_SEND_SMS_COMPLETE:
319            // An outbound SMS has been successfully transferred, or failed.
320            handleSendComplete((AsyncResult) msg.obj);
321            break;
322
323        case EVENT_SEND_RETRY:
324            sendSms((SmsTracker) msg.obj);
325            break;
326
327        case EVENT_NEW_SMS_STATUS_REPORT:
328            handleStatusReport((AsyncResult)msg.obj);
329            break;
330
331        case EVENT_ICC_FULL:
332            handleIccFull();
333            break;
334
335        case EVENT_POST_ALERT:
336            handleReachSentLimit((SmsTracker)(msg.obj));
337            break;
338
339        case EVENT_ALERT_TIMEOUT:
340            ((AlertDialog)(msg.obj)).dismiss();
341            msg.obj = null;
342            if (mSTrackers.isEmpty() == false) {
343                try {
344                    SmsTracker sTracker = mSTrackers.remove(0);
345                    sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
346                } catch (CanceledException ex) {
347                    Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
348                }
349            }
350            if (Config.LOGD) {
351                Log.d(TAG, "EVENT_ALERT_TIMEOUT, message stop sending");
352            }
353            break;
354
355        case EVENT_SEND_CONFIRMED_SMS:
356            if (mSTrackers.isEmpty() == false) {
357                SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
358                if (isMultipartTracker(sTracker)) {
359                    sendMultipartSms(sTracker);
360                } else {
361                    sendSms(sTracker);
362                }
363                removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
364            }
365            break;
366
367        case EVENT_STOP_SENDING:
368            if (mSTrackers.isEmpty() == false) {
369                // Remove the latest one.
370                try {
371                    SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
372                    sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
373                } catch (CanceledException ex) {
374                    Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
375                }
376                removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
377            }
378            break;
379
380        case EVENT_REPORT_MEMORY_STATUS_DONE:
381            ar = (AsyncResult)msg.obj;
382            if (ar.exception != null) {
383                mReportMemoryStatusPending = true;
384                Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
385                        + mStorageAvailable);
386            } else {
387                mReportMemoryStatusPending = false;
388            }
389            break;
390
391        case EVENT_RADIO_ON:
392            if (mReportMemoryStatusPending) {
393                Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
394                        + mStorageAvailable);
395                mCm.reportSmsMemoryStatus(mStorageAvailable,
396                        obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
397            }
398            break;
399
400        case EVENT_NEW_BROADCAST_SMS:
401            handleBroadcastSms((AsyncResult)msg.obj);
402            break;
403        }
404    }
405
406    private void createWakelock() {
407        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
408        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher");
409        mWakeLock.setReferenceCounted(true);
410    }
411
412    /**
413     * Grabs a wake lock and sends intent as an ordered broadcast.
414     * The resultReceiver will check for errors and ACK/NACK back
415     * to the RIL.
416     *
417     * @param intent intent to broadcast
418     * @param permission Receivers are required to have this permission
419     */
420    void dispatch(Intent intent, String permission) {
421        // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any
422        // receivers time to take their own wake locks.
423        mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
424        mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,
425                this, Activity.RESULT_OK, null, null);
426    }
427
428    /**
429     * Called when SIM_FULL message is received from the RIL.  Notifies interested
430     * parties that SIM storage for SMS messages is full.
431     */
432    private void handleIccFull(){
433        // broadcast SIM_FULL intent
434        Intent intent = new Intent(Intents.SIM_FULL_ACTION);
435        mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
436        mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
437    }
438
439    /**
440     * Called when a status report is received.  This should correspond to
441     * a previously successful SEND.
442     *
443     * @param ar AsyncResult passed into the message handler.  ar.result should
444     *           be a String representing the status report PDU, as ASCII hex.
445     */
446    protected abstract void handleStatusReport(AsyncResult ar);
447
448    /**
449     * Called when SMS send completes. Broadcasts a sentIntent on success.
450     * On failure, either sets up retries or broadcasts a sentIntent with
451     * the failure in the result code.
452     *
453     * @param ar AsyncResult passed into the message handler.  ar.result should
454     *           an SmsResponse instance if send was successful.  ar.userObj
455     *           should be an SmsTracker instance.
456     */
457    protected void handleSendComplete(AsyncResult ar) {
458        SmsTracker tracker = (SmsTracker) ar.userObj;
459        PendingIntent sentIntent = tracker.mSentIntent;
460
461        if (ar.exception == null) {
462            if (Config.LOGD) {
463                Log.d(TAG, "SMS send complete. Broadcasting "
464                        + "intent: " + sentIntent);
465            }
466
467            if (tracker.mDeliveryIntent != null) {
468                // Expecting a status report.  Add it to the list.
469                int messageRef = ((SmsResponse)ar.result).messageRef;
470                tracker.mMessageRef = messageRef;
471                deliveryPendingList.add(tracker);
472            }
473
474            if (sentIntent != null) {
475                try {
476                    if (mRemainingMessages > -1) {
477                        mRemainingMessages--;
478                    }
479
480                    if (mRemainingMessages == 0) {
481                        Intent sendNext = new Intent();
482                        sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
483                        sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
484                    } else {
485                        sentIntent.send(Activity.RESULT_OK);
486                    }
487                } catch (CanceledException ex) {}
488            }
489        } else {
490            if (Config.LOGD) {
491                Log.d(TAG, "SMS send failed");
492            }
493
494            int ss = mPhone.getServiceState().getState();
495
496            if (ss != ServiceState.STATE_IN_SERVICE) {
497                handleNotInService(ss, tracker);
498            } else if ((((CommandException)(ar.exception)).getCommandError()
499                    == CommandException.Error.SMS_FAIL_RETRY) &&
500                   tracker.mRetryCount < MAX_SEND_RETRIES) {
501                // Retry after a delay if needed.
502                // TODO: According to TS 23.040, 9.2.3.6, we should resend
503                //       with the same TP-MR as the failed message, and
504                //       TP-RD set to 1.  However, we don't have a means of
505                //       knowing the MR for the failed message (EF_SMSstatus
506                //       may or may not have the MR corresponding to this
507                //       message, depending on the failure).  Also, in some
508                //       implementations this retry is handled by the baseband.
509                tracker.mRetryCount++;
510                Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
511                sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
512            } else if (tracker.mSentIntent != null) {
513                int error = RESULT_ERROR_GENERIC_FAILURE;
514
515                if (((CommandException)(ar.exception)).getCommandError()
516                        == CommandException.Error.FDN_CHECK_FAILURE) {
517                    error = RESULT_ERROR_FDN_CHECK_FAILURE;
518                }
519                // Done retrying; return an error to the app.
520                try {
521                    Intent fillIn = new Intent();
522                    if (ar.result != null) {
523                        fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
524                    }
525                    if (mRemainingMessages > -1) {
526                        mRemainingMessages--;
527                    }
528
529                    if (mRemainingMessages == 0) {
530                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
531                    }
532
533                    tracker.mSentIntent.send(mContext, error, fillIn);
534                } catch (CanceledException ex) {}
535            }
536        }
537    }
538
539    /**
540     * Handles outbound message when the phone is not in service.
541     *
542     * @param ss     Current service state.  Valid values are:
543     *                  OUT_OF_SERVICE
544     *                  EMERGENCY_ONLY
545     *                  POWER_OFF
546     * @param tracker   An SmsTracker for the current message.
547     */
548    protected void handleNotInService(int ss, SmsTracker tracker) {
549        if (tracker.mSentIntent != null) {
550            try {
551                if (ss == ServiceState.STATE_POWER_OFF) {
552                    tracker.mSentIntent.send(RESULT_ERROR_RADIO_OFF);
553                } else {
554                    tracker.mSentIntent.send(RESULT_ERROR_NO_SERVICE);
555                }
556            } catch (CanceledException ex) {}
557        }
558    }
559
560    /**
561     * Dispatches an incoming SMS messages.
562     *
563     * @param sms the incoming message from the phone
564     * @return a result code from {@link Telephony.Sms.Intents}, or
565     *         {@link Activity#RESULT_OK} if the message has been broadcast
566     *         to applications
567     */
568    protected abstract int dispatchMessage(SmsMessageBase sms);
569
570
571    /**
572     * If this is the last part send the parts out to the application, otherwise
573     * the part is stored for later processing.
574     *
575     * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
576     * @return a result code from {@link Telephony.Sms.Intents}, or
577     *         {@link Activity#RESULT_OK} if the message has been broadcast
578     *         to applications
579     */
580    protected int processMessagePart(SmsMessageBase sms,
581            SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
582
583        // Lookup all other related parts
584        StringBuilder where = new StringBuilder("reference_number =");
585        where.append(concatRef.refNumber);
586        where.append(" AND address = ?");
587        String[] whereArgs = new String[] {sms.getOriginatingAddress()};
588
589        byte[][] pdus = null;
590        Cursor cursor = null;
591        try {
592            cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null);
593            int cursorCount = cursor.getCount();
594            if (cursorCount != concatRef.msgCount - 1) {
595                // We don't have all the parts yet, store this one away
596                ContentValues values = new ContentValues();
597                values.put("date", new Long(sms.getTimestampMillis()));
598                values.put("pdu", HexDump.toHexString(sms.getPdu()));
599                values.put("address", sms.getOriginatingAddress());
600                values.put("reference_number", concatRef.refNumber);
601                values.put("count", concatRef.msgCount);
602                values.put("sequence", concatRef.seqNumber);
603                if (portAddrs != null) {
604                    values.put("destination_port", portAddrs.destPort);
605                }
606                mResolver.insert(mRawUri, values);
607                return Intents.RESULT_SMS_HANDLED;
608            }
609
610            // All the parts are in place, deal with them
611            int pduColumn = cursor.getColumnIndex("pdu");
612            int sequenceColumn = cursor.getColumnIndex("sequence");
613
614            pdus = new byte[concatRef.msgCount][];
615            for (int i = 0; i < cursorCount; i++) {
616                cursor.moveToNext();
617                int cursorSequence = (int)cursor.getLong(sequenceColumn);
618                pdus[cursorSequence - 1] = HexDump.hexStringToByteArray(
619                        cursor.getString(pduColumn));
620            }
621            // This one isn't in the DB, so add it
622            pdus[concatRef.seqNumber - 1] = sms.getPdu();
623
624            // Remove the parts from the database
625            mResolver.delete(mRawUri, where.toString(), whereArgs);
626        } catch (SQLException e) {
627            Log.e(TAG, "Can't access multipart SMS database", e);
628            // TODO:  Would OUT_OF_MEMORY be more appropriate?
629            return Intents.RESULT_SMS_GENERIC_ERROR;
630        } finally {
631            if (cursor != null) cursor.close();
632        }
633
634        /**
635         * TODO(cleanup): The following code has duplicated logic with
636         * the radio-specific dispatchMessage code, which is fragile,
637         * in addition to being redundant.  Instead, if this method
638         * maybe returned the reassembled message (or just contents),
639         * the following code (which is not really related to
640         * reconstruction) could be better consolidated.
641         */
642
643        // Dispatch the PDUs to applications
644        if (portAddrs != null) {
645            if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
646                // Build up the data stream
647                ByteArrayOutputStream output = new ByteArrayOutputStream();
648                for (int i = 0; i < concatRef.msgCount; i++) {
649                    SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
650                    byte[] data = msg.getUserData();
651                    output.write(data, 0, data.length);
652                }
653                // Handle the PUSH
654                return mWapPush.dispatchWapPdu(output.toByteArray());
655            } else {
656                // The messages were sent to a port, so concoct a URI for it
657                dispatchPortAddressedPdus(pdus, portAddrs.destPort);
658            }
659        } else {
660            // The messages were not sent to a port
661            dispatchPdus(pdus);
662        }
663        return Activity.RESULT_OK;
664    }
665
666    /**
667     * Dispatches standard PDUs to interested applications
668     *
669     * @param pdus The raw PDUs making up the message
670     */
671    protected void dispatchPdus(byte[][] pdus) {
672        Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
673        intent.putExtra("pdus", pdus);
674        dispatch(intent, "android.permission.RECEIVE_SMS");
675    }
676
677    /**
678     * Dispatches port addressed PDUs to interested applications
679     *
680     * @param pdus The raw PDUs making up the message
681     * @param port The destination port of the messages
682     */
683    protected void dispatchPortAddressedPdus(byte[][] pdus, int port) {
684        Uri uri = Uri.parse("sms://localhost:" + port);
685        Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
686        intent.putExtra("pdus", pdus);
687        dispatch(intent, "android.permission.RECEIVE_SMS");
688    }
689
690    /**
691     * Send a data based SMS to a specific application port.
692     *
693     * @param destAddr the address to send the message to
694     * @param scAddr is the service center address or null to use
695     *  the current default SMSC
696     * @param destPort the port to deliver the message to
697     * @param data the body of the message to send
698     * @param sentIntent if not NULL this <code>PendingIntent</code> is
699     *  broadcast when the message is successfully sent, or failed.
700     *  The result code will be <code>Activity.RESULT_OK<code> for success,
701     *  or one of these errors:<br>
702     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
703     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
704     *  <code>RESULT_ERROR_NULL_PDU</code><br>
705     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
706     *  the extra "errorCode" containing a radio technology specific value,
707     *  generally only useful for troubleshooting.<br>
708     *  The per-application based SMS control checks sentIntent. If sentIntent
709     *  is NULL the caller will be checked against all unknown applications,
710     *  which cause smaller number of SMS to be sent in checking period.
711     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
712     *  broadcast when the message is delivered to the recipient.  The
713     *  raw pdu of the status report is in the extended data ("pdu").
714     */
715    protected abstract void sendData(String destAddr, String scAddr, int destPort,
716            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
717
718    /**
719     * Send a text based SMS.
720     *
721     * @param destAddr the address to send the message to
722     * @param scAddr is the service center address or null to use
723     *  the current default SMSC
724     * @param text the body of the message to send
725     * @param sentIntent if not NULL this <code>PendingIntent</code> is
726     *  broadcast when the message is successfully sent, or failed.
727     *  The result code will be <code>Activity.RESULT_OK<code> for success,
728     *  or one of these errors:<br>
729     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
730     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
731     *  <code>RESULT_ERROR_NULL_PDU</code><br>
732     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
733     *  the extra "errorCode" containing a radio technology specific value,
734     *  generally only useful for troubleshooting.<br>
735     *  The per-application based SMS control checks sentIntent. If sentIntent
736     *  is NULL the caller will be checked against all unknown applications,
737     *  which cause smaller number of SMS to be sent in checking period.
738     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
739     *  broadcast when the message is delivered to the recipient.  The
740     *  raw pdu of the status report is in the extended data ("pdu").
741     */
742    protected abstract void sendText(String destAddr, String scAddr,
743            String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
744
745    /**
746     * Send a multi-part text based SMS.
747     *
748     * @param destAddr the address to send the message to
749     * @param scAddr is the service center address or null to use
750     *   the current default SMSC
751     * @param parts an <code>ArrayList</code> of strings that, in order,
752     *   comprise the original message
753     * @param sentIntents if not null, an <code>ArrayList</code> of
754     *   <code>PendingIntent</code>s (one for each message part) that is
755     *   broadcast when the corresponding message part has been sent.
756     *   The result code will be <code>Activity.RESULT_OK<code> for success,
757     *   or one of these errors:
758     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
759     *   <code>RESULT_ERROR_RADIO_OFF</code>
760     *   <code>RESULT_ERROR_NULL_PDU</code>.
761     *  The per-application based SMS control checks sentIntent. If sentIntent
762     *  is NULL the caller will be checked against all unknown applications,
763     *  which cause smaller number of SMS to be sent in checking period.
764     * @param deliveryIntents if not null, an <code>ArrayList</code> of
765     *   <code>PendingIntent</code>s (one for each message part) that is
766     *   broadcast when the corresponding message part has been delivered
767     *   to the recipient.  The raw pdu of the status report is in the
768     *   extended data ("pdu").
769     */
770    protected abstract void sendMultipartText(String destAddr, String scAddr,
771            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
772            ArrayList<PendingIntent> deliveryIntents);
773
774    /**
775     * Send a SMS
776     *
777     * @param smsc the SMSC to send the message through, or NULL for the
778     *  default SMSC
779     * @param pdu the raw PDU to send
780     * @param sentIntent if not NULL this <code>Intent</code> is
781     *  broadcast when the message is successfully sent, or failed.
782     *  The result code will be <code>Activity.RESULT_OK<code> for success,
783     *  or one of these errors:
784     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
785     *  <code>RESULT_ERROR_RADIO_OFF</code>
786     *  <code>RESULT_ERROR_NULL_PDU</code>.
787     *  The per-application based SMS control checks sentIntent. If sentIntent
788     *  is NULL the caller will be checked against all unknown applications,
789     *  which cause smaller number of SMS to be sent in checking period.
790     * @param deliveryIntent if not NULL this <code>Intent</code> is
791     *  broadcast when the message is delivered to the recipient.  The
792     *  raw pdu of the status report is in the extended data ("pdu").
793     */
794    protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
795            PendingIntent deliveryIntent) {
796        if (pdu == null) {
797            if (sentIntent != null) {
798                try {
799                    sentIntent.send(RESULT_ERROR_NULL_PDU);
800                } catch (CanceledException ex) {}
801            }
802            return;
803        }
804
805        HashMap<String, Object> map = new HashMap<String, Object>();
806        map.put("smsc", smsc);
807        map.put("pdu", pdu);
808
809        SmsTracker tracker = new SmsTracker(map, sentIntent,
810                deliveryIntent);
811        int ss = mPhone.getServiceState().getState();
812
813        if (ss != ServiceState.STATE_IN_SERVICE) {
814            handleNotInService(ss, tracker);
815        } else {
816            String appName = getAppNameByIntent(sentIntent);
817            if (mCounter.check(appName, SINGLE_PART_SMS)) {
818                sendSms(tracker);
819            } else {
820                sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
821            }
822        }
823    }
824
825    /**
826     * Post an alert while SMS needs user confirm.
827     *
828     * An SmsTracker for the current message.
829     */
830    protected void handleReachSentLimit(SmsTracker tracker) {
831        if (mSTrackers.size() >= MO_MSG_QUEUE_LIMIT) {
832            // Deny the sending when the queue limit is reached.
833            try {
834                tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
835            } catch (CanceledException ex) {
836                Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
837            }
838            return;
839        }
840
841        Resources r = Resources.getSystem();
842
843        String appName = getAppNameByIntent(tracker.mSentIntent);
844
845        AlertDialog d = new AlertDialog.Builder(mContext)
846                .setTitle(r.getString(R.string.sms_control_title))
847                .setMessage(appName + " " + r.getString(R.string.sms_control_message))
848                .setPositiveButton(r.getString(R.string.sms_control_yes), mListener)
849                .setNegativeButton(r.getString(R.string.sms_control_no), mListener)
850                .create();
851
852        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
853        d.show();
854
855        mSTrackers.add(tracker);
856        sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
857                DEFAULT_SMS_TIMEOUT);
858    }
859
860    protected String getAppNameByIntent(PendingIntent intent) {
861        Resources r = Resources.getSystem();
862        return (intent != null) ? intent.getTargetPackage()
863            : r.getString(R.string.sms_control_default_app_name);
864    }
865
866    /**
867     * Send the message along to the radio.
868     *
869     * @param tracker holds the SMS message to send
870     */
871    protected abstract void sendSms(SmsTracker tracker);
872
873    /**
874     * Send the multi-part SMS based on multipart Sms tracker
875     *
876     * @param tracker holds the multipart Sms tracker ready to be sent
877     */
878    protected abstract void sendMultipartSms (SmsTracker tracker);
879
880    /**
881     * Activate or deactivate cell broadcast SMS.
882     *
883     * @param activate
884     *            0 = activate, 1 = deactivate
885     * @param response
886     *            Callback message is empty on completion
887     */
888    protected abstract void activateCellBroadcastSms(int activate, Message response);
889
890    /**
891     * Query the current configuration of cell broadcast SMS.
892     *
893     * @param response
894     *            Callback message contains the configuration from the modem on completion
895     *            @see #setCellBroadcastConfig
896     */
897    protected abstract void getCellBroadcastSmsConfig(Message response);
898
899    /**
900     * Configure cell broadcast SMS.
901     *
902     * @param configValuesArray
903     *          The first element defines the number of triples that follow.
904     *          A triple is made up of the service category, the language identifier
905     *          and a boolean that specifies whether the category is set active.
906     * @param response
907     *            Callback message is empty on completion
908     */
909    protected abstract void setCellBroadcastConfig(int[] configValuesArray, Message response);
910
911    /**
912     * Send an acknowledge message.
913     * @param success indicates that last message was successfully received.
914     * @param result result code indicating any error
915     * @param response callback message sent when operation completes.
916     */
917    protected abstract void acknowledgeLastIncomingSms(boolean success,
918            int result, Message response);
919
920    /**
921     * Notify interested apps if the framework has rejected an incoming SMS,
922     * and send an acknowledge message to the network.
923     * @param success indicates that last message was successfully received.
924     * @param result result code indicating any error
925     * @param response callback message sent when operation completes.
926     */
927    private void notifyAndAcknowledgeLastIncomingSms(boolean success,
928            int result, Message response) {
929        if (!success) {
930            // broadcast SMS_REJECTED_ACTION intent
931            Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
932            intent.putExtra("result", result);
933            mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
934            mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
935        }
936        acknowledgeLastIncomingSms(success, result, response);
937    }
938
939    /**
940     * Check if a SmsTracker holds multi-part Sms
941     *
942     * @param tracker a SmsTracker could hold a multi-part Sms
943     * @return true for tracker holds Multi-parts Sms
944     */
945    private boolean isMultipartTracker (SmsTracker tracker) {
946        HashMap map = tracker.mData;
947        return ( map.get("parts") != null);
948    }
949
950    /**
951     * Keeps track of an SMS that has been sent to the RIL, until it has
952     * successfully been sent, or we're done trying.
953     *
954     */
955    static protected class SmsTracker {
956        // fields need to be public for derived SmsDispatchers
957        public HashMap mData;
958        public int mRetryCount;
959        public int mMessageRef;
960
961        public PendingIntent mSentIntent;
962        public PendingIntent mDeliveryIntent;
963
964        SmsTracker(HashMap data, PendingIntent sentIntent,
965                PendingIntent deliveryIntent) {
966            mData = data;
967            mSentIntent = sentIntent;
968            mDeliveryIntent = deliveryIntent;
969            mRetryCount = 0;
970        }
971    }
972
973    protected SmsTracker SmsTrackerFactory(HashMap data, PendingIntent sentIntent,
974            PendingIntent deliveryIntent) {
975        return new SmsTracker(data, sentIntent, deliveryIntent);
976    }
977
978    private DialogInterface.OnClickListener mListener =
979        new DialogInterface.OnClickListener() {
980
981            public void onClick(DialogInterface dialog, int which) {
982                if (which == DialogInterface.BUTTON_POSITIVE) {
983                    Log.d(TAG, "click YES to send out sms");
984                    sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS));
985                } else if (which == DialogInterface.BUTTON_NEGATIVE) {
986                    Log.d(TAG, "click NO to stop sending");
987                    sendMessage(obtainMessage(EVENT_STOP_SENDING));
988                }
989            }
990        };
991
992    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
993        @Override
994        public void onReceive(Context context, Intent intent) {
995            if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
996                mStorageAvailable = false;
997                mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
998            } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
999                mStorageAvailable = true;
1000                mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
1001            } else {
1002                // Assume the intent is one of the SMS receive intents that
1003                // was sent as an ordered broadcast.  Check result and ACK.
1004                int rc = getResultCode();
1005                boolean success = (rc == Activity.RESULT_OK)
1006                        || (rc == Intents.RESULT_SMS_HANDLED);
1007
1008                // For a multi-part message, this only ACKs the last part.
1009                // Previous parts were ACK'd as they were received.
1010                acknowledgeLastIncomingSms(success, rc, null);
1011            }
1012        }
1013    };
1014
1015    protected abstract void handleBroadcastSms(AsyncResult ar);
1016
1017    protected void dispatchBroadcastPdus(byte[][] pdus) {
1018        Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
1019        intent.putExtra("pdus", pdus);
1020
1021        if (Config.LOGD)
1022            Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
1023
1024        dispatch(intent, "android.permission.RECEIVE_SMS");
1025    }
1026
1027}
1028