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",
636e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            "message_body"
640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    };
650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
668f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private static SmsBroadcastUndelivered instance;
670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Content resolver to use to access raw table from SmsProvider. */
690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final ContentResolver mResolver;
700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP-format messages (may be null). */
720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final GsmInboundSmsHandler mGsmInboundSmsHandler;
730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP2-format messages (may be null). */
750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final CdmaInboundSmsHandler mCdmaInboundSmsHandler;
760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
778f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    /** Broadcast receiver that processes the raw table when the user unlocks the phone for the
788f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang     *  first time after reboot and the credential-encrypted storage is available.
798f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang     */
808f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
818f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        @Override
828a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        public void onReceive(final Context context, Intent intent) {
838f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            Rlog.d(TAG, "Received broadcast " + intent.getAction());
848f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
858a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang                new ScanRawTableThread(context).start();
868f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            }
878f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        }
888f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    };
898f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang
90a83613955a113b805729cd415aca50707529208aAmit Mahajan    private class ScanRawTableThread extends Thread {
918a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        private final Context context;
928a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang
938a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        private ScanRawTableThread(Context context) {
948a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang            this.context = context;
958a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang        }
968a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang
97a83613955a113b805729cd415aca50707529208aAmit Mahajan        @Override
98a83613955a113b805729cd415aca50707529208aAmit Mahajan        public void run() {
99a83613955a113b805729cd415aca50707529208aAmit Mahajan            scanRawTable();
1008a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang            InboundSmsHandler.cancelNewMessageNotification(context);
101a83613955a113b805729cd415aca50707529208aAmit Mahajan        }
102a83613955a113b805729cd415aca50707529208aAmit Mahajan    }
103a83613955a113b805729cd415aca50707529208aAmit Mahajan
1048f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    public static void initialize(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
1058f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        CdmaInboundSmsHandler cdmaInboundSmsHandler) {
1068f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        if (instance == null) {
1078f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang            instance = new SmsBroadcastUndelivered(
1088f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang                context, gsmInboundSmsHandler, cdmaInboundSmsHandler);
1098f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        }
110dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang
111dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // Tell handlers to start processing new messages and transit from the startup state to the
112dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // idle state. This method may be called multiple times for multi-sim devices. We must make
113dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        // sure the state transition happen to all inbound sms handlers.
114dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        if (gsmInboundSmsHandler != null) {
115dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang            gsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
116dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        }
117dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        if (cdmaInboundSmsHandler != null) {
118dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang            cdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
119dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang        }
1208f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    }
1218f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang
1228f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang    private SmsBroadcastUndelivered(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
1230d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            CdmaInboundSmsHandler cdmaInboundSmsHandler) {
1240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mResolver = context.getContentResolver();
1250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mGsmInboundSmsHandler = gsmInboundSmsHandler;
1260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mCdmaInboundSmsHandler = cdmaInboundSmsHandler;
1270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
128a83613955a113b805729cd415aca50707529208aAmit Mahajan        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
129a83613955a113b805729cd415aca50707529208aAmit Mahajan
130a83613955a113b805729cd415aca50707529208aAmit Mahajan        if (userManager.isUserUnlocked()) {
1318a54cdb7fdac1b325213a775939ccfc50dc65425Ji Yang            new ScanRawTableThread(context).start();
132a83613955a113b805729cd415aca50707529208aAmit Mahajan        } else {
133a83613955a113b805729cd415aca50707529208aAmit Mahajan            IntentFilter userFilter = new IntentFilter();
134a83613955a113b805729cd415aca50707529208aAmit Mahajan            userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
1359b813aa455186511edd03bb738334a1acca7e7b3Amit Mahajan            context.registerReceiver(mBroadcastReceiver, userFilter);
136a83613955a113b805729cd415aca50707529208aAmit Mahajan        }
1370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
1380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
1400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
1410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
1420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void scanRawTable() {
1438f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang        if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
1440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        long startTime = System.nanoTime();
1450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
1460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                new HashMap<SmsReferenceKey, Integer>(4);
1470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashSet<SmsReferenceKey> oldMultiPartMessages = new HashSet<SmsReferenceKey>(4);
1480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        Cursor cursor = null;
1490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        try {
1506e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            // query only non-deleted ones
1516e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan            cursor = mResolver.query(InboundSmsHandler.sRawUri, PDU_PENDING_MESSAGE_PROJECTION,
1526e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                    "deleted = 0", null,
1536e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                    null);
1540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor == null) {
1550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                Rlog.e(TAG, "error getting pending message cursor");
1560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return;
1570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            boolean isCurrentFormat3gpp2 = InboundSmsHandler.isCurrentFormat3gpp2();
1600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            while (cursor.moveToNext()) {
1610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                InboundSmsTracker tracker;
1620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                try {
163871a2ec0bc8e8198aeb1913e7bf6cb22a7ff3604Amit Mahajan                    tracker = TelephonyComponentFactory.getInstance().makeInboundSmsTracker(cursor,
164871a2ec0bc8e8198aeb1913e7bf6cb22a7ff3604Amit Mahajan                            isCurrentFormat3gpp2);
1650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } catch (IllegalArgumentException e) {
1660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "error loading SmsTracker: " + e);
1670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    continue;
1680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (tracker.getMessageCount() == 1) {
1710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    // deliver single-part message
1720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    broadcastSms(tracker);
1730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else {
1740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    SmsReferenceKey reference = new SmsReferenceKey(tracker);
1750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer receivedCount = multiPartReceivedCount.get(reference);
1760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    if (receivedCount == null) {
1770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        multiPartReceivedCount.put(reference, 1);    // first segment seen
1780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (tracker.getTimestamp() <
1790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                                (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) {
1800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // older than 30 days; delete if we don't find all the segments
1810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.add(reference);
1820d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1830d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    } else {
1840d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        int newCount = receivedCount + 1;
1850d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (newCount == tracker.getMessageCount()) {
1860d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // looks like we've got all the pieces; send a single tracker
1870d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // to state machine which will find the other pieces to broadcast
1880d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            if (DBG) Rlog.d(TAG, "found complete multi-part message");
1890d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            broadcastSms(tracker);
1900d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // don't delete this old message until after we broadcast it
1910d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.remove(reference);
1920d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        } else {
1930d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            multiPartReceivedCount.put(reference, newCount);
1940d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1950d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    }
1960d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1970d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1980d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            // Delete old incomplete message segments
1990d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            for (SmsReferenceKey message : oldMultiPartMessages) {
2006e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                // delete permanently
2016e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                int rows = mResolver.delete(InboundSmsHandler.sRawUriPermanentDelete,
2026e4abd849e12860a7459f7ec74496ca02b9d7ee0Amit Mahajan                        InboundSmsHandler.SELECT_BY_REFERENCE, message.getDeleteWhereArgs());
2030d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (rows == 0) {
2040d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "No rows were deleted from raw table!");
2050d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else if (DBG) {
2060d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.d(TAG, "Deleted " + rows + " rows from raw table for incomplete "
2070d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            + message.mMessageCount + " part message");
2080d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
2090d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } catch (SQLException e) {
2110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "error reading pending SMS messages", e);
2120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } finally {
2130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor != null) {
2140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                cursor.close();
2150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (DBG) Rlog.d(TAG, "finished scanning raw table in "
2170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    + ((System.nanoTime() - startTime) / 1000000) + " ms");
2180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
2220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
2230d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
2240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void broadcastSms(InboundSmsTracker tracker) {
2250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        InboundSmsHandler handler;
2260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (tracker.is3gpp2()) {
2270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mCdmaInboundSmsHandler;
2280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
2290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mGsmInboundSmsHandler;
2300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2310d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (handler != null) {
2320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
2330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
2340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "null handler for " + tracker.getFormat() + " format, can't deliver.");
2350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
2390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Used as the HashMap key for matching concatenated message segments.
2400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
2410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static class SmsReferenceKey {
2420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final String mAddress;
2430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mReferenceNumber;
2440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mMessageCount;
2450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        SmsReferenceKey(InboundSmsTracker tracker) {
2470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mAddress = tracker.getAddress();
2480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mReferenceNumber = tracker.getReferenceNumber();
2490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mMessageCount = tracker.getMessageCount();
2500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        String[] getDeleteWhereArgs() {
2530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return new String[]{mAddress, Integer.toString(mReferenceNumber),
2540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer.toString(mMessageCount)};
2550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public int hashCode() {
2590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return ((mReferenceNumber * 31) + mMessageCount) * 31 + mAddress.hashCode();
2600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public boolean equals(Object o) {
2640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (o instanceof SmsReferenceKey) {
2650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                SmsReferenceKey other = (SmsReferenceKey) o;
2660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return other.mAddress.equals(mAddress)
2670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mReferenceNumber == mReferenceNumber)
2680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mMessageCount == mMessageCount);
2690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return false;
2710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby}
274