SmsBroadcastUndelivered.java revision dc7411e9fc8ac029a7bad8b85b0f24642a1774b6
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; 280d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.provider.Telephony; 290d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport android.telephony.Rlog; 300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 310d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.cdma.CdmaInboundSmsHandler; 320d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport com.android.internal.telephony.gsm.GsmInboundSmsHandler; 330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 340d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashMap; 350d4bcdf379842af4b6304809156971e926f374f0Jake Hambyimport java.util.HashSet; 360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby/** 388f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * Called when the credential-encrypted storage is unlocked, collecting all acknowledged messages 398f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * and deleting any partial message segments older than 30 days. Called from a worker thread to 408f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * avoid delaying phone app startup. The last step is to broadcast the first pending message from 418f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * the main thread, then the remaining pending messages will be broadcast after the previous 428f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang * ordered broadcast completes. 430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */ 448f21e0c6528f015924d153350dd2a717d3b51f66Ji Yangpublic class SmsBroadcastUndelivered { 450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private static final String TAG = "SmsBroadcastUndelivered"; 460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private static final boolean DBG = InboundSmsHandler.DBG; 470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby /** Delete any partial message segments older than 30 days. */ 490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby static final long PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30; 500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby /** 520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Query projection for dispatching pending messages at boot time. 530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Column order must match the {@code *_COLUMN} constants in {@link InboundSmsHandler}. 540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */ 550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private static final String[] PDU_PENDING_MESSAGE_PROJECTION = { 560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "pdu", 570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "sequence", 580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "destination_port", 590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "date", 600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "reference_number", 610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "count", 620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "address", 630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby "_id" 640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby }; 650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby /** URI for raw table from SmsProvider. */ 670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 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())) { 878f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang Thread t = new Thread() { 888f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang @Override 898f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang public void run() { 908f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang scanRawTable(); 918f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang } 928f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang }; 938f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang t.start(); 948f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang } 958f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang } 968f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang }; 978f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang 988f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang public static void initialize(Context context, GsmInboundSmsHandler gsmInboundSmsHandler, 998f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang CdmaInboundSmsHandler cdmaInboundSmsHandler) { 1008f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang if (instance == null) { 1018f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang instance = new SmsBroadcastUndelivered( 1028f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang context, gsmInboundSmsHandler, cdmaInboundSmsHandler); 1038f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang } 104dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang 105dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang // Tell handlers to start processing new messages and transit from the startup state to the 106dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang // idle state. This method may be called multiple times for multi-sim devices. We must make 107dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang // sure the state transition happen to all inbound sms handlers. 108dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang if (gsmInboundSmsHandler != null) { 109dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang gsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS); 110dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang } 111dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang if (cdmaInboundSmsHandler != null) { 112dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang cdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS); 113dc7411e9fc8ac029a7bad8b85b0f24642a1774b6Ji Yang } 1148f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang } 1158f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang 1168f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang private SmsBroadcastUndelivered(Context context, GsmInboundSmsHandler gsmInboundSmsHandler, 1170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby CdmaInboundSmsHandler cdmaInboundSmsHandler) { 1180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby mResolver = context.getContentResolver(); 1190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby mGsmInboundSmsHandler = gsmInboundSmsHandler; 1200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby mCdmaInboundSmsHandler = cdmaInboundSmsHandler; 1210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 1228f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang IntentFilter userFilter = new IntentFilter(); 1238f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang userFilter.addAction(Intent.ACTION_USER_UNLOCKED); 1248f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 1250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 1270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby /** 1280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete. 1290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */ 1300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private void scanRawTable() { 1318f21e0c6528f015924d153350dd2a717d3b51f66Ji Yang if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages"); 1320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby long startTime = System.nanoTime(); 1330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby HashMap<SmsReferenceKey, Integer> multiPartReceivedCount = 1340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby new HashMap<SmsReferenceKey, Integer>(4); 1350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby HashSet<SmsReferenceKey> oldMultiPartMessages = new HashSet<SmsReferenceKey>(4); 1360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Cursor cursor = null; 1370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby try { 1380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby cursor = mResolver.query(sRawUri, PDU_PENDING_MESSAGE_PROJECTION, null, null, null); 1390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (cursor == null) { 1400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Rlog.e(TAG, "error getting pending message cursor"); 1410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby return; 1420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 1440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby boolean isCurrentFormat3gpp2 = InboundSmsHandler.isCurrentFormat3gpp2(); 1450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby while (cursor.moveToNext()) { 1460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby InboundSmsTracker tracker; 1470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby try { 1480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby tracker = new InboundSmsTracker(cursor, isCurrentFormat3gpp2); 1490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } catch (IllegalArgumentException e) { 1500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Rlog.e(TAG, "error loading SmsTracker: " + e); 1510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby continue; 1520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 1540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (tracker.getMessageCount() == 1) { 1550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby // deliver single-part message 1560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby broadcastSms(tracker); 1570d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } else { 1580d4bcdf379842af4b6304809156971e926f374f0Jake Hamby SmsReferenceKey reference = new SmsReferenceKey(tracker); 1590d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Integer receivedCount = multiPartReceivedCount.get(reference); 1600d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (receivedCount == null) { 1610d4bcdf379842af4b6304809156971e926f374f0Jake Hamby multiPartReceivedCount.put(reference, 1); // first segment seen 1620d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (tracker.getTimestamp() < 1630d4bcdf379842af4b6304809156971e926f374f0Jake Hamby (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) { 1640d4bcdf379842af4b6304809156971e926f374f0Jake Hamby // older than 30 days; delete if we don't find all the segments 1650d4bcdf379842af4b6304809156971e926f374f0Jake Hamby oldMultiPartMessages.add(reference); 1660d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1670d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } else { 1680d4bcdf379842af4b6304809156971e926f374f0Jake Hamby int newCount = receivedCount + 1; 1690d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (newCount == tracker.getMessageCount()) { 1700d4bcdf379842af4b6304809156971e926f374f0Jake Hamby // looks like we've got all the pieces; send a single tracker 1710d4bcdf379842af4b6304809156971e926f374f0Jake Hamby // to state machine which will find the other pieces to broadcast 1720d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (DBG) Rlog.d(TAG, "found complete multi-part message"); 1730d4bcdf379842af4b6304809156971e926f374f0Jake Hamby broadcastSms(tracker); 1740d4bcdf379842af4b6304809156971e926f374f0Jake Hamby // don't delete this old message until after we broadcast it 1750d4bcdf379842af4b6304809156971e926f374f0Jake Hamby oldMultiPartMessages.remove(reference); 1760d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } else { 1770d4bcdf379842af4b6304809156971e926f374f0Jake Hamby multiPartReceivedCount.put(reference, newCount); 1780d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1790d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1800d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1810d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1820d4bcdf379842af4b6304809156971e926f374f0Jake Hamby // Delete old incomplete message segments 1830d4bcdf379842af4b6304809156971e926f374f0Jake Hamby for (SmsReferenceKey message : oldMultiPartMessages) { 1840d4bcdf379842af4b6304809156971e926f374f0Jake Hamby int rows = mResolver.delete(sRawUri, InboundSmsHandler.SELECT_BY_REFERENCE, 1850d4bcdf379842af4b6304809156971e926f374f0Jake Hamby message.getDeleteWhereArgs()); 1860d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (rows == 0) { 1870d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Rlog.e(TAG, "No rows were deleted from raw table!"); 1880d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } else if (DBG) { 1890d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Rlog.d(TAG, "Deleted " + rows + " rows from raw table for incomplete " 1900d4bcdf379842af4b6304809156971e926f374f0Jake Hamby + message.mMessageCount + " part message"); 1910d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1920d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1930d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } catch (SQLException e) { 1940d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Rlog.e(TAG, "error reading pending SMS messages", e); 1950d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } finally { 1960d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (cursor != null) { 1970d4bcdf379842af4b6304809156971e926f374f0Jake Hamby cursor.close(); 1980d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 1990d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (DBG) Rlog.d(TAG, "finished scanning raw table in " 2000d4bcdf379842af4b6304809156971e926f374f0Jake Hamby + ((System.nanoTime() - startTime) / 1000000) + " ms"); 2010d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2020d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2030d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 2040d4bcdf379842af4b6304809156971e926f374f0Jake Hamby /** 2050d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast. 2060d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */ 2070d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private void broadcastSms(InboundSmsTracker tracker) { 2080d4bcdf379842af4b6304809156971e926f374f0Jake Hamby InboundSmsHandler handler; 2090d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (tracker.is3gpp2()) { 2100d4bcdf379842af4b6304809156971e926f374f0Jake Hamby handler = mCdmaInboundSmsHandler; 2110d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } else { 2120d4bcdf379842af4b6304809156971e926f374f0Jake Hamby handler = mGsmInboundSmsHandler; 2130d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2140d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (handler != null) { 2150d4bcdf379842af4b6304809156971e926f374f0Jake Hamby handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker); 2160d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } else { 2170d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Rlog.e(TAG, "null handler for " + tracker.getFormat() + " format, can't deliver."); 2180d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2190d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2200d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 2210d4bcdf379842af4b6304809156971e926f374f0Jake Hamby /** 2220d4bcdf379842af4b6304809156971e926f374f0Jake Hamby * Used as the HashMap key for matching concatenated message segments. 2230d4bcdf379842af4b6304809156971e926f374f0Jake Hamby */ 2240d4bcdf379842af4b6304809156971e926f374f0Jake Hamby private static class SmsReferenceKey { 2250d4bcdf379842af4b6304809156971e926f374f0Jake Hamby final String mAddress; 2260d4bcdf379842af4b6304809156971e926f374f0Jake Hamby final int mReferenceNumber; 2270d4bcdf379842af4b6304809156971e926f374f0Jake Hamby final int mMessageCount; 2280d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 2290d4bcdf379842af4b6304809156971e926f374f0Jake Hamby SmsReferenceKey(InboundSmsTracker tracker) { 2300d4bcdf379842af4b6304809156971e926f374f0Jake Hamby mAddress = tracker.getAddress(); 2310d4bcdf379842af4b6304809156971e926f374f0Jake Hamby mReferenceNumber = tracker.getReferenceNumber(); 2320d4bcdf379842af4b6304809156971e926f374f0Jake Hamby mMessageCount = tracker.getMessageCount(); 2330d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2340d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 2350d4bcdf379842af4b6304809156971e926f374f0Jake Hamby String[] getDeleteWhereArgs() { 2360d4bcdf379842af4b6304809156971e926f374f0Jake Hamby return new String[]{mAddress, Integer.toString(mReferenceNumber), 2370d4bcdf379842af4b6304809156971e926f374f0Jake Hamby Integer.toString(mMessageCount)}; 2380d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2390d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 2400d4bcdf379842af4b6304809156971e926f374f0Jake Hamby @Override 2410d4bcdf379842af4b6304809156971e926f374f0Jake Hamby public int hashCode() { 2420d4bcdf379842af4b6304809156971e926f374f0Jake Hamby return ((mReferenceNumber * 31) + mMessageCount) * 31 + mAddress.hashCode(); 2430d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2440d4bcdf379842af4b6304809156971e926f374f0Jake Hamby 2450d4bcdf379842af4b6304809156971e926f374f0Jake Hamby @Override 2460d4bcdf379842af4b6304809156971e926f374f0Jake Hamby public boolean equals(Object o) { 2470d4bcdf379842af4b6304809156971e926f374f0Jake Hamby if (o instanceof SmsReferenceKey) { 2480d4bcdf379842af4b6304809156971e926f374f0Jake Hamby SmsReferenceKey other = (SmsReferenceKey) o; 2490d4bcdf379842af4b6304809156971e926f374f0Jake Hamby return other.mAddress.equals(mAddress) 2500d4bcdf379842af4b6304809156971e926f374f0Jake Hamby && (other.mReferenceNumber == mReferenceNumber) 2510d4bcdf379842af4b6304809156971e926f374f0Jake Hamby && (other.mMessageCount == mMessageCount); 2520d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2530d4bcdf379842af4b6304809156971e926f374f0Jake Hamby return false; 2540d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2550d4bcdf379842af4b6304809156971e926f374f0Jake Hamby } 2560d4bcdf379842af4b6304809156971e926f374f0Jake Hamby} 257