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.Config; 25 import android.util.Log; 26 27 import java.util.HashMap; 28 import java.util.HashSet; 29 30 public final class PduCache extends AbstractCache<Uri, PduCacheEntry> { 31 private static final String TAG = "PduCache"; 32 private static final boolean DEBUG = false; 33 private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; 34 35 private static final int MMS_ALL = 0; 36 private static final int MMS_ALL_ID = 1; 37 private static final int MMS_INBOX = 2; 38 private static final int MMS_INBOX_ID = 3; 39 private static final int MMS_SENT = 4; 40 private static final int MMS_SENT_ID = 5; 41 private static final int MMS_DRAFTS = 6; 42 private static final int MMS_DRAFTS_ID = 7; 43 private static final int MMS_OUTBOX = 8; 44 private static final int MMS_OUTBOX_ID = 9; 45 private static final int MMS_CONVERSATION = 10; 46 private static final int MMS_CONVERSATION_ID = 11; 47 48 private static final UriMatcher URI_MATCHER; 49 private static final HashMap<Integer, Integer> MATCH_TO_MSGBOX_ID_MAP; 50 51 private static PduCache sInstance; 52 53 static { 54 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); 55 URI_MATCHER.addURI("mms", null, MMS_ALL); 56 URI_MATCHER.addURI("mms", "#", MMS_ALL_ID); 57 URI_MATCHER.addURI("mms", "inbox", MMS_INBOX); 58 URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID); 59 URI_MATCHER.addURI("mms", "sent", MMS_SENT); 60 URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID); 61 URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS); 62 URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID); 63 URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX); 64 URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID); 65 URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION); 66 URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID); 67 68 MATCH_TO_MSGBOX_ID_MAP = new HashMap<Integer, Integer>(); 69 MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX); 70 MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT); 71 MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS); 72 MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX); 73 } 74 75 private final HashMap<Integer, HashSet<Uri>> mMessageBoxes; 76 private final HashMap<Long, HashSet<Uri>> mThreads; 77 78 private PduCache() { 79 mMessageBoxes = new HashMap<Integer, HashSet<Uri>>(); 80 mThreads = new HashMap<Long, HashSet<Uri>>(); 81 } 82 83 synchronized public static final PduCache getInstance() { 84 if (sInstance == null) { 85 if (LOCAL_LOGV) { 86 Log.v(TAG, "Constructing new PduCache instance."); 87 } 88 sInstance = new PduCache(); 89 } 90 return sInstance; 91 } 92 93 @Override 94 synchronized public boolean put(Uri uri, PduCacheEntry entry) { 95 int msgBoxId = entry.getMessageBox(); 96 HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId); 97 if (msgBox == null) { 98 msgBox = new HashSet<Uri>(); 99 mMessageBoxes.put(msgBoxId, msgBox); 100 } 101 102 long threadId = entry.getThreadId(); 103 HashSet<Uri> thread = mThreads.get(threadId); 104 if (thread == null) { 105 thread = new HashSet<Uri>(); 106 mThreads.put(threadId, thread); 107 } 108 109 Uri finalKey = normalizeKey(uri); 110 boolean result = super.put(finalKey, entry); 111 if (result) { 112 msgBox.add(finalKey); 113 thread.add(finalKey); 114 } 115 return result; 116 } 117 118 @Override 119 synchronized public PduCacheEntry purge(Uri uri) { 120 int match = URI_MATCHER.match(uri); 121 switch (match) { 122 case MMS_ALL_ID: 123 return purgeSingleEntry(uri); 124 case MMS_INBOX_ID: 125 case MMS_SENT_ID: 126 case MMS_DRAFTS_ID: 127 case MMS_OUTBOX_ID: 128 String msgId = uri.getLastPathSegment(); 129 return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId)); 130 // Implicit batch of purge, return null. 131 case MMS_ALL: 132 case MMS_CONVERSATION: 133 purgeAll(); 134 return null; 135 case MMS_INBOX: 136 case MMS_SENT: 137 case MMS_DRAFTS: 138 case MMS_OUTBOX: 139 purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match)); 140 return null; 141 case MMS_CONVERSATION_ID: 142 purgeByThreadId(ContentUris.parseId(uri)); 143 return null; 144 default: 145 return null; 146 } 147 } 148 149 private PduCacheEntry purgeSingleEntry(Uri key) { 150 PduCacheEntry entry = super.purge(key); 151 if (entry != null) { 152 removeFromThreads(key, entry); 153 removeFromMessageBoxes(key, entry); 154 return entry; 155 } 156 return null; 157 } 158 159 @Override 160 synchronized public void purgeAll() { 161 super.purgeAll(); 162 163 mMessageBoxes.clear(); 164 mThreads.clear(); 165 } 166 167 /** 168 * @param uri The Uri to be normalized. 169 * @return Uri The normalized key of cached entry. 170 */ 171 private Uri normalizeKey(Uri uri) { 172 int match = URI_MATCHER.match(uri); 173 Uri normalizedKey = null; 174 175 switch (match) { 176 case MMS_ALL_ID: 177 normalizedKey = uri; 178 break; 179 case MMS_INBOX_ID: 180 case MMS_SENT_ID: 181 case MMS_DRAFTS_ID: 182 case MMS_OUTBOX_ID: 183 String msgId = uri.getLastPathSegment(); 184 normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId); 185 break; 186 default: 187 return null; 188 } 189 190 if (LOCAL_LOGV) { 191 Log.v(TAG, uri + " -> " + normalizedKey); 192 } 193 return normalizedKey; 194 } 195 196 private void purgeByMessageBox(Integer msgBoxId) { 197 if (LOCAL_LOGV) { 198 Log.v(TAG, "Purge cache in message box: " + msgBoxId); 199 } 200 201 if (msgBoxId != null) { 202 HashSet<Uri> msgBox = mMessageBoxes.remove(msgBoxId); 203 if (msgBox != null) { 204 for (Uri key : msgBox) { 205 PduCacheEntry entry = super.purge(key); 206 if (entry != null) { 207 removeFromThreads(key, entry); 208 } 209 } 210 } 211 } 212 } 213 214 private void removeFromThreads(Uri key, PduCacheEntry entry) { 215 HashSet<Uri> thread = mThreads.get(entry.getThreadId()); 216 if (thread != null) { 217 thread.remove(key); 218 } 219 } 220 221 private void purgeByThreadId(long threadId) { 222 if (LOCAL_LOGV) { 223 Log.v(TAG, "Purge cache in thread: " + threadId); 224 } 225 226 HashSet<Uri> thread = mThreads.remove(threadId); 227 if (thread != null) { 228 for (Uri key : thread) { 229 PduCacheEntry entry = super.purge(key); 230 if (entry != null) { 231 removeFromMessageBoxes(key, entry); 232 } 233 } 234 } 235 } 236 237 private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) { 238 HashSet<Uri> msgBox = mThreads.get(entry.getMessageBox()); 239 if (msgBox != null) { 240 msgBox.remove(key); 241 } 242 } 243 } 244