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