1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.mms.util; 18 19 import java.util.Set; 20 21 import android.content.Context; 22 import android.net.Uri; 23 import android.util.Log; 24 25 import com.android.mms.LogTag; 26 import com.android.mms.model.SlideshowModel; 27 import com.google.android.mms.MmsException; 28 import com.google.android.mms.pdu.GenericPdu; 29 import com.google.android.mms.pdu.MultimediaMessagePdu; 30 import com.google.android.mms.pdu.PduPersister; 31 import com.google.android.mms.util.PduCache; 32 import com.google.android.mms.util.PduCacheEntry; 33 34 /** 35 * Primary {@link PduLoaderManager} implementation used by {@link MessagingApplication}. 36 * <p> 37 * Public methods should only be used from a single thread (typically the UI 38 * thread). Callbacks will be invoked on the thread where the PduLoaderManager 39 * was instantiated. 40 * <p> 41 * Uses a thread-pool ExecutorService instead of AsyncTasks since clients may 42 * request lots of pdus around the same time, and AsyncTask may reject tasks 43 * in that case and has no way of bounding the number of threads used by those 44 * tasks. 45 * <p> 46 * PduLoaderManager is used to asynchronously load mms pdu's and then build a slideshow model 47 * from that loaded pdu. Then it will call the passed in callback with the result. This class 48 * uses the PduCache built into the mms framework. It also manages a local cache of slideshow 49 * models. The slideshow cache uses SoftReferences to hang onto the slideshow. 50 * 51 * Based on BooksImageManager by Virgil King. 52 */ 53 public class PduLoaderManager extends BackgroundLoaderManager { 54 private static final String TAG = "Mms:PduLoaderManager"; 55 56 private static final boolean DEBUG_DISABLE_CACHE = false; 57 private static final boolean DEBUG_DISABLE_PDUS = false; 58 private static final boolean DEBUG_LONG_WAIT = false; 59 60 private static PduCache mPduCache; 61 private final PduPersister mPduPersister; 62 private final SimpleCache<Uri, SlideshowModel> mSlideshowCache; 63 private final Context mContext; 64 65 public PduLoaderManager(final Context context) { 66 super(context); 67 68 mSlideshowCache = new SimpleCache<Uri, SlideshowModel>(8, 16, 0.75f, true); 69 mPduCache = PduCache.getInstance(); 70 mPduPersister = PduPersister.getPduPersister(context); 71 mContext = context; 72 } 73 74 public ItemLoadedFuture getPdu(Uri uri, boolean requestSlideshow, 75 final ItemLoadedCallback<PduLoaded> callback) { 76 if (uri == null) { 77 throw new NullPointerException(); 78 } 79 80 PduCacheEntry cacheEntry = null; 81 synchronized(mPduCache) { 82 if (!mPduCache.isUpdating(uri)) { 83 cacheEntry = mPduCache.get(uri); 84 } 85 } 86 final SlideshowModel slideshow = (requestSlideshow && !DEBUG_DISABLE_CACHE) ? 87 mSlideshowCache.get(uri) : null; 88 89 final boolean slideshowExists = (!requestSlideshow || slideshow != null); 90 final boolean pduExists = (cacheEntry != null && cacheEntry.getPdu() != null); 91 final boolean taskExists = mPendingTaskUris.contains(uri); 92 final boolean newTaskRequired = (!pduExists || !slideshowExists) && !taskExists; 93 final boolean callbackRequired = (callback != null); 94 95 if (pduExists && slideshowExists) { 96 if (callbackRequired) { 97 PduLoaded pduLoaded = new PduLoaded(cacheEntry.getPdu(), slideshow); 98 callback.onItemLoaded(pduLoaded, null); 99 } 100 return new NullItemLoadedFuture(); 101 } 102 103 if (callbackRequired) { 104 addCallback(uri, callback); 105 } 106 107 if (newTaskRequired) { 108 mPendingTaskUris.add(uri); 109 Runnable task = new PduTask(uri, requestSlideshow); 110 mExecutor.execute(task); 111 } 112 return new ItemLoadedFuture() { 113 private boolean mIsDone; 114 115 public void cancel(Uri uri) { 116 cancelCallback(callback); 117 removePdu(uri); // the pdu and/or slideshow might be half loaded. Make sure 118 // we load fresh the next time this uri is requested. 119 } 120 121 public void setIsDone(boolean done) { 122 mIsDone = done; 123 } 124 125 public boolean isDone() { 126 return mIsDone; 127 } 128 }; 129 } 130 131 @Override 132 public void clear() { 133 super.clear(); 134 135 synchronized(mPduCache) { 136 mPduCache.purgeAll(); 137 } 138 mSlideshowCache.clear(); 139 } 140 141 public void removePdu(Uri uri) { 142 if (Log.isLoggable(TAG, Log.DEBUG)) { 143 Log.d(TAG, "removePdu: " + uri); 144 } 145 if (uri != null) { 146 synchronized(mPduCache) { 147 mPduCache.purge(uri); 148 } 149 mSlideshowCache.remove(uri); 150 } 151 } 152 153 public String getTag() { 154 return TAG; 155 } 156 157 public class PduTask implements Runnable { 158 private final Uri mUri; 159 private final boolean mRequestSlideshow; 160 161 public PduTask(Uri uri, boolean requestSlideshow) { 162 if (uri == null) { 163 throw new NullPointerException(); 164 } 165 mUri = uri; 166 mRequestSlideshow = requestSlideshow; 167 } 168 169 /** {@inheritDoc} */ 170 public void run() { 171 if (DEBUG_DISABLE_PDUS) { 172 return; 173 } 174 if (DEBUG_LONG_WAIT) { 175 try { 176 Thread.sleep(10000); 177 } catch (InterruptedException e) { 178 } 179 } 180 GenericPdu pdu = null; 181 SlideshowModel slideshow = null; 182 Throwable exception = null; 183 try { 184 pdu = mPduPersister.load(mUri); 185 if (pdu != null && mRequestSlideshow) { 186 slideshow = SlideshowModel.createFromPduBody(mContext, 187 ((MultimediaMessagePdu)pdu).getBody()); 188 } 189 } catch (final MmsException e) { 190 Log.e(TAG, "MmsException loading uri: " + mUri, e); 191 exception = e; 192 } 193 final GenericPdu resultPdu = pdu; 194 final SlideshowModel resultSlideshow = slideshow; 195 final Throwable resultException = exception; 196 mCallbackHandler.post(new Runnable() { 197 public void run() { 198 final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri); 199 if (callbacks != null) { 200 // Make a copy so that the callback can unregister itself 201 for (final ItemLoadedCallback<PduLoaded> callback : asList(callbacks)) { 202 if (Log.isLoggable(TAG, Log.DEBUG)) { 203 Log.d(TAG, "Invoking pdu callback " + callback); 204 } 205 PduLoaded pduLoaded = new PduLoaded(resultPdu, resultSlideshow); 206 callback.onItemLoaded(pduLoaded, resultException); 207 } 208 } 209 // Add the slideshow to the soft cache if the load succeeded 210 if (resultSlideshow != null) { 211 mSlideshowCache.put(mUri, resultSlideshow); 212 } 213 214 mCallbacks.remove(mUri); 215 mPendingTaskUris.remove(mUri); 216 217 if (Log.isLoggable(LogTag.PDU_CACHE, Log.DEBUG)) { 218 Log.d(TAG, "Pdu task for " + mUri + "exiting; " + mPendingTaskUris.size() 219 + " remain"); 220 } 221 } 222 }); 223 } 224 } 225 226 public static class PduLoaded { 227 public final GenericPdu mPdu; 228 public final SlideshowModel mSlideshow; 229 230 public PduLoaded(GenericPdu pdu, SlideshowModel slideshow) { 231 mPdu = pdu; 232 mSlideshow = slideshow; 233 } 234 } 235 } 236