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