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