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;
268f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangimport android.os.UserHandle;
27a83613955a113b805729cd415aca50707529208aAmit Mahajanimport android.os.UserManager;
280d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.telephony.Rlog;
290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
300d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
310d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.gsm.GsmInboundSmsHandler;
320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
330d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashMap;
340d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashSet;
350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby/**
378f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * Called when the credential-encrypted storage is unlocked, collecting all acknowledged messages
388f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * and deleting any partial message segments older than 30 days. Called from a worker thread to
398f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * avoid delaying phone app startup. The last step is to broadcast the first pending message from
408f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * the main thread, then the remaining pending messages will be broadcast after the previous
418f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * ordered broadcast completes.
420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */
438f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangpublic class SmsBroadcastUndelivered {
440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final String TAG = "SmsBroadcastUndelivered";
450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final boolean DBG = InboundSmsHandler.DBG;
460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Delete any partial message segments older than 30 days. */
480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    static final long PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Query projection for dispatching pending messages at boot time.
520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Column order must match the {@code *_COLUMN} constants in {@link InboundSmsHandler}.
530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final String[] PDU_PENDING_MESSAGE_PROJECTION = {
550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "pdu",
560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "sequence",
570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "destination_port",
580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "date",
590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "reference_number",
600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "count",
610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "address",
626e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            "_id",
63af7bc32573007f2ea1185fc5cb4f3980e1ff1304fionaxu            "message_body",
64af7bc32573007f2ea1185fc5cb4f3980e1ff1304fionaxu            "display_originating_addr"
650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    };
660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
678f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private static SmsBroadcastUndelivered instance;
680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Content resolver to use to access raw table from SmsProvider. */
700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final ContentResolver mResolver;
710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP-format messages (may be null). */
730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final GsmInboundSmsHandler mGsmInboundSmsHandler;
740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP2-format messages (may be null). */
760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final CdmaInboundSmsHandler mCdmaInboundSmsHandler;
770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
788f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    /** Broadcast receiver that processes the raw table when the user unlocks the phone for the
798f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang     *  first time after reboot and the credential-encrypted storage is available.
808f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang     */
818f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
828f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        @Override
838a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        public void onReceive(final Context context, Intent intent) {
848f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            Rlog.d(TAG, "Received broadcast " + intent.getAction());
858f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
868a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang                new ScanRawTableThread(context).start();
878f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            }
888f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        }
898f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    };
908f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang
91a83613955a113b805729cd415aca50707529208aAmit Mahajan    private class ScanRawTableThread extends Thread {
928a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        private final Context context;
938a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang
948a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        private ScanRawTableThread(Context context) {
958a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang            this.context = context;
968a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        }
978a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang
98a83613955a113b805729cd415aca50707529208aAmit Mahajan        @Override
99a83613955a113b805729cd415aca50707529208aAmit Mahajan        public void run() {
100a83613955a113b805729cd415aca50707529208aAmit Mahajan            scanRawTable();
1018a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang            InboundSmsHandler.cancelNewMessageNotification(context);
102a83613955a113b805729cd415aca50707529208aAmit Mahajan        }
103a83613955a113b805729cd415aca50707529208aAmit Mahajan    }
104a83613955a113b805729cd415aca50707529208aAmit Mahajan
1058f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    public static void initialize(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
1068f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        CdmaInboundSmsHandler cdmaInboundSmsHandler) {
1078f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        if (instance == null) {
1088f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            instance = new SmsBroadcastUndelivered(
1098f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang                context, gsmInboundSmsHandler, cdmaInboundSmsHandler);
1108f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        }
111dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang
112dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // Tell handlers to start processing new messages and transit from the startup state to the
113dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // idle state. This method may be called multiple times for multi-sim devices. We must make
114dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // sure the state transition happen to all inbound sms handlers.
115dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        if (gsmInboundSmsHandler != null) {
116dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang            gsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
117dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        }
118dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        if (cdmaInboundSmsHandler != null) {
119dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang            cdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
120dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        }
1218f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    }
1228f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang
1238f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private SmsBroadcastUndelivered(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
1240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            CdmaInboundSmsHandler cdmaInboundSmsHandler) {
1250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mResolver = context.getContentResolver();
1260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mGsmInboundSmsHandler = gsmInboundSmsHandler;
1270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mCdmaInboundSmsHandler = cdmaInboundSmsHandler;
1280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
129a83613955a113b805729cd415aca50707529208aAmit Mahajan        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
130a83613955a113b805729cd415aca50707529208aAmit Mahajan
131a83613955a113b805729cd415aca50707529208aAmit Mahajan        if (userManager.isUserUnlocked()) {
1328a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang            new ScanRawTableThread(context).start();
133a83613955a113b805729cd415aca50707529208aAmit Mahajan        } else {
134a83613955a113b805729cd415aca50707529208aAmit Mahajan            IntentFilter userFilter = new IntentFilter();
135a83613955a113b805729cd415aca50707529208aAmit Mahajan            userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
1369b813aa455186511edd03bb738334a1acca7e7b3Amit Mahajan            context.registerReceiver(mBroadcastReceiver, userFilter);
137a83613955a113b805729cd415aca50707529208aAmit Mahajan        }
1380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
1390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
1410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
1420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
1430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void scanRawTable() {
1448f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
1450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        long startTime = System.nanoTime();
1460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
1470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                new HashMap<SmsReferenceKey, Integer>(4);
1480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashSet<SmsReferenceKey> oldMultiPartMessages = new HashSet<SmsReferenceKey>(4);
1490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        Cursor cursor = null;
1500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        try {
1516e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            // query only non-deleted ones
1526e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            cursor = mResolver.query(InboundSmsHandler.sRawUri, PDU_PENDING_MESSAGE_PROJECTION,
1536e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                    "deleted = 0", null,
1546e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                    null);
1550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor == null) {
1560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                Rlog.e(TAG, "error getting pending message cursor");
1570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return;
1580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            boolean isCurrentFormat3gpp2 = InboundSmsHandler.isCurrentFormat3gpp2();
1610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            while (cursor.moveToNext()) {
1620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                InboundSmsTracker tracker;
1630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                try {
164871a2ec0bc8e8198aeb1913e7bf6cb22a7ff3604Amit Mahajan                    tracker = TelephonyComponentFactory.getInstance().makeInboundSmsTracker(cursor,
165871a2ec0bc8e8198aeb1913e7bf6cb22a7ff3604Amit Mahajan                            isCurrentFormat3gpp2);
1660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } catch (IllegalArgumentException e) {
1670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "error loading SmsTracker: " + e);
1680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    continue;
1690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (tracker.getMessageCount() == 1) {
1720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    // deliver single-part message
1730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    broadcastSms(tracker);
1740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else {
1750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    SmsReferenceKey reference = new SmsReferenceKey(tracker);
1760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer receivedCount = multiPartReceivedCount.get(reference);
1770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    if (receivedCount == null) {
1780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        multiPartReceivedCount.put(reference, 1);    // first segment seen
1790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (tracker.getTimestamp() <
1800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                                (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) {
1810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // older than 30 days; delete if we don't find all the segments
1820d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.add(reference);
1830d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1840d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    } else {
1850d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        int newCount = receivedCount + 1;
1860d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (newCount == tracker.getMessageCount()) {
1870d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // looks like we've got all the pieces; send a single tracker
1880d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // to state machine which will find the other pieces to broadcast
1890d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            if (DBG) Rlog.d(TAG, "found complete multi-part message");
1900d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            broadcastSms(tracker);
1910d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // don't delete this old message until after we broadcast it
1920d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.remove(reference);
1930d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        } else {
1940d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            multiPartReceivedCount.put(reference, newCount);
1950d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1960d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    }
1970d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1980d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1990d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            // Delete old incomplete message segments
2000d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            for (SmsReferenceKey message : oldMultiPartMessages) {
2016e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                // delete permanently
2026e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                int rows = mResolver.delete(InboundSmsHandler.sRawUriPermanentDelete,
203f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu                        message.getDeleteWhere(), message.getDeleteWhereArgs());
2040d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (rows == 0) {
2050d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "No rows were deleted from raw table!");
2060d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else if (DBG) {
2070d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.d(TAG, "Deleted " + rows + " rows from raw table for incomplete "
2080d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            + message.mMessageCount + " part message");
2090d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
2100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } catch (SQLException e) {
2120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "error reading pending SMS messages", e);
2130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } finally {
2140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor != null) {
2150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                cursor.close();
2160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (DBG) Rlog.d(TAG, "finished scanning raw table in "
2180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    + ((System.nanoTime() - startTime) / 1000000) + " ms");
2190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
2230d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
2240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
2250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void broadcastSms(InboundSmsTracker tracker) {
2260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        InboundSmsHandler handler;
2270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (tracker.is3gpp2()) {
2280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mCdmaInboundSmsHandler;
2290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
2300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mGsmInboundSmsHandler;
2310d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (handler != null) {
2330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
2340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
2350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "null handler for " + tracker.getFormat() + " format, can't deliver.");
2360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
2400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Used as the HashMap key for matching concatenated message segments.
2410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
2420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static class SmsReferenceKey {
2430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final String mAddress;
2440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mReferenceNumber;
2450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mMessageCount;
246f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu        final String mQuery;
2470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        SmsReferenceKey(InboundSmsTracker tracker) {
2490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mAddress = tracker.getAddress();
2500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mReferenceNumber = tracker.getReferenceNumber();
2510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mMessageCount = tracker.getMessageCount();
252f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu            mQuery = tracker.getQueryForSegments();
253f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu
2540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        String[] getDeleteWhereArgs() {
2570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return new String[]{mAddress, Integer.toString(mReferenceNumber),
2580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer.toString(mMessageCount)};
2590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
261f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu        String getDeleteWhere() {
262f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu            return mQuery;
263f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu        }
264f97f0535c2793c0f662a83fa6458f9d3241bd904fionaxu
2650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public int hashCode() {
2670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return ((mReferenceNumber * 31) + mMessageCount) * 31 + mAddress.hashCode();
2680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public boolean equals(Object o) {
2720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (o instanceof SmsReferenceKey) {
2730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                SmsReferenceKey other = (SmsReferenceKey) o;
2740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return other.mAddress.equals(mAddress)
2750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mReferenceNumber == mReferenceNumber)
2760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mMessageCount == mMessageCount);
2770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return false;
2790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby}
282