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