PduLoaderManager.java revision 79bf6f70c54d08dc9c3481b8461a3a46a3cefb83
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
17package com.android.mms.util;
18
19import java.util.Set;
20
21import android.content.Context;
22import android.net.Uri;
23import android.util.Log;
24
25import com.android.mms.LogTag;
26import com.android.mms.model.SlideshowModel;
27import com.google.android.mms.MmsException;
28import com.google.android.mms.pdu.GenericPdu;
29import com.google.android.mms.pdu.MultimediaMessagePdu;
30import com.google.android.mms.pdu.PduPersister;
31import com.google.android.mms.util.PduCache;
32import 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 */
53public class PduLoaderManager extends BackgroundLoaderManager {
54    private static final String TAG = "Mms:PduLoaderManager";
55
56    private static final boolean DEBUG_DISABLE_PDUS = false;
57    private static final boolean DEBUG_LONG_WAIT = false;
58
59    private static PduCache mPduCache;
60    private final PduPersister mPduPersister;
61    private final SimpleCache<Uri, SlideshowModel> mSlideshowCache;
62    private final Context mContext;
63
64    public PduLoaderManager(final Context context) {
65        super(context);
66
67        mSlideshowCache = new SimpleCache<Uri, SlideshowModel>(8, 48, 0.75f);
68        mPduCache = PduCache.getInstance();
69        mPduPersister = PduPersister.getPduPersister(context);
70        mContext = context;
71    }
72
73    public ItemLoadedFuture getPdu(Uri uri, boolean requestSlideshow,
74            final ItemLoadedCallback<PduLoaded> callback) {
75        if (uri == null) {
76            throw new NullPointerException();
77        }
78
79        PduCacheEntry cacheEntry = null;
80        synchronized(mPduCache) {
81            if (!mPduCache.isUpdating(uri)) {
82                cacheEntry = mPduCache.get(uri);
83            }
84        }
85        final SlideshowModel slideshow = requestSlideshow ? mSlideshowCache.get(uri) : null;
86
87        final boolean slideshowExists = (!requestSlideshow || slideshow != null);
88        final boolean pduExists = (cacheEntry != null && cacheEntry.getPdu() != null);
89        final boolean taskExists = mPendingTaskUris.contains(uri);
90        final boolean newTaskRequired = (!pduExists || !slideshowExists) && !taskExists;
91        final boolean callbackRequired = (callback != null);
92
93        if (pduExists && slideshowExists) {
94            if (callbackRequired) {
95                PduLoaded pduLoaded = new PduLoaded(cacheEntry.getPdu(), slideshow);
96                callback.onItemLoaded(pduLoaded, null);
97            }
98            return new NullItemLoadedFuture();
99        }
100
101        if (callbackRequired) {
102            addCallback(uri, callback);
103        }
104
105        if (newTaskRequired) {
106            mPendingTaskUris.add(uri);
107            Runnable task = new PduTask(uri, requestSlideshow);
108            mExecutor.execute(task);
109        }
110        return new ItemLoadedFuture() {
111            public void cancel() {
112                cancelCallback(callback);
113            }
114            public boolean isDone() {
115                return false;
116            }
117        };
118    }
119
120    @Override
121    public void clear() {
122        super.clear();
123
124        PduCache.getInstance().purgeAll();
125        mSlideshowCache.clear();
126    }
127
128    public String getTag() {
129        return TAG;
130    }
131
132    public class PduTask implements Runnable {
133        private final Uri mUri;
134        private final boolean mRequestSlideshow;
135
136        public PduTask(Uri uri, boolean requestSlideshow) {
137            if (uri == null) {
138                throw new NullPointerException();
139            }
140            mUri = uri;
141            mRequestSlideshow = requestSlideshow;
142        }
143
144        /** {@inheritDoc} */
145        public void run() {
146            if (DEBUG_DISABLE_PDUS) {
147                return;
148            }
149            if (DEBUG_LONG_WAIT) {
150                try {
151                    Thread.sleep(10000);
152                } catch (InterruptedException e) {
153                }
154            }
155            GenericPdu pdu = null;
156            SlideshowModel slideshow = null;
157            Throwable exception = null;
158            try {
159                pdu = mPduPersister.load(mUri);
160                if (pdu != null && mRequestSlideshow) {
161                    slideshow = SlideshowModel.createFromPduBody(mContext,
162                            ((MultimediaMessagePdu)pdu).getBody());
163                }
164            } catch (final MmsException e) {
165                Log.e(TAG, "MmsException loading uri: " + mUri, e);
166                exception = e;
167            }
168            final GenericPdu resultPdu = pdu;
169            final SlideshowModel resultSlideshow = slideshow;
170            final Throwable resultException = exception;
171            mCallbackHandler.post(new Runnable() {
172                public void run() {
173                    final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri);
174                    if (callbacks != null) {
175                        // Make a copy so that the callback can unregister itself
176                        for (final ItemLoadedCallback<PduLoaded> callback : asList(callbacks)) {
177                            if (Log.isLoggable(TAG, Log.DEBUG)) {
178                                Log.d(TAG, "Invoking pdu callback " + callback);
179                            }
180                            PduLoaded pduLoaded = new PduLoaded(resultPdu, resultSlideshow);
181                            callback.onItemLoaded(pduLoaded, resultException);
182                        }
183                    }
184                    // Add the slideshow to the soft cache if the load succeeded
185                    if (resultSlideshow != null) {
186                        mSlideshowCache.put(mUri, resultSlideshow);
187                    }
188
189                    mCallbacks.remove(mUri);
190                    mPendingTaskUris.remove(mUri);
191
192                    if (Log.isLoggable(LogTag.PDU_CACHE, Log.DEBUG)) {
193                        Log.d(TAG, "Pdu task for " + mUri + "exiting; " + mPendingTaskUris.size()
194                                + " remain");
195                    }
196                }
197            });
198        }
199    }
200
201    public static class PduLoaded {
202        public final GenericPdu mPdu;
203        public final SlideshowModel mSlideshow;
204
205        public PduLoaded(GenericPdu pdu, SlideshowModel slideshow) {
206            mPdu = pdu;
207            mSlideshow = slideshow;
208        }
209    }
210}
211