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