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