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