1666ea1b28a76aeba74744148b15099254d918671Owen Lin/*
2666ea1b28a76aeba74744148b15099254d918671Owen Lin * Copyright (C) 2009 The Android Open Source Project
3666ea1b28a76aeba74744148b15099254d918671Owen Lin *
4666ea1b28a76aeba74744148b15099254d918671Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5666ea1b28a76aeba74744148b15099254d918671Owen Lin * you may not use this file except in compliance with the License.
6666ea1b28a76aeba74744148b15099254d918671Owen Lin * You may obtain a copy of the License at
7666ea1b28a76aeba74744148b15099254d918671Owen Lin *
8666ea1b28a76aeba74744148b15099254d918671Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9666ea1b28a76aeba74744148b15099254d918671Owen Lin *
10666ea1b28a76aeba74744148b15099254d918671Owen Lin * Unless required by applicable law or agreed to in writing, software
11666ea1b28a76aeba74744148b15099254d918671Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12666ea1b28a76aeba74744148b15099254d918671Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13666ea1b28a76aeba74744148b15099254d918671Owen Lin * See the License for the specific language governing permissions and
14666ea1b28a76aeba74744148b15099254d918671Owen Lin * limitations under the License.
15666ea1b28a76aeba74744148b15099254d918671Owen Lin */
16666ea1b28a76aeba74744148b15099254d918671Owen Lin
17666ea1b28a76aeba74744148b15099254d918671Owen Linpackage com.android.camera;
18666ea1b28a76aeba74744148b15099254d918671Owen Lin
198aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Linimport android.content.ContentResolver;
20666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Bitmap;
21666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.BitmapFactory;
228aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Linimport android.provider.MediaStore.Images;
238aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Linimport android.provider.MediaStore.Video;
24666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.Log;
25666ea1b28a76aeba74744148b15099254d918671Owen Lin
26666ea1b28a76aeba74744148b15099254d918671Owen Linimport java.io.FileDescriptor;
27666ea1b28a76aeba74744148b15099254d918671Owen Linimport java.util.WeakHashMap;
28666ea1b28a76aeba74744148b15099254d918671Owen Lin
29666ea1b28a76aeba74744148b15099254d918671Owen Lin/**
30666ea1b28a76aeba74744148b15099254d918671Owen Lin * This class provides several utilities to cancel bitmap decoding.
31666ea1b28a76aeba74744148b15099254d918671Owen Lin *
32666ea1b28a76aeba74744148b15099254d918671Owen Lin * The function decodeFileDescriptor() is used to decode a bitmap. During
33666ea1b28a76aeba74744148b15099254d918671Owen Lin * decoding if another thread wants to cancel it, it calls the function
34666ea1b28a76aeba74744148b15099254d918671Owen Lin * cancelThreadDecoding() specifying the Thread which is in decoding.
35666ea1b28a76aeba74744148b15099254d918671Owen Lin *
36666ea1b28a76aeba74744148b15099254d918671Owen Lin * cancelThreadDecoding() is sticky until allowThreadDecoding() is called.
37666ea1b28a76aeba74744148b15099254d918671Owen Lin */
38666ea1b28a76aeba74744148b15099254d918671Owen Linpublic class BitmapManager {
39666ea1b28a76aeba74744148b15099254d918671Owen Lin    private static final String TAG = "BitmapManager";
40666ea1b28a76aeba74744148b15099254d918671Owen Lin    private static enum State {CANCEL, ALLOW}
41666ea1b28a76aeba74744148b15099254d918671Owen Lin    private static class ThreadStatus {
42666ea1b28a76aeba74744148b15099254d918671Owen Lin        public State mState = State.ALLOW;
43666ea1b28a76aeba74744148b15099254d918671Owen Lin        public BitmapFactory.Options mOptions;
448aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        public boolean mThumbRequesting;
45666ea1b28a76aeba74744148b15099254d918671Owen Lin        @Override
46666ea1b28a76aeba74744148b15099254d918671Owen Lin        public String toString() {
47666ea1b28a76aeba74744148b15099254d918671Owen Lin            String s;
48666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (mState == State.CANCEL) {
49666ea1b28a76aeba74744148b15099254d918671Owen Lin                s = "Cancel";
50666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (mState == State.ALLOW) {
51666ea1b28a76aeba74744148b15099254d918671Owen Lin                s = "Allow";
52666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else {
53666ea1b28a76aeba74744148b15099254d918671Owen Lin                s = "?";
54666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
55666ea1b28a76aeba74744148b15099254d918671Owen Lin            s = "thread state = " + s + ", options = " + mOptions;
56666ea1b28a76aeba74744148b15099254d918671Owen Lin            return s;
57666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
58666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
59666ea1b28a76aeba74744148b15099254d918671Owen Lin
60666ea1b28a76aeba74744148b15099254d918671Owen Lin    private final WeakHashMap<Thread, ThreadStatus> mThreadStatus =
61666ea1b28a76aeba74744148b15099254d918671Owen Lin            new WeakHashMap<Thread, ThreadStatus>();
62666ea1b28a76aeba74744148b15099254d918671Owen Lin
63666ea1b28a76aeba74744148b15099254d918671Owen Lin    private static BitmapManager sManager = null;
64666ea1b28a76aeba74744148b15099254d918671Owen Lin
65666ea1b28a76aeba74744148b15099254d918671Owen Lin    private BitmapManager() {
66666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
67666ea1b28a76aeba74744148b15099254d918671Owen Lin
68666ea1b28a76aeba74744148b15099254d918671Owen Lin    /**
69666ea1b28a76aeba74744148b15099254d918671Owen Lin     * Get thread status and create one if specified.
70666ea1b28a76aeba74744148b15099254d918671Owen Lin     */
71666ea1b28a76aeba74744148b15099254d918671Owen Lin    private synchronized ThreadStatus getOrCreateThreadStatus(Thread t) {
72666ea1b28a76aeba74744148b15099254d918671Owen Lin        ThreadStatus status = mThreadStatus.get(t);
73666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (status == null) {
74666ea1b28a76aeba74744148b15099254d918671Owen Lin            status = new ThreadStatus();
75666ea1b28a76aeba74744148b15099254d918671Owen Lin            mThreadStatus.put(t, status);
76666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
77666ea1b28a76aeba74744148b15099254d918671Owen Lin        return status;
78666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
79666ea1b28a76aeba74744148b15099254d918671Owen Lin
80666ea1b28a76aeba74744148b15099254d918671Owen Lin    /**
81666ea1b28a76aeba74744148b15099254d918671Owen Lin     * The following three methods are used to keep track of
82666ea1b28a76aeba74744148b15099254d918671Owen Lin     * BitmapFaction.Options used for decoding and cancelling.
83666ea1b28a76aeba74744148b15099254d918671Owen Lin     */
84666ea1b28a76aeba74744148b15099254d918671Owen Lin    private synchronized void setDecodingOptions(Thread t,
85666ea1b28a76aeba74744148b15099254d918671Owen Lin            BitmapFactory.Options options) {
86666ea1b28a76aeba74744148b15099254d918671Owen Lin        getOrCreateThreadStatus(t).mOptions = options;
87666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
88666ea1b28a76aeba74744148b15099254d918671Owen Lin
89666ea1b28a76aeba74744148b15099254d918671Owen Lin    synchronized void removeDecodingOptions(Thread t) {
90666ea1b28a76aeba74744148b15099254d918671Owen Lin        ThreadStatus status = mThreadStatus.get(t);
91666ea1b28a76aeba74744148b15099254d918671Owen Lin        status.mOptions = null;
92666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
93666ea1b28a76aeba74744148b15099254d918671Owen Lin
94666ea1b28a76aeba74744148b15099254d918671Owen Lin    /**
95666ea1b28a76aeba74744148b15099254d918671Owen Lin     * The following three methods are used to keep track of which thread
96666ea1b28a76aeba74744148b15099254d918671Owen Lin     * is being disabled for bitmap decoding.
97666ea1b28a76aeba74744148b15099254d918671Owen Lin     */
98666ea1b28a76aeba74744148b15099254d918671Owen Lin    public synchronized boolean canThreadDecoding(Thread t) {
99666ea1b28a76aeba74744148b15099254d918671Owen Lin        ThreadStatus status = mThreadStatus.get(t);
100666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (status == null) {
101666ea1b28a76aeba74744148b15099254d918671Owen Lin            // allow decoding by default
102666ea1b28a76aeba74744148b15099254d918671Owen Lin            return true;
103666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
104666ea1b28a76aeba74744148b15099254d918671Owen Lin
105666ea1b28a76aeba74744148b15099254d918671Owen Lin        boolean result = (status.mState != State.CANCEL);
106666ea1b28a76aeba74744148b15099254d918671Owen Lin        return result;
107666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
108666ea1b28a76aeba74744148b15099254d918671Owen Lin
109666ea1b28a76aeba74744148b15099254d918671Owen Lin    public synchronized void allowThreadDecoding(Thread t) {
110666ea1b28a76aeba74744148b15099254d918671Owen Lin        getOrCreateThreadStatus(t).mState = State.ALLOW;
111666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
112666ea1b28a76aeba74744148b15099254d918671Owen Lin
1138aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin    public synchronized void cancelThreadDecoding(Thread t, ContentResolver cr) {
114666ea1b28a76aeba74744148b15099254d918671Owen Lin        ThreadStatus status = getOrCreateThreadStatus(t);
115666ea1b28a76aeba74744148b15099254d918671Owen Lin        status.mState = State.CANCEL;
116666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (status.mOptions != null) {
117666ea1b28a76aeba74744148b15099254d918671Owen Lin            status.mOptions.requestCancelDecode();
118666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
119666ea1b28a76aeba74744148b15099254d918671Owen Lin
120666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Wake up threads in waiting list
121666ea1b28a76aeba74744148b15099254d918671Owen Lin        notifyAll();
1228aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin
1238aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        // Since our cancel request can arrive MediaProvider earlier than getThumbnail request,
1248aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        // we use mThumbRequesting flag to make sure our request does cancel the request.
1258aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        try {
1268aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            synchronized (status) {
1278aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                while (status.mThumbRequesting) {
1288aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                    Images.Thumbnails.cancelThumbnailRequest(cr, -1, t.getId());
1298aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                    Video.Thumbnails.cancelThumbnailRequest(cr, -1, t.getId());
1308aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                    status.wait(200);
1318aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                }
1328aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            }
1338aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        } catch (InterruptedException ex) {
1348aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            // ignore it.
1358aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        }
1368aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin    }
1378aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin
1388aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin    public Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
1398aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            BitmapFactory.Options options, boolean isVideo) {
1408aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        Thread t = Thread.currentThread();
1418aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        ThreadStatus status = getOrCreateThreadStatus(t);
1428aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin
1438aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        if (!canThreadDecoding(t)) {
1448aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            Log.d(TAG, "Thread " + t + " is not allowed to decode.");
1458aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            return null;
1468aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        }
1478aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin
1488aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        try {
1498aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            synchronized (status) {
1508aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                status.mThumbRequesting = true;
1518aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            }
1528aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            if (isVideo) {
1538aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                return Video.Thumbnails.getThumbnail(cr, origId, t.getId(),
1548aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                        kind, null);
1558aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            } else {
1568aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                return Images.Thumbnails.getThumbnail(cr, origId, t.getId(),
1578aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                        kind, null);
1588aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            }
1598aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        } finally {
1608aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            synchronized (status) {
1618aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                status.mThumbRequesting = false;
1628aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin                status.notifyAll();
1638aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin            }
1648aa3fbcf9a5a0d11acd48861f5167073e3554980Owen Lin        }
165666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
166666ea1b28a76aeba74744148b15099254d918671Owen Lin
167666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static synchronized BitmapManager instance() {
168666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (sManager == null) {
169666ea1b28a76aeba74744148b15099254d918671Owen Lin            sManager = new BitmapManager();
170666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
171666ea1b28a76aeba74744148b15099254d918671Owen Lin        return sManager;
172666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
173666ea1b28a76aeba74744148b15099254d918671Owen Lin
174666ea1b28a76aeba74744148b15099254d918671Owen Lin    /**
175666ea1b28a76aeba74744148b15099254d918671Owen Lin     * The real place to delegate bitmap decoding to BitmapFactory.
176666ea1b28a76aeba74744148b15099254d918671Owen Lin     */
177666ea1b28a76aeba74744148b15099254d918671Owen Lin    public Bitmap decodeFileDescriptor(FileDescriptor fd,
178666ea1b28a76aeba74744148b15099254d918671Owen Lin                                       BitmapFactory.Options options) {
179666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (options.mCancel) {
180666ea1b28a76aeba74744148b15099254d918671Owen Lin            return null;
181666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
182666ea1b28a76aeba74744148b15099254d918671Owen Lin
183666ea1b28a76aeba74744148b15099254d918671Owen Lin        Thread thread = Thread.currentThread();
184666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (!canThreadDecoding(thread)) {
185666ea1b28a76aeba74744148b15099254d918671Owen Lin            Log.d(TAG, "Thread " + thread + " is not allowed to decode.");
186666ea1b28a76aeba74744148b15099254d918671Owen Lin            return null;
187666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
188666ea1b28a76aeba74744148b15099254d918671Owen Lin
189666ea1b28a76aeba74744148b15099254d918671Owen Lin        setDecodingOptions(thread, options);
190666ea1b28a76aeba74744148b15099254d918671Owen Lin        Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, options);
191666ea1b28a76aeba74744148b15099254d918671Owen Lin
192666ea1b28a76aeba74744148b15099254d918671Owen Lin        removeDecodingOptions(thread);
193666ea1b28a76aeba74744148b15099254d918671Owen Lin        return b;
194666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
195666ea1b28a76aeba74744148b15099254d918671Owen Lin}
196