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
190d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.content.ContentResolver;
200d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.content.Context;
210d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.database.Cursor;
220d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.database.SQLException;
230d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.net.Uri;
240d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.provider.Telephony;
250d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.telephony.Rlog;
260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
270d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
280d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.gsm.GsmInboundSmsHandler;
290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
300d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashMap;
310d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashSet;
320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby/**
340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Called at boot time to clean out the raw table, collecting all acknowledged messages and
350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * deleting any partial message segments older than 30 days. Called from a worker thread to
360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * avoid delaying phone app startup. The last step is to broadcast the first pending message
370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * from the main thread, then the remaining pending messages will be broadcast after the
380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * previous ordered broadcast completes.
390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */
400d4bcdf379842af4b6304809156971e926f374f0Jake Hambypublic class SmsBroadcastUndelivered implements Runnable {
410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final String TAG = "SmsBroadcastUndelivered";
420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final boolean DBG = InboundSmsHandler.DBG;
430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Delete any partial message segments older than 30 days. */
450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    static final long PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Query projection for dispatching pending messages at boot time.
490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Column order must match the {@code *_COLUMN} constants in {@link InboundSmsHandler}.
500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final String[] PDU_PENDING_MESSAGE_PROJECTION = {
520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "pdu",
530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "sequence",
540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "destination_port",
550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "date",
560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "reference_number",
570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "count",
580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "address",
590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            "_id"
600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    };
610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** URI for raw table from SmsProvider. */
630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Content resolver to use to access raw table from SmsProvider. */
660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final ContentResolver mResolver;
670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP-format messages (may be null). */
690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final GsmInboundSmsHandler mGsmInboundSmsHandler;
700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /** Handler for 3GPP2-format messages (may be null). */
720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private final CdmaInboundSmsHandler mCdmaInboundSmsHandler;
730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    public SmsBroadcastUndelivered(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            CdmaInboundSmsHandler cdmaInboundSmsHandler) {
760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mResolver = context.getContentResolver();
770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mGsmInboundSmsHandler = gsmInboundSmsHandler;
780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        mCdmaInboundSmsHandler = cdmaInboundSmsHandler;
790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    @Override
820d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    public void run() {
830d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
840d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        scanRawTable();
850d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        // tell handlers to start processing new messages
860d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (mGsmInboundSmsHandler != null) {
870d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
880d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
890d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (mCdmaInboundSmsHandler != null) {
900d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
910d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
920d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
930d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
940d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
950d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
960d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
970d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void scanRawTable() {
980d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        long startTime = System.nanoTime();
990d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
1000d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                new HashMap<SmsReferenceKey, Integer>(4);
1010d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        HashSet<SmsReferenceKey> oldMultiPartMessages = new HashSet<SmsReferenceKey>(4);
1020d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        Cursor cursor = null;
1030d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        try {
1040d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            cursor = mResolver.query(sRawUri, PDU_PENDING_MESSAGE_PROJECTION, null, null, null);
1050d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor == null) {
1060d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                Rlog.e(TAG, "error getting pending message cursor");
1070d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return;
1080d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1090d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            boolean isCurrentFormat3gpp2 = InboundSmsHandler.isCurrentFormat3gpp2();
1110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            while (cursor.moveToNext()) {
1120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                InboundSmsTracker tracker;
1130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                try {
1140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    tracker = new InboundSmsTracker(cursor, isCurrentFormat3gpp2);
1150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } catch (IllegalArgumentException e) {
1160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "error loading SmsTracker: " + e);
1170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    continue;
1180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (tracker.getMessageCount() == 1) {
1210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    // deliver single-part message
1220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    broadcastSms(tracker);
1230d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else {
1240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    SmsReferenceKey reference = new SmsReferenceKey(tracker);
1250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer receivedCount = multiPartReceivedCount.get(reference);
1260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    if (receivedCount == null) {
1270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        multiPartReceivedCount.put(reference, 1);    // first segment seen
1280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (tracker.getTimestamp() <
1290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                                (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) {
1300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // older than 30 days; delete if we don't find all the segments
1310d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.add(reference);
1320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    } else {
1340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        int newCount = receivedCount + 1;
1350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        if (newCount == tracker.getMessageCount()) {
1360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // looks like we've got all the pieces; send a single tracker
1370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // to state machine which will find the other pieces to broadcast
1380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            if (DBG) Rlog.d(TAG, "found complete multi-part message");
1390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            broadcastSms(tracker);
1400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            // don't delete this old message until after we broadcast it
1410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            oldMultiPartMessages.remove(reference);
1420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        } else {
1430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            multiPartReceivedCount.put(reference, newCount);
1440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        }
1450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    }
1460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            // Delete old incomplete message segments
1490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            for (SmsReferenceKey message : oldMultiPartMessages) {
1500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                int rows = mResolver.delete(sRawUri, InboundSmsHandler.SELECT_BY_REFERENCE,
1510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        message.getDeleteWhereArgs());
1520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                if (rows == 0) {
1530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.e(TAG, "No rows were deleted from raw table!");
1540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                } else if (DBG) {
1550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Rlog.d(TAG, "Deleted " + rows + " rows from raw table for incomplete "
1560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                            + message.mMessageCount + " part message");
1570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                }
1580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } catch (SQLException e) {
1600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "error reading pending SMS messages", e);
1610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } finally {
1620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (cursor != null) {
1630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                cursor.close();
1640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
1650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (DBG) Rlog.d(TAG, "finished scanning raw table in "
1660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    + ((System.nanoTime() - startTime) / 1000000) + " ms");
1670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
1680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
1690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
1710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
1720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
1730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private void broadcastSms(InboundSmsTracker tracker) {
1740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        InboundSmsHandler handler;
1750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (tracker.is3gpp2()) {
1760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mCdmaInboundSmsHandler;
1770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
1780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler = mGsmInboundSmsHandler;
1790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
1800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        if (handler != null) {
1810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
1820d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        } else {
1830d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            Rlog.e(TAG, "null handler for " + tracker.getFormat() + " format, can't deliver.");
1840d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
1850d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
1860d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1870d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    /**
1880d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     * Used as the HashMap key for matching concatenated message segments.
1890d4bcdf379842af4b6304809156971e926f374f0Jake Hamby     */
1900d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    private static class SmsReferenceKey {
1910d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final String mAddress;
1920d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mReferenceNumber;
1930d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        final int mMessageCount;
1940d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
1950d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        SmsReferenceKey(InboundSmsTracker tracker) {
1960d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mAddress = tracker.getAddress();
1970d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mReferenceNumber = tracker.getReferenceNumber();
1980d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            mMessageCount = tracker.getMessageCount();
1990d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2000d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2010d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        String[] getDeleteWhereArgs() {
2020d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return new String[]{mAddress, Integer.toString(mReferenceNumber),
2030d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                    Integer.toString(mMessageCount)};
2040d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2050d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2060d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2070d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public int hashCode() {
2080d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return ((mReferenceNumber * 31) + mMessageCount) * 31 + mAddress.hashCode();
2090d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby
2110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        @Override
2120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        public boolean equals(Object o) {
2130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            if (o instanceof SmsReferenceKey) {
2140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                SmsReferenceKey other = (SmsReferenceKey) o;
2150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                return other.mAddress.equals(mAddress)
2160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mReferenceNumber == mReferenceNumber)
2170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby                        && (other.mMessageCount == mMessageCount);
2180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            }
2190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby            return false;
2200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby        }
2210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby    }
2220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby}
223