SmsBroadcastUndelivered.java revision a83613955a113b805729cd415aca50707529208a
10d4bcdf379842af4b6304809156971e926f374f0Jake Hamby/*
20d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Copyright (C) 2013 The Android Open Source Project
30d4bcdf379842af4b6304809156971e926f374f0Jake Hamby *
40d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Licensed under the Apache License, Version 2.0 (the "License");
50d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * you may not use this file except in compliance with the License.
60d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * You may obtain a copy of the License at
70d4bcdf379842af4b6304809156971e926f374f0Jake Hamby *
80d4bcdf379842af4b6304809156971e926f374f0Jake Hamby *      http://www.apache.org/licenses/LICENSE-2.0
90d4bcdf379842af4b6304809156971e926f374f0Jake Hamby *
100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Unless required by applicable law or agreed to in writing, software
110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * distributed under the License is distributed on an "AS IS" BASIS,
120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * See the License for the specific language governing permissions and
140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * limitations under the License.
150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */
160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
170d4bcdf379842af4b6304809156971e926f374f0Jake Hambypackage com.android.internal.telephony;
180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
198f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangimport android.content.BroadcastReceiver;
200d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.content.ContentResolver;
210d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.content.Context;
228f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangimport android.content.Intent;
238f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangimport android.content.IntentFilter;
240d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.database.Cursor;
250d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.database.SQLException;
260d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.net.Uri;
278f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangimport android.os.UserHandle;
28a83613955a113b805729cd415aca50707529208aAmit Mahajanimport android.os.UserManager;
290d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.provider.Telephony;
300d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.telephony.Rlog;
310d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
320d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
330d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.gsm.GsmInboundSmsHandler;
340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
350d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashMap;
360d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashSet;
370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby/**
398f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * Called when the credential-encrypted storage is unlocked, collecting all acknowledged messages
408f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * and deleting any partial message segments older than 30 days. Called from a worker thread to
418f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * avoid delaying phone app startup. The last step is to broadcast the first pending message from
428f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * the main thread, then the remaining pending messages will be broadcast after the previous
438f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * ordered broadcast completes.
440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */
458f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangpublic class SmsBroadcastUndelivered {
460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final String TAG = "SmsBroadcastUndelivered";
470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final boolean DBG = InboundSmsHandler.DBG;
480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Delete any partial message segments older than 30 days. */
500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    static final long PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Query projection for dispatching pending messages at boot time.
540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Column order must match the {@code *_COLUMN} constants in {@link InboundSmsHandler}.
550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final String[] PDU_PENDING_MESSAGE_PROJECTION = {
570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "pdu",
580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "sequence",
590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "destination_port",
600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "date",
610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "reference_number",
620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "count",
630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "address",
646e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            "_id",
656e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            "message_body"
660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    };
670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
688f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private static SmsBroadcastUndelivered instance;
690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Content resolver to use to access raw table from SmsProvider. */
710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final ContentResolver mResolver;
720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP-format messages (may be null). */
740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final GsmInboundSmsHandler mGsmInboundSmsHandler;
750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP2-format messages (may be null). */
770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final CdmaInboundSmsHandler mCdmaInboundSmsHandler;
780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
798f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    /** Broadcast receiver that processes the raw table when the user unlocks the phone for the
808f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang     *  first time after reboot and the credential-encrypted storage is available.
818f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang     */
828f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
838f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        @Override
848f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        public void onReceive(Context context, Intent intent) {
858f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            Rlog.d(TAG, "Received broadcast " + intent.getAction());
868f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
87a83613955a113b805729cd415aca50707529208aAmit Mahajan                new ScanRawTableThread().start();
888f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            }
898f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        }
908f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    };
918f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang
92a83613955a113b805729cd415aca50707529208aAmit Mahajan    private class ScanRawTableThread extends Thread {
93a83613955a113b805729cd415aca50707529208aAmit Mahajan        @Override
94a83613955a113b805729cd415aca50707529208aAmit Mahajan        public void run() {
95a83613955a113b805729cd415aca50707529208aAmit Mahajan            scanRawTable();
96a83613955a113b805729cd415aca50707529208aAmit Mahajan        }
97a83613955a113b805729cd415aca50707529208aAmit Mahajan    }
98a83613955a113b805729cd415aca50707529208aAmit Mahajan
998f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    public static void initialize(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
1008f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        CdmaInboundSmsHandler cdmaInboundSmsHandler) {
1018f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        if (instance == null) {
1028f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            instance = new SmsBroadcastUndelivered(
1038f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang                context, gsmInboundSmsHandler, cdmaInboundSmsHandler);
1048f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        }
105dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang
106dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // Tell handlers to start processing new messages and transit from the startup state to the
107dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // idle state. This method may be called multiple times for multi-sim devices. We must make
108dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // sure the state transition happen to all inbound sms handlers.
109dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        if (gsmInboundSmsHandler != null) {
110dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang            gsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
111dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        }
112dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        if (cdmaInboundSmsHandler != null) {
113dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang            cdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
114dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        }
1158f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    }
1168f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang
1178f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private SmsBroadcastUndelivered(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
1180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            CdmaInboundSmsHandler cdmaInboundSmsHandler) {
1190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mResolver = context.getContentResolver();
1200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mGsmInboundSmsHandler = gsmInboundSmsHandler;
1210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mCdmaInboundSmsHandler = cdmaInboundSmsHandler;
1220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
123a83613955a113b805729cd415aca50707529208aAmit Mahajan        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
124a83613955a113b805729cd415aca50707529208aAmit Mahajan
125a83613955a113b805729cd415aca50707529208aAmit Mahajan        if (userManager.isUserUnlocked()) {
126a83613955a113b805729cd415aca50707529208aAmit Mahajan            new ScanRawTableThread().start();
127a83613955a113b805729cd415aca50707529208aAmit Mahajan        } else {
128a83613955a113b805729cd415aca50707529208aAmit Mahajan            IntentFilter userFilter = new IntentFilter();
129a83613955a113b805729cd415aca50707529208aAmit Mahajan            userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
130a83613955a113b805729cd415aca50707529208aAmit Mahajan            context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null,
131a83613955a113b805729cd415aca50707529208aAmit Mahajan                    null);
132a83613955a113b805729cd415aca50707529208aAmit Mahajan        }
1330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
1340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
1360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
1370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
1380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void scanRawTable() {
1398f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
1400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        long startTime = System.nanoTime();
1410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
1420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                new HashMap<SmsReferenceKey, Integer>(4);
1430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashSet<SmsReferenceKey> oldMultiPartMessages = new HashSet<SmsReferenceKey>(4);
1440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        Cursor cursor = null;
1450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        try {
1466e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            // query only non-deleted ones
1476e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            cursor = mResolver.query(InboundSmsHandler.sRawUri, PDU_PENDING_MESSAGE_PROJECTION,
1486e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                    "deleted = 0", null,
1496e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                    null);
1500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor == null) {
1510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                Rlog.e(TAG, "error getting pending message cursor");
1520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return;
1530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            boolean isCurrentFormat3gpp2 = InboundSmsHandler.isCurrentFormat3gpp2();
1560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            while (cursor.moveToNext()) {
1570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                InboundSmsTracker tracker;
1580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                try {
159871a2ec0bc8e8198aeb1913e7bf6cb22a7ff3604Amit Mahajan                    tracker = TelephonyComponentFactory.getInstance().makeInboundSmsTracker(cursor,
160871a2ec0bc8e8198aeb1913e7bf6cb22a7ff3604Amit Mahajan                            isCurrentFormat3gpp2);
1610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } catch (IllegalArgumentException e) {
1620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "error loading SmsTracker: " + e);
1630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    continue;
1640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (tracker.getMessageCount() == 1) {
1670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    // deliver single-part message
1680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    broadcastSms(tracker);
1690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else {
1700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    SmsReferenceKey reference = new SmsReferenceKey(tracker);
1710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer receivedCount = multiPartReceivedCount.get(reference);
1720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    if (receivedCount == null) {
1730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        multiPartReceivedCount.put(reference, 1);    // first segment seen
1740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (tracker.getTimestamp() <
1750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                                (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) {
1760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // older than 30 days; delete if we don't find all the segments
1770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.add(reference);
1780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    } else {
1800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        int newCount = receivedCount + 1;
1810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (newCount == tracker.getMessageCount()) {
1820d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // looks like we've got all the pieces; send a single tracker
1830d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // to state machine which will find the other pieces to broadcast
1840d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            if (DBG) Rlog.d(TAG, "found complete multi-part message");
1850d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            broadcastSms(tracker);
1860d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // don't delete this old message until after we broadcast it
1870d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.remove(reference);
1880d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        } else {
1890d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            multiPartReceivedCount.put(reference, newCount);
1900d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1910d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    }
1920d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1930d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1940d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            // Delete old incomplete message segments
1950d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            for (SmsReferenceKey message : oldMultiPartMessages) {
1966e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                // delete permanently
1976e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                int rows = mResolver.delete(InboundSmsHandler.sRawUriPermanentDelete,
1986e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                        InboundSmsHandler.SELECT_BY_REFERENCE, message.getDeleteWhereArgs());
1990d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (rows == 0) {
2000d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "No rows were deleted from raw table!");
2010d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else if (DBG) {
2020d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.d(TAG, "Deleted " + rows + " rows from raw table for incomplete "
2030d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            + message.mMessageCount + " part message");
2040d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
2050d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2060d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } catch (SQLException e) {
2070d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "error reading pending SMS messages", e);
2080d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } finally {
2090d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor != null) {
2100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                cursor.close();
2110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (DBG) Rlog.d(TAG, "finished scanning raw table in "
2130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    + ((System.nanoTime() - startTime) / 1000000) + " ms");
2140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
2180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
2190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
2200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void broadcastSms(InboundSmsTracker tracker) {
2210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        InboundSmsHandler handler;
2220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (tracker.is3gpp2()) {
2230d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mCdmaInboundSmsHandler;
2240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
2250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mGsmInboundSmsHandler;
2260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (handler != null) {
2280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
2290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
2300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "null handler for " + tracker.getFormat() + " format, can't deliver.");
2310d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
2350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Used as the HashMap key for matching concatenated message segments.
2360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
2370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static class SmsReferenceKey {
2380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final String mAddress;
2390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mReferenceNumber;
2400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mMessageCount;
2410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        SmsReferenceKey(InboundSmsTracker tracker) {
2430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mAddress = tracker.getAddress();
2440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mReferenceNumber = tracker.getReferenceNumber();
2450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mMessageCount = tracker.getMessageCount();
2460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        String[] getDeleteWhereArgs() {
2490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return new String[]{mAddress, Integer.toString(mReferenceNumber),
2500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer.toString(mMessageCount)};
2510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public int hashCode() {
2550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return ((mReferenceNumber * 31) + mMessageCount) * 31 + mAddress.hashCode();
2560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public boolean equals(Object o) {
2600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (o instanceof SmsReferenceKey) {
2610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                SmsReferenceKey other = (SmsReferenceKey) o;
2620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return other.mAddress.equals(mAddress)
2630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mReferenceNumber == mReferenceNumber)
2640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mMessageCount == mMessageCount);
2650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return false;
2670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby}
270