1 /* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.google.android.mms.util; 19 20 import android.content.ContentUris; 21 import android.content.UriMatcher; 22 import android.net.Uri; 23 import android.provider.Telephony.Mms; 24 import android.util.Log; 25 26 import java.util.HashMap; 27 import java.util.HashSet; 28 29 public final class PduCache extends AbstractCache<Uri, PduCacheEntry> { 30 private static final String TAG = "PduCache"; 31 private static final boolean DEBUG = false; 32 private static final boolean LOCAL_LOGV = false; 33 34 private static final int MMS_ALL = 0; 35 private static final int MMS_ALL_ID = 1; 36 private static final int MMS_INBOX = 2; 37 private static final int MMS_INBOX_ID = 3; 38 private static final int MMS_SENT = 4; 39 private static final int MMS_SENT_ID = 5; 40 private static final int MMS_DRAFTS = 6; 41 private static final int MMS_DRAFTS_ID = 7; 42 private static final int MMS_OUTBOX = 8; 43 private static final int MMS_OUTBOX_ID = 9; 44 private static final int MMS_CONVERSATION = 10; 45 private static final int MMS_CONVERSATION_ID = 11; 46 47 private static final UriMatcher URI_MATCHER; 48 private static final HashMap<Integer, Integer> MATCH_TO_MSGBOX_ID_MAP; 49 50 private static PduCache sInstance; 51 52 static { 53 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); 54 URI_MATCHER.addURI("mms", null, MMS_ALL); 55 URI_MATCHER.addURI("mms", "#", MMS_ALL_ID); 56 URI_MATCHER.addURI("mms", "inbox", MMS_INBOX); 57 URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID); 58 URI_MATCHER.addURI("mms", "sent", MMS_SENT); 59 URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID); 60 URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS); 61 URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID); 62 URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX); 63 URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID); 64 URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION); 65 URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID); 66 67 MATCH_TO_MSGBOX_ID_MAP = new HashMap<Integer, Integer>(); 68 MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX); 69 MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT); 70 MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS); 71 MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX); 72 } 73 74 private final HashMap<Integer, HashSet<Uri>> mMessageBoxes; 75 private final HashMap<Long, HashSet<Uri>> mThreads; 76 77 private PduCache() { 78 mMessageBoxes = new HashMap<Integer, HashSet<Uri>>(); 79 mThreads = new HashMap<Long, HashSet<Uri>>(); 80 } 81 82 synchronized public static final PduCache getInstance() { 83 if (sInstance == null) { 84 if (LOCAL_LOGV) { 85 Log.v(TAG, "Constructing new PduCache instance."); 86 } 87 sInstance = new PduCache(); 88 } 89 return sInstance; 90 } 91 92 @Override 93 synchronized public boolean put(Uri uri, PduCacheEntry entry) { 94 int msgBoxId = entry.getMessageBox(); 95 HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId); 96 if (msgBox == null) { 97 msgBox = new HashSet<Uri>(); 98 mMessageBoxes.put(msgBoxId, msgBox); 99 } 100 101 long threadId = entry.getThreadId(); 102 HashSet<Uri> thread = mThreads.get(threadId); 103 if (thread == null) { 104 thread = new HashSet<Uri>(); 105 mThreads.put(threadId, thread); 106 } 107 108 Uri finalKey = normalizeKey(uri); 109 boolean result = super.put(finalKey, entry); 110 if (result) { 111 msgBox.add(finalKey); 112 thread.add(finalKey); 113 } 114 return result; 115 } 116 117 @Override 118 synchronized public PduCacheEntry purge(Uri uri) { 119 int match = URI_MATCHER.match(uri); 120 switch (match) { 121 case MMS_ALL_ID: 122 return purgeSingleEntry(uri); 123 case MMS_INBOX_ID: 124 case MMS_SENT_ID: 125 case MMS_DRAFTS_ID: 126 case MMS_OUTBOX_ID: 127 String msgId = uri.getLastPathSegment(); 128 return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId)); 129 // Implicit batch of purge, return null. 130 case MMS_ALL: 131 case MMS_CONVERSATION: 132 purgeAll(); 133 return null; 134 case MMS_INBOX: 135 case MMS_SENT: 136 case MMS_DRAFTS: 137 case MMS_OUTBOX: 138 purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match)); 139 return null; 140 case MMS_CONVERSATION_ID: 141 purgeByThreadId(ContentUris.parseId(uri)); 142 return null; 143 default: 144 return null; 145 } 146 } 147 148 private PduCacheEntry purgeSingleEntry(Uri key) { 149 PduCacheEntry entry = super.purge(key); 150 if (entry != null) { 151 removeFromThreads(key, entry); 152 removeFromMessageBoxes(key, entry); 153 return entry; 154 } 155 return null; 156 } 157 158 @Override 159 synchronized public void purgeAll() { 160 super.purgeAll(); 161 162 mMessageBoxes.clear(); 163 mThreads.clear(); 164 } 165 166 /** 167 * @param uri The Uri to be normalized. 168 * @return Uri The normalized key of cached entry. 169 */ 170 private Uri normalizeKey(Uri uri) { 171 int match = URI_MATCHER.match(uri); 172 Uri normalizedKey = null; 173 174 switch (match) { 175 case MMS_ALL_ID: 176 normalizedKey = uri; 177 break; 178 case MMS_INBOX_ID: 179 case MMS_SENT_ID: 180 case MMS_DRAFTS_ID: 181 case MMS_OUTBOX_ID: 182 String msgId = uri.getLastPathSegment(); 183 normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId); 184 break; 185 default: 186 return null; 187 } 188 189 if (LOCAL_LOGV) { 190 Log.v(TAG, uri + " -> " + normalizedKey); 191 } 192 return normalizedKey; 193 } 194 195 private void purgeByMessageBox(Integer msgBoxId) { 196 if (LOCAL_LOGV) { 197 Log.v(TAG, "Purge cache in message box: " + msgBoxId); 198 } 199 200 if (msgBoxId != null) { 201 HashSet<Uri> msgBox = mMessageBoxes.remove(msgBoxId); 202 if (msgBox != null) { 203 for (Uri key : msgBox) { 204 PduCacheEntry entry = super.purge(key); 205 if (entry != null) { 206 removeFromThreads(key, entry); 207 } 208 } 209 } 210 } 211 } 212 213 private void removeFromThreads(Uri key, PduCacheEntry entry) { 214 HashSet<Uri> thread = mThreads.get(entry.getThreadId()); 215 if (thread != null) { 216 thread.remove(key); 217 } 218 } 219 220 private void purgeByThreadId(long threadId) { 221 if (LOCAL_LOGV) { 222 Log.v(TAG, "Purge cache in thread: " + threadId); 223 } 224 225 HashSet<Uri> thread = mThreads.remove(threadId); 226 if (thread != null) { 227 for (Uri key : thread) { 228 PduCacheEntry entry = super.purge(key); 229 if (entry != null) { 230 removeFromMessageBoxes(key, entry); 231 } 232 } 233 } 234 } 235 236 private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) { 237 HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox())); 238 if (msgBox != null) { 239 msgBox.remove(key); 240 } 241 } 242 } 243