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.android.messaging.mmslib.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 androidx.collection.SimpleArrayMap; 25 import android.util.Log; 26 import android.util.SparseArray; 27 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 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 SparseArray<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 SparseArray<Integer>(); MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX)68 MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX); MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT)69 MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT); MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS)70 MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS); MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX)71 MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX); 72 } 73 74 private final SparseArray<HashSet<Uri>> mMessageBoxes; 75 private final SimpleArrayMap<Long, HashSet<Uri>> mThreads; 76 private final HashSet<Uri> mUpdating; 77 PduCache()78 private PduCache() { 79 mMessageBoxes = new SparseArray<HashSet<Uri>>(); 80 mThreads = new SimpleArrayMap<Long, HashSet<Uri>>(); 81 mUpdating = new HashSet<Uri>(); 82 } 83 getInstance()84 public static final synchronized PduCache getInstance() { 85 if (sInstance == null) { 86 if (LOCAL_LOGV) { 87 Log.v(TAG, "Constructing new PduCache instance."); 88 } 89 sInstance = new PduCache(); 90 } 91 return sInstance; 92 } 93 94 @Override put(Uri uri, PduCacheEntry entry)95 public synchronized boolean put(Uri uri, PduCacheEntry entry) { 96 int msgBoxId = entry.getMessageBox(); 97 HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId); 98 if (msgBox == null) { 99 msgBox = new HashSet<Uri>(); 100 mMessageBoxes.put(msgBoxId, msgBox); 101 } 102 103 long threadId = entry.getThreadId(); 104 HashSet<Uri> thread = mThreads.get(threadId); 105 if (thread == null) { 106 thread = new HashSet<Uri>(); 107 mThreads.put(threadId, thread); 108 } 109 110 Uri finalKey = normalizeKey(uri); 111 boolean result = super.put(finalKey, entry); 112 if (result) { 113 msgBox.add(finalKey); 114 thread.add(finalKey); 115 } 116 setUpdating(uri, false); 117 return result; 118 } 119 setUpdating(Uri uri, boolean updating)120 public synchronized void setUpdating(Uri uri, boolean updating) { 121 if (updating) { 122 mUpdating.add(uri); 123 } else { 124 mUpdating.remove(uri); 125 } 126 } 127 isUpdating(Uri uri)128 public synchronized boolean isUpdating(Uri uri) { 129 return mUpdating.contains(uri); 130 } 131 132 @Override purge(Uri uri)133 public synchronized PduCacheEntry purge(Uri uri) { 134 int match = URI_MATCHER.match(uri); 135 switch (match) { 136 case MMS_ALL_ID: 137 return purgeSingleEntry(uri); 138 case MMS_INBOX_ID: 139 case MMS_SENT_ID: 140 case MMS_DRAFTS_ID: 141 case MMS_OUTBOX_ID: 142 String msgId = uri.getLastPathSegment(); 143 return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId)); 144 // Implicit batch of purge, return null. 145 case MMS_ALL: 146 case MMS_CONVERSATION: 147 purgeAll(); 148 return null; 149 case MMS_INBOX: 150 case MMS_SENT: 151 case MMS_DRAFTS: 152 case MMS_OUTBOX: 153 purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match)); 154 return null; 155 case MMS_CONVERSATION_ID: 156 purgeByThreadId(ContentUris.parseId(uri)); 157 return null; 158 default: 159 return null; 160 } 161 } 162 purgeSingleEntry(Uri key)163 private PduCacheEntry purgeSingleEntry(Uri key) { 164 mUpdating.remove(key); 165 PduCacheEntry entry = super.purge(key); 166 if (entry != null) { 167 removeFromThreads(key, entry); 168 removeFromMessageBoxes(key, entry); 169 return entry; 170 } 171 return null; 172 } 173 174 @Override purgeAll()175 public synchronized void purgeAll() { 176 super.purgeAll(); 177 178 mMessageBoxes.clear(); 179 mThreads.clear(); 180 mUpdating.clear(); 181 } 182 183 /** 184 * @param uri The Uri to be normalized. 185 * @return Uri The normalized key of cached entry. 186 */ normalizeKey(Uri uri)187 private Uri normalizeKey(Uri uri) { 188 int match = URI_MATCHER.match(uri); 189 Uri normalizedKey = null; 190 191 switch (match) { 192 case MMS_ALL_ID: 193 normalizedKey = uri; 194 break; 195 case MMS_INBOX_ID: 196 case MMS_SENT_ID: 197 case MMS_DRAFTS_ID: 198 case MMS_OUTBOX_ID: 199 String msgId = uri.getLastPathSegment(); 200 normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId); 201 break; 202 default: 203 return null; 204 } 205 206 if (LOCAL_LOGV) { 207 Log.v(TAG, uri + " -> " + normalizedKey); 208 } 209 return normalizedKey; 210 } 211 purgeByMessageBox(Integer msgBoxId)212 private void purgeByMessageBox(Integer msgBoxId) { 213 if (LOCAL_LOGV) { 214 Log.v(TAG, "Purge cache in message box: " + msgBoxId); 215 } 216 217 if (msgBoxId != null) { 218 HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId); 219 mMessageBoxes.remove(msgBoxId); 220 if (msgBox != null) { 221 for (Uri key : msgBox) { 222 mUpdating.remove(key); 223 PduCacheEntry entry = super.purge(key); 224 if (entry != null) { 225 removeFromThreads(key, entry); 226 } 227 } 228 } 229 } 230 } 231 removeFromThreads(Uri key, PduCacheEntry entry)232 private void removeFromThreads(Uri key, PduCacheEntry entry) { 233 HashSet<Uri> thread = mThreads.get(entry.getThreadId()); 234 if (thread != null) { 235 thread.remove(key); 236 } 237 } 238 purgeByThreadId(long threadId)239 private void purgeByThreadId(long threadId) { 240 if (LOCAL_LOGV) { 241 Log.v(TAG, "Purge cache in thread: " + threadId); 242 } 243 244 HashSet<Uri> thread = mThreads.remove(threadId); 245 if (thread != null) { 246 for (Uri key : thread) { 247 mUpdating.remove(key); 248 PduCacheEntry entry = super.purge(key); 249 if (entry != null) { 250 removeFromMessageBoxes(key, entry); 251 } 252 } 253 } 254 } 255 removeFromMessageBoxes(Uri key, PduCacheEntry entry)256 private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) { 257 HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox())); 258 if (msgBox != null) { 259 msgBox.remove(key); 260 } 261 } 262 } 263