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 public void cancel() { 114 cancelCallback(callback); 115 } 116 public boolean isDone() { 117 return false; 118 } 119 }; 120 } 121 122 @Override 123 public void clear() { 124 super.clear(); 125 126 synchronized(mPduCache) { 127 mPduCache.purgeAll(); 128 } 129 mSlideshowCache.clear(); 130 } 131 132 public void removePdu(Uri uri) { 133 if (Log.isLoggable(TAG, Log.DEBUG)) { 134 Log.d(TAG, "removePdu: " + uri); 135 } 136 synchronized(mPduCache) { 137 mPduCache.purge(uri); 138 } 139 mSlideshowCache.remove(uri); 140 } 141 142 public String getTag() { 143 return TAG; 144 } 145 146 public class PduTask implements Runnable { 147 private final Uri mUri; 148 private final boolean mRequestSlideshow; 149 150 public PduTask(Uri uri, boolean requestSlideshow) { 151 if (uri == null) { 152 throw new NullPointerException(); 153 } 154 mUri = uri; 155 mRequestSlideshow = requestSlideshow; 156 } 157 158 /** {@inheritDoc} */ 159 public void run() { 160 if (DEBUG_DISABLE_PDUS) { 161 return; 162 } 163 if (DEBUG_LONG_WAIT) { 164 try { 165 Thread.sleep(10000); 166 } catch (InterruptedException e) { 167 } 168 } 169 GenericPdu pdu = null; 170 SlideshowModel slideshow = null; 171 Throwable exception = null; 172 try { 173 pdu = mPduPersister.load(mUri); 174 if (pdu != null && mRequestSlideshow) { 175 slideshow = SlideshowModel.createFromPduBody(mContext, 176 ((MultimediaMessagePdu)pdu).getBody()); 177 } 178 } catch (final MmsException e) { 179 Log.e(TAG, "MmsException loading uri: " + mUri, e); 180 exception = e; 181 } 182 final GenericPdu resultPdu = pdu; 183 final SlideshowModel resultSlideshow = slideshow; 184 final Throwable resultException = exception; 185 mCallbackHandler.post(new Runnable() { 186 public void run() { 187 final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri); 188 if (callbacks != null) { 189 // Make a copy so that the callback can unregister itself 190 for (final ItemLoadedCallback<PduLoaded> callback : asList(callbacks)) { 191 if (Log.isLoggable(TAG, Log.DEBUG)) { 192 Log.d(TAG, "Invoking pdu callback " + callback); 193 } 194 PduLoaded pduLoaded = new PduLoaded(resultPdu, resultSlideshow); 195 callback.onItemLoaded(pduLoaded, resultException); 196 } 197 } 198 // Add the slideshow to the soft cache if the load succeeded 199 if (resultSlideshow != null) { 200 mSlideshowCache.put(mUri, resultSlideshow); 201 } 202 203 mCallbacks.remove(mUri); 204 mPendingTaskUris.remove(mUri); 205 206 if (Log.isLoggable(LogTag.PDU_CACHE, Log.DEBUG)) { 207 Log.d(TAG, "Pdu task for " + mUri + "exiting; " + mPendingTaskUris.size() 208 + " remain"); 209 } 210 } 211 }); 212 } 213 } 214 215 public static class PduLoaded { 216 public final GenericPdu mPdu; 217 public final SlideshowModel mSlideshow; 218 219 public PduLoaded(GenericPdu pdu, SlideshowModel slideshow) { 220 mPdu = pdu; 221 mSlideshow = slideshow; 222 } 223 } 224 } 225