1ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/*
2ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Copyright (C) 2014 The Android Open Source Project
3ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
4ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Licensed under the Apache License, Version 2.0 (the "License");
5ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * you may not use this file except in compliance with the License.
6ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * You may obtain a copy of the License at
7ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
8ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *      http://www.apache.org/licenses/LICENSE-2.0
9ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
10ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Unless required by applicable law or agreed to in writing, software
11ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * distributed under the License is distributed on an "AS IS" BASIS,
12ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * See the License for the specific language governing permissions and
14ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * limitations under the License.
15ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
16ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
173830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingpackage com.android.camera.processing.imagebackend;
18ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
19ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberlingimport android.os.Process;
20ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling
21ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberlingimport com.android.camera.async.AndroidPriorityThread;
22ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport com.android.camera.debug.Log;
23abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Linimport com.android.camera.processing.ProcessingTaskConsumer;
24de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohdeimport com.android.camera.processing.memory.ByteBufferDirectPool;
25de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohdeimport com.android.camera.processing.memory.LruResourcePool;
2630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.session.CaptureSession;
2730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Linimport com.android.camera.util.Size;
280f0329889f69182648fe8f535335e48978d63cc0I-Jong Linimport com.google.common.base.Optional;
290f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin
30de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohdeimport java.nio.ByteBuffer;
31ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.HashMap;
32ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.HashSet;
33ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.Map;
34ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.Set;
35ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executor;
36ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.ExecutorService;
37ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executors;
38ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.ThreadFactory;
39ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.locks.Condition;
40ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.locks.ReentrantLock;
41ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
42ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/**
4330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * This ImageBackend is created for the purpose of creating a task-running
4430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * infrastructure that has two-level of priority and doing the book-keeping to
4530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * keep track of tasks that use Android Images. Android.media.images are
4630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * critical system resources that MUST be properly managed in order to maintain
4730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * camera application performance. Android.media.images are merely Java handles
4830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * to regions of physically contiguous memory used by the camera hardware as a
4930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * destination for imaging data. In general, this physically contiguous memory
5030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * is not counted as an application resource, but as a system resources held by
5130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * the application and does NOT count against the limits of application memory.
5230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * The performance pressures of both computing and memory resources must often
5330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * be prioritized in releasing Android.media.images in a timely manner. In order
5430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * to properly balance these concerns, most image processing requested should be
5530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * routed through this object. This object is also responsible for releasing
5630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * Android.media image as soon as possible, so as not to stall the camera
5730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * hardware subsystem. Image that reserve these images are a subclass of the
5830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * basic Java Runnable with a few conditions placed upon their run()
5930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * implementation:
60ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * <ol>
6130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>The task will try to release the image as early as possible by calling
6230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * the releaseSemaphoreReference as soon as a reference to the original image is
6330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * no longer required.</li>
6430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>A set of tasks that require ImageData must only happen on the first
6530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * receiveImage call. receiveImage must only be called once per image.</li>
6630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>However, the submitted tasks may spawn new tasks via the appendTask with
6730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * any image that have had a task submitted, but NOT released via
6830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * releaseSemaphoreReference.</li>
6930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * <li>Computation that is dependent on multiple images should be written into
7030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * this task framework in a distributed manner where image task can be computed
7130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * independently and join their results to a common shared object.This style of
7230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * implementation allows for the earliest release of Android Images while
7330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * honoring the resources priorities set by this class. See the Lucky shot
7430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * implementation for a concrete example for this shared object and its
7530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * respective task {@link TaskLuckyShotSession} {@link LuckyShotSession}</li>
76ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * </ol>
77abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * To integrate with the ProcessingServiceManager, ImageBackend also signals to
78abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * the ProcessingServiceManager its processing state by enqueuing
790f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin * ImageShadowTasks on each ImageBackend::receiveImage call. These ImageShadow
80abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * tasks have no implementation, but emulate the processing delay by blocking
81abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * until all tasks submitted and spawned by a particular receiveImage call have
82abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * completed their processing. This emulated functionality ensures that other
83abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * ProcessingTasks associated with Lens Blur and Panorama are not processing
84abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * while the ImageBackend is running. Unfairly, the ImageBackend proceeds with
85abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * its own processing regardless of the state of ImageShadowTask.
86abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * ImageShadowTasks that are associated with ImageBackend tasks that have
87abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin * already been completed should return immediately on its process call.
88ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
893c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Linpublic class ImageBackend implements ImageConsumer, ImageTaskManager {
90ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling    private static final Log.Tag TAG = new Log.Tag("ImageBackend");
91ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
92ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected static final int NUM_THREADS_FAST = 2;
93e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin    protected static final int NUM_THREADS_AVERAGE = 2;
94ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected static final int NUM_THREADS_SLOW = 2;
95ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
96ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling    private static final int FAST_THREAD_PRIORITY = Process.THREAD_PRIORITY_DISPLAY;
97ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling    private static final int AVERAGE_THREAD_PRIORITY = Process.THREAD_PRIORITY_DEFAULT
98ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling            + Process.THREAD_PRIORITY_LESS_FAVORABLE;
99ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling    private static final int SLOW_THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND
100ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling            + Process.THREAD_PRIORITY_MORE_FAVORABLE;
101ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling
102de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde    private static final int IMAGE_BACKEND_HARD_REF_POOL_SIZE = 2;
103de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde
104abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected final ProcessingTaskConsumer mProcessingTaskConsumer;
105abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
106abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
107abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Map for TaskImageContainer and the release of ImageProxy Book-keeping
108abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
1093830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    protected final Map<ImageToProcess, ImageReleaseProtocol> mImageSemaphoreMap;
110abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
111abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Map for ImageShadowTask and release of blocking on
112abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * ImageShadowTask::process
113abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
114abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected final Map<CaptureSession, ImageShadowTask> mShadowTaskMap;
115ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
116e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin    // The available threadpools for scheduling
117ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected final ExecutorService mThreadPoolFast;
118e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin    protected final ExecutorService mThreadPoolAverage;
119ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected final ExecutorService mThreadPoolSlow;
120ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
121de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde    private final LruResourcePool<Integer, ByteBuffer> mByteBufferDirectPool;
122ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
12330ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    /**
12430ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * Approximate viewable size (in pixels) for the fast thumbnail in the
12530ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * current UX definition of the product. Note that these values will be the
126abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * minimum size of FAST_THUMBNAIL target for the CONVERT_TO_RGB_PREVIEW
127abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * task.
12830ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     */
1292c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde    private final Size mTinyThumbnailTargetSize;
13030ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin
13130ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    /**
13230ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * A standard viewable size (in pixels) for the filmstrip thumbnail in the
13330ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * current UX definition of the product. Note that this size is the minimum
13430ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     * size for the Preview on the filmstrip associated with
1354dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * COMPRESS_TO_JPEG_AND_WRITE_TO_DISK task.
13630ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin     */
13730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin    private final static Size FILMSTRIP_THUMBNAIL_TARGET_SIZE = new Size(512, 384);
13830ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin
139ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Some invariants to know that we're keeping track of everything
140ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // that reflect the state of mImageSemaphoreMap
141ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private int mOutstandingImageRefs = 0;
142ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
143ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private int mOutstandingImageOpened = 0;
144ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
145ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private int mOutstandingImageClosed = 0;
146ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
147ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Objects that may be registered to this objects events.
148ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private ImageProcessorProxyListener mProxyListener = null;
149ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
150ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Default constructor, values are conservatively targeted to the Nexus 6
1512c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde    public ImageBackend(ProcessingTaskConsumer processingTaskConsumer, int tinyThumbnailSize) {
152ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolFast = Executors.newFixedThreadPool(NUM_THREADS_FAST, new FastThreadFactory());
153e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin        mThreadPoolAverage = Executors.newFixedThreadPool(NUM_THREADS_AVERAGE,
154e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                new AverageThreadFactory());
155ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolSlow = Executors.newFixedThreadPool(NUM_THREADS_SLOW, new SlowThreadFactory());
156de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde        mByteBufferDirectPool = new ByteBufferDirectPool(IMAGE_BACKEND_HARD_REF_POOL_SIZE);
157ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mProxyListener = new ImageProcessorProxyListener();
1583830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        mImageSemaphoreMap = new HashMap<>();
159abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mShadowTaskMap = new HashMap<>();
160abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mProcessingTaskConsumer = processingTaskConsumer;
1612c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde        mTinyThumbnailTargetSize = new Size(tinyThumbnailSize, tinyThumbnailSize);
162ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
163ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
164ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
165ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Direct Injection Constructor for Testing purposes.
166ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
167ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param fastService Service where Tasks of FAST Priority are placed.
168e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin     * @param averageService Service where Tasks of AVERAGE Priority are placed.
169ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param slowService Service where Tasks of SLOW Priority are placed.
170ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param imageProcessorProxyListener iamge proxy listener to be used
171ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
172de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde    public ImageBackend(ExecutorService fastService,
173de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde            ExecutorService averageService,
174e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin            ExecutorService slowService,
175de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde            LruResourcePool<Integer, ByteBuffer> byteBufferDirectPool,
176abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            ImageProcessorProxyListener imageProcessorProxyListener,
177de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde            ProcessingTaskConsumer processingTaskConsumer,
178de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde            int tinyThumbnailSize) {
179ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolFast = fastService;
180e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin        mThreadPoolAverage = averageService;
181ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolSlow = slowService;
182de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde        mByteBufferDirectPool = byteBufferDirectPool;
183ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mProxyListener = imageProcessorProxyListener;
1843830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        mImageSemaphoreMap = new HashMap<>();
185abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mShadowTaskMap = new HashMap<>();
186abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        mProcessingTaskConsumer = processingTaskConsumer;
1872c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde        mTinyThumbnailTargetSize = new Size(tinyThumbnailSize, tinyThumbnailSize);
188ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
189ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
190ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
1913c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * Simple getter for the associated listener object associated with this
19230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * instantiation that handles registration of events listeners.
193ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
194ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return listener proxy that handles events messaging for this object.
195ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
196ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public ImageProcessorProxyListener getProxyListener() {
197ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return mProxyListener;
198ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
199ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
200abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
20130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Wrapper function for all log messages created by this object. Default
20230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * implementation is to send messages to the Android logger. For test
20330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * purposes, this method can be overridden to avoid "Stub!" Runtime
20430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * exceptions in Unit Tests.
205ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
206ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void logWrapper(String message) {
2070745f6eb1a8cbed85761e02839367ed8b2ff6ef6I-Jong Lin        Log.v(TAG, message);
208ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
209ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
210ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
211ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return Number of Image references currently held by this instance
212ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
213ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
214abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    public int getNumberOfReservedOpenImages() {
215ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
216ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // since mOutstandingImageOpened, mOutstandingImageClosed reflect
217ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // the historical state of mImageSemaphoreMap, we need to lock on
218ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // before we return a value.
219ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return mOutstandingImageOpened - mOutstandingImageClosed;
220ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
221ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
222ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
223ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
224abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Returns of the number of receiveImage calls that are currently enqueued
225abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * and/or being processed.
226abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
227abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @return The number of receiveImage calls that are currently enqueued
228abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *         and/or being processed
229abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
230abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    @Override
231abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    public int getNumberOfOutstandingCalls() {
232abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
233abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            return mShadowTaskMap.size();
234abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
235abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
236abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
237abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
23830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Signals the ImageBackend that a tasks has released a reference to the
23930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * image. Imagebackend determines whether all references have been released
24030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * and applies its specified release protocol of closing image and/or
24130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * unblocking the caller. Should ONLY be called by the tasks running on this
24230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * class.
243ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
244ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img the image to be released by the task.
24530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor the executor on which the image close is run. if null,
24630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            image close is run by the calling thread (usually the main
24730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            task thread).
248ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
2493c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin    @Override
2503830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public void releaseSemaphoreReference(final ImageToProcess img, Executor executor) {
251ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
252ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            ImageReleaseProtocol protocol = mImageSemaphoreMap.get(img);
253ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (protocol == null || protocol.getCount() <= 0) {
254ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // That means task implementation has allowed an unbalanced
255ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // semaphore release.
256ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException(
257ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                        "ERROR: Task implementation did NOT balance its release.");
258ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
259ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
260ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // Normal operation from here.
261ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            protocol.addCount(-1);
262ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageRefs--;
263ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            logWrapper("Ref release.  Total refs = " + mOutstandingImageRefs);
264ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (protocol.getCount() == 0) {
265ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Image is ready to be released
266ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Remove the image from the map so that it may be submitted
267ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // again.
268ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                mImageSemaphoreMap.remove(img);
269ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
270ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Conditionally close the image, specified by initial
271ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // receiveImage call
272ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                if (protocol.closeOnRelease) {
273ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    closeImageExecutorSafe(img, executor);
27430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin                    logWrapper("Ref release close.");
275ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                }
276ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
277ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Conditionally signal the blocking thread to go.
278ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                if (protocol.blockUntilRelease) {
279ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    protocol.signal();
280ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                }
281ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            } else {
282ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Image is still being held by other tasks.
283ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // Otherwise, update the semaphore
284ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                mImageSemaphoreMap.put(img, protocol);
285ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
286ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
287ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
288ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
289ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
2903c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * Spawns dependent tasks from internal implementation of a set of tasks. If
2913c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * a dependent task does NOT require the image reference, it should be
2923c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * passed a null pointer as an image reference. In general, this method
2933c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * should be called after the task has completed its own computations, but
2943c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * before it has released its own image reference (via the
2953c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * releaseSemaphoreReference call).
296ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
297ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks The set of tasks to be run
298ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return whether tasks are successfully submitted.
299ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
3003c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin    @Override
3013830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean appendTasks(ImageToProcess img, Set<TaskImageContainer> tasks) {
302ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Make sure that referred images are all the same, if it exists.
303ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // And count how image references need to be kept track of.
304ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        int countImageRefs = numPropagatedImageReferences(img, tasks);
305ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
306ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (img != null) {
307ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // If you're still holding onto the reference, make sure you keep
308ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // count
309ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            incrementSemaphoreReferenceCount(img, countImageRefs);
310ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
311ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
312abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Update the done count on the new tasks.
313abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        incrementTaskDone(tasks);
314abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
315ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        scheduleTasks(tasks);
316ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return true;
317ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
318ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
319ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
320ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Spawns a single dependent task from internal implementation of a task.
321ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
322ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param task The task to be run
323ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @return whether tasks are successfully submitted.
324ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
3253c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin    @Override
3263830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean appendTasks(ImageToProcess img, TaskImageContainer task) {
327ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Set<TaskImageContainer> tasks = new HashSet<TaskImageContainer>(1);
328ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        tasks.add(task);
329ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return appendTasks(img, tasks);
330ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
331ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
332ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
33330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Implements that top-level image single task submission that is defined by
3340f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * the ImageConsumer interface w/o Runnable to executed.
335ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
336ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image required by the task
337ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param task Task to be run
33830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param blockUntilImageRelease If true, call blocks until the object img
33930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is no longer referred by any task. If false, call is
34030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            non-blocking
34130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param closeOnImageRelease If true, images is closed when the object img
34230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is is no longer referred by any task. If false, After an image
34330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is submitted, it should never be submitted again to the
34430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            interface until all tasks and their spawned tasks are
345ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *            finished.
3460f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @return whether jobs were enqueued to the ImageBackend.
347ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
348ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
3493830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean receiveImage(ImageToProcess img, TaskImageContainer task,
3503c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin            boolean blockUntilImageRelease, boolean closeOnImageRelease)
351ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws InterruptedException {
3520f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        return receiveImage(img, task, blockUntilImageRelease, closeOnImageRelease,
3530f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                Optional.<Runnable> absent());
3540f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    }
3550f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin
3560f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    /**
3570f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * Implements that top-level image single task submission that is defined by
3580f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * the ImageConsumer interface.
3590f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *
3600f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param img Image required by the task
3610f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param task Task to be run
3620f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param blockUntilImageRelease If true, call blocks until the object img
3630f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            is no longer referred by any task. If false, call is
3640f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            non-blocking
3650f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param closeOnImageRelease If true, images is closed when the object img
3660f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            is is no longer referred by any task. If false, After an image
3670f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            is submitted, it should never be submitted again to the
3680f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            interface until all tasks and their spawned tasks are
3690f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            finished.
3700f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param runnableWhenDone Optional runnable to be executed when the set of
3710f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            tasks are done.
3720f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @return whether jobs were enqueued to the ImageBackend.
3730f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     */
3740f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    @Override
3750f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    public boolean receiveImage(ImageToProcess img, TaskImageContainer task,
3760f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            boolean blockUntilImageRelease, boolean closeOnImageRelease,
3770f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            Optional<Runnable> runnableWhenDone)
3780f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            throws InterruptedException {
379ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Set<TaskImageContainer> passTasks = new HashSet<TaskImageContainer>(1);
380ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        passTasks.add(task);
3810f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        return receiveImage(img, passTasks, blockUntilImageRelease, closeOnImageRelease,
3820f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                runnableWhenDone);
3830f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    }
3840f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin
3850f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    /**
3860f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * Returns an informational string about the current status of ImageBackend,
3870f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * along with an approximate number of references being held.
3880f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *
3890f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @return an informational string suitable to be dumped into logcat
3900f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     */
3910f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    @Override
3920f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    public String toString() {
3930f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        return "ImageBackend Status BEGIN:\n" +
3940f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                "Shadow Image Map Size = " + mShadowTaskMap.size() + "\n" +
3950f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                "Image Semaphore Map Size = " + mImageSemaphoreMap.size() + "\n" +
3960f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                "OutstandingImageRefs = " + mOutstandingImageRefs + "\n" +
3970f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                "Proxy Listener Map Size = " + mProxyListener.getMapSize() + "\n" +
3980f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                "Proxy Listener = " + mProxyListener.getNumRegisteredListeners() + "\n" +
3990f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                "ImageBackend Status END:\n";
400ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
401ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
402ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
40330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Implements that top-level image single task submission that is defined by
40430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * the ImageConsumer interface.
405ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
406ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image required by the task
407ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks A set of Tasks to be run
40830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param blockUntilImageRelease If true, call blocks until the object img
40930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is no longer referred by any task. If false, call is
41030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            non-blocking
41130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param closeOnImageRelease If true, images is closed when the object img
41230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is is no longer referred by any task. If false, After an image
41330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            is submitted, it should never be submitted again to the
41430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            interface until all tasks and their spawned tasks are
415ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *            finished.
4160f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param runnableWhenDone Optional runnable to be executed when the set of
4170f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            tasks are done.
4180f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @return whether receiveImage succeeded. Generally, only happens when the
4190f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *         image reference is null or the task set is empty.
4200f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @throws InterruptedException occurs when call is set to be blocking and
4210f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *             is interrupted.
422ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
423ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
4243830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean receiveImage(ImageToProcess img, Set<TaskImageContainer> tasks,
4250f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            boolean blockUntilImageRelease, boolean closeOnImageRelease,
4260f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            Optional<Runnable> runnableWhenDone)
427ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws InterruptedException {
428ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
429ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Short circuit if no tasks submitted.
430ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (tasks == null || tasks.size() <= 0) {
431ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return false;
432ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
433ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
434ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (img == null) {
435ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // TODO: Determine whether you need to be so strict at the top level
436ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throw new RuntimeException("ERROR: Initial call must reference valid Image!");
437ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
438ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
439ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Make sure that referred images are all the same, if it exists.
440ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // And count how image references need to be kept track of.
441ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        int countImageRefs = numPropagatedImageReferences(img, tasks);
442ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
443abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Initialize the counters for process-level tasks
4440f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        initializeTaskDone(tasks, runnableWhenDone);
445abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
446ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Set the semaphore, given that the number of tasks that need to be
447ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // scheduled
448ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // and the boolean flags for imaging closing and thread blocking
449ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        ImageReleaseProtocol protocol = setSemaphoreReferenceCount(img, countImageRefs,
450ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                blockUntilImageRelease, closeOnImageRelease);
451ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
452ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Put the tasks on their respective queues.
453ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        scheduleTasks(tasks);
454ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
455ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Implement blocking if required
456ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (protocol.blockUntilRelease) {
457ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            protocol.block();
458ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
459ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
460ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return true;
461ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
462ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
463ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
46430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Implements that top-level image task submission short-cut that is defined
46530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * by the ImageConsumer interface.
466ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
467ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image required by the task
46830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor Executor to run events and image closes, in case of
46930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            control leakage
47030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param processingFlags Magical bit vector that specifies jobs to be run
47130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            After an image is submitted, it should never be submitted
47230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            again to the interface until all tasks and their spawned tasks
47330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            are finished.
4740f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @param imageProcessorListener Optional listener to automatically register
4750f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *            at the job task and unregister after all tasks are done
4760f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @return whether receiveImage succeeded. Generally, only happens when the
4770f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *         image reference is null or the task set is empty.
4780f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @throws InterruptedException occurs when call is set to be blocking and
4790f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     *             is interrupted.
480ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
481ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
4823830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public boolean receiveImage(ImageToProcess img, Executor executor,
4830f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            Set<ImageTaskFlags> processingFlags, CaptureSession session,
4840f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            Optional<ImageProcessorListener> imageProcessorListener)
48530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin            throws InterruptedException {
486ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4870f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        // Uncomment for occasional debugging
4880f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        // Log.v(TAG, toString());
4890f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin
490ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Set<TaskImageContainer> tasksToExecute = new HashSet<TaskImageContainer>();
491ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
492ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (img == null) {
493ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // No data to process, just pure message.
494ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return true;
495ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
496ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
497ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        // Now add the pre-mixed versions of the tasks.
498ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4994dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        if (processingFlags.contains(ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK)) {
5004dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            if (processingFlags.contains(ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW)) {
5014dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                // Request job that creates both filmstrip thumbnail from YUV,
502abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // JPEG compression of the YUV Image, and writes the result to
503abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // disk
5044dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                tasksToExecute.add(new TaskPreviewChainedJpeg(img, executor, this, session,
505de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde                        FILMSTRIP_THUMBNAIL_TARGET_SIZE, mByteBufferDirectPool));
5064dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            } else {
507abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // Request job that only does JPEG compression and writes the
508abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // result to disk
509de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde                tasksToExecute.add(new TaskCompressImageToJpeg(img, executor, this, session,
510de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde                      mByteBufferDirectPool));
5114dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            }
512ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
513ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
5144dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        if (processingFlags.contains(ImageTaskFlags.CONVERT_TO_RGB_PREVIEW)) {
5154dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // Add an additional type of task to the appropriate queue.
51630ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin            tasksToExecute.add(new TaskConvertImageToRGBPreview(img, executor,
51730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                    this, TaskImageContainer.ProcessingPriority.FAST, session,
5182c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde                    mTinyThumbnailTargetSize,
51930ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                    TaskConvertImageToRGBPreview.ThumbnailShape.SQUARE_ASPECT_CIRCULAR_INSET));
520ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
521ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
5220f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        // Wrap the listener in a runnable that will be fired when all tasks are
5230f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        // complete.
5240f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        final Optional<Runnable> runnableOptional;
5250f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        if (imageProcessorListener.isPresent()) {
5260f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            final ImageProcessorListener finalImageProcessorListener = imageProcessorListener.get();
5270f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            Runnable unregisterRunnable = new Runnable() {
5280f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                @Override
5290f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                public void run() {
5300f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                    getProxyListener().unregisterListener(finalImageProcessorListener);
5310f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                }
5320f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            };
5330f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            runnableOptional = Optional.of(unregisterRunnable);
5340f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        } else {
5350f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            runnableOptional = Optional.<Runnable> absent();
5360f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        }
537ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
5380f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        if (receiveImage(img, tasksToExecute,
5390f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                processingFlags.contains(ImageTaskFlags.BLOCK_UNTIL_ALL_TASKS_RELEASE),
5400f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                processingFlags.contains(ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE),
5410f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                runnableOptional)) {
5420f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            if (imageProcessorListener.isPresent()) {
5430f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                getProxyListener().registerListener(imageProcessorListener.get(), img.proxy);
5440f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            }
5450f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            return true;
5460f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        } else {
5470f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            return false;
5480f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        }
549ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
550ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
551ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
552ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Factory functions, in case, you want some shake and bake functionality.
553ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
5543830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public TaskConvertImageToRGBPreview createTaskConvertImageToRGBPreview(
5553830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling            ImageToProcess image, Executor executor, ImageBackend imageBackend,
55630ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin            CaptureSession session, Size targetSize,
55730ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin            TaskConvertImageToRGBPreview.ThumbnailShape thumbnailShape) {
55830ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin        return new TaskConvertImageToRGBPreview(image, executor, imageBackend,
55930ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                TaskImageContainer.ProcessingPriority.FAST, session,
5602c0832daaeca1bf9a0568ab1bfb7492303caaacfPaul Rohde                mTinyThumbnailTargetSize, thumbnailShape);
561ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
562ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
5633830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    public TaskCompressImageToJpeg createTaskCompressImageToJpeg(ImageToProcess image,
56430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin            Executor executor, ImageBackend imageBackend, CaptureSession session) {
565de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde        return new TaskCompressImageToJpeg(image, executor, imageBackend, session,
566de2c5f84c216d91a366d3361be78a8c8710bd65dPaul Rohde              mByteBufferDirectPool);
567ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
568ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
569ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
570ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Blocks and waits for all tasks to complete.
571ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
572ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
573ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void shutdown() {
574ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolSlow.shutdown();
575ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        mThreadPoolFast.shutdown();
576ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
577ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
578ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
579abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * For a given set of starting tasks, initialize the associated sessions
580abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * with a proper blocking semaphore and value of number of tasks to be run.
581abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * For each semaphore, a ImageShadowTask will be instantiated and enqueued
582abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * onto the selected ProcessingSerivceManager.
583abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
584abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param tasks The set of ImageContainer tasks to be run on ImageBackend
585abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
5860f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    protected void initializeTaskDone(Set<TaskImageContainer> tasks,
5870f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            Optional<Runnable> runnableWhenDone) {
588abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        Set<CaptureSession> sessionSet = new HashSet<>();
589abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        Map<CaptureSession, Integer> sessionTaskCount = new HashMap<>();
590abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
591abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Create a set w/ no session duplicates and count them
592abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        for (TaskImageContainer task : tasks) {
593abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            sessionSet.add(task.mSession);
594abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            Integer currentCount = sessionTaskCount.get(task.mSession);
595abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            if (currentCount == null) {
596abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                sessionTaskCount.put(task.mSession, 1);
597abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            } else {
598abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                sessionTaskCount.put(task.mSession, currentCount + 1);
599abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
600abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
601abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
602abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // Create a new blocking semaphore for each set of tasks on a given
603abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // session.
604abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
605abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            for (CaptureSession captureSession : sessionSet) {
606abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                BlockSignalProtocol protocol = new BlockSignalProtocol();
607abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                protocol.setCount(sessionTaskCount.get(captureSession));
6080f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                final ImageShadowTask shadowTask;
6090f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                shadowTask = new ImageShadowTask(protocol, captureSession,
6100f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                            runnableWhenDone);
611abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mShadowTaskMap.put(captureSession, shadowTask);
612abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mProcessingTaskConsumer.enqueueTask(shadowTask);
613abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
614abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
615abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
616abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
617abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
618abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * For ImageBackend tasks that spawn their own tasks, increase the semaphore
619abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * count to take into account the new tasks being spawned.
620abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
621abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param tasks The set of tasks to be spawned.
622abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
623abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    protected void incrementTaskDone(Set<TaskImageContainer> tasks) throws RuntimeException {
624abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        // TODO: Add invariant test so that all sessions are the same.
625abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
626abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            for (TaskImageContainer task : tasks) {
627abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                ImageShadowTask shadowTask = mShadowTaskMap.get(task.mSession);
628abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                if (shadowTask == null) {
629abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                    throw new RuntimeException(
630abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                            "Session NOT previously registered."
631abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                                    + " ImageShadowTask booking-keeping is incorrect.");
632abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                }
633abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                shadowTask.getProtocol().addCount(1);
634abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
635abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
636abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
637abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
638abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
639abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Decrement the semaphore count of the ImageShadowTask. Should be called
640abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * when a task completes its processing in ImageBackend.
641abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
642abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param imageShadowTask The ImageShadow task that contains the blocking
643abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *            semaphore.
6440f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin     * @return whether all the tasks associated with an ImageShadowTask are done
645abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
6460f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin    protected boolean decrementTaskDone(ImageShadowTask imageShadowTask) {
647abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
648abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            int remainingTasks = imageShadowTask.getProtocol().addCount(-1);
649abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            if (remainingTasks == 0) {
650abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mShadowTaskMap.remove(imageShadowTask.getSession());
651abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                imageShadowTask.getProtocol().signal();
6520f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                return true;
6530f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            } else {
6540f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                return false;
655abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
656abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
657abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
658abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
659abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
660abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
66130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Puts the tasks on the specified queue. May be more complicated in the
66230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * future.
663ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
664ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks The set of tasks to be run
665ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
666ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    protected void scheduleTasks(Set<TaskImageContainer> tasks) {
667abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        synchronized (mShadowTaskMap) {
668abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            for (TaskImageContainer task : tasks) {
669abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                ImageShadowTask shadowTask = mShadowTaskMap.get(task.mSession);
670abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                if (shadowTask == null) {
671abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                    throw new IllegalStateException("Scheduling a task with a unknown session.");
672abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                }
673abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // Before scheduling, wrap TaskImageContainer inside of the
674abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // TaskDoneWrapper to add
675abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                // instrumentation for managing ImageShadowTasks
676e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                switch (task.getProcessingPriority()) {
677e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                    case FAST:
678e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        mThreadPoolFast.execute(new TaskDoneWrapper(this, shadowTask, task));
679e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        break;
680e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                    case AVERAGE:
681e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        mThreadPoolAverage.execute(new TaskDoneWrapper(this, shadowTask, task));
682e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        break;
683e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                    case SLOW:
684e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        mThreadPoolSlow.execute(new TaskDoneWrapper(this, shadowTask, task));
685e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        break;
686e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                    default:
687e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        mThreadPoolSlow.execute(new TaskDoneWrapper(this, shadowTask, task));
688e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin                        break;
689abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                }
690ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
691ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
692ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
693ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
694ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
695ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * Initializes the semaphore count for the image
696ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
69730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @return The protocol object that keeps tracks of the image reference
69830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *         count and actions to be taken on release.
699ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
7003830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    protected ImageReleaseProtocol setSemaphoreReferenceCount(ImageToProcess img, int count,
701ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            boolean blockUntilRelease, boolean closeOnRelease) throws RuntimeException {
702ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
703ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (mImageSemaphoreMap.get(img) != null) {
704ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException(
705abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                        "ERROR: Rewriting of Semaphore Lock."
706abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                                + "  Image references may not freed properly");
707ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
708ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
709ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // Create the new booking-keeping object.
710ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            ImageReleaseProtocol protocol = new ImageReleaseProtocol(blockUntilRelease,
711ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    closeOnRelease);
712abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            protocol.setCount(count);
713ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
714ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mImageSemaphoreMap.put(img, protocol);
715ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageRefs += count;
716ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageOpened++;
717ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            logWrapper("Received an opened image: " + mOutstandingImageOpened + "/"
718ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    + mOutstandingImageClosed);
719ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            logWrapper("Setting an image reference count of " + count + "   Total refs = "
720ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    + mOutstandingImageRefs);
721ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return protocol;
722ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
723ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
724ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
725ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
72630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Increments the semaphore count for the image. Should ONLY be internally
72730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * via appendTasks by internal tasks. Otherwise, image references could get
72830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * out of whack.
729abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *
730abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param img The Image associated with the set of tasks running on it.
731abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @param count The number of tasks to be added
732abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * @throws RuntimeException Indicates image Closing Bookkeeping is screwed
733abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     *             up.
734ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
7353830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    protected void incrementSemaphoreReferenceCount(ImageToProcess img, int count)
736ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws RuntimeException {
737ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        synchronized (mImageSemaphoreMap) {
738ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            ImageReleaseProtocol protocol = mImageSemaphoreMap.get(img);
739ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            if (mImageSemaphoreMap.get(img) == null) {
740ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException(
741ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                        "Image Reference has already been released or has never been held.");
742ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
743ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
744ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            protocol.addCount(count);
745ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mImageSemaphoreMap.put(img, protocol);
746ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
747ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mOutstandingImageRefs += count;
748ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
749ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
750ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
751ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
75230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Close an Image with a executor if it's available and does the proper
75330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * booking keeping on the object.
754ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
755ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param img Image to be closed
75630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor Executor to be used, if executor is null, the close is
75730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *            run on the task thread
758ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
7593830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    private void closeImageExecutorSafe(final ImageToProcess img, Executor executor) {
760ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        Runnable closeTask = new Runnable() {
761ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            @Override
762ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            public void run() {
7633830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling                img.proxy.close();
764ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                mOutstandingImageClosed++;
765ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                logWrapper("Release of image occurred.  Good fun. " + "Total Images Open/Closed = "
766ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                        + mOutstandingImageOpened + "/" + mOutstandingImageClosed);
767ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
768ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        };
769ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        if (executor == null) {
770ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            // Just run it on the main thread.
771ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            closeTask.run();
772ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        } else {
773ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            executor.execute(closeTask);
774ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
775ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
776ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
777ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
77830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Calculates the number of new Image references in a set of dependent
77930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * tasks. Checks to make sure no new image references are being introduced.
780ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     *
781ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     * @param tasks The set of dependent tasks to be run
782ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
7833830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    private int numPropagatedImageReferences(ImageToProcess img, Set<TaskImageContainer> tasks)
784ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            throws RuntimeException {
785ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        int countImageRefs = 0;
786ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        for (TaskImageContainer task : tasks) {
7873830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling            if (task.mImage != null && task.mImage != img) {
788ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw new RuntimeException("ERROR:  Spawned tasks cannot reference new images!");
789ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
790ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
7913830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling            if (task.mImage != null) {
792ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                countImageRefs++;
793ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
794ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
795ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
796ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        return countImageRefs;
797ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
798ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
799ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    /**
800abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Simple wrapper task to instrument when tasks ends so that ImageBackend
801abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * can fire events when set of tasks created by a ReceiveImage call have all
802abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * completed.
803ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin     */
804abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    private class TaskDoneWrapper implements Runnable {
805abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        private final ImageBackend mImageBackend;
806abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        private final ImageShadowTask mImageShadowTask;
8070f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin        private final TaskImageContainer mWrappedTask;
808abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
809abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        /**
810abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * Constructor
8110f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin         *
812abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * @param imageBackend ImageBackend that the task is running on
813abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         * @param imageShadowTask ImageShadowTask that is blocking on the
814abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         *            completion of the task
8150f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin         * @param wrappedTask The task to be run w/o instrumentation
816abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         */
817abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public TaskDoneWrapper(ImageBackend imageBackend, ImageShadowTask imageShadowTask,
8180f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                TaskImageContainer wrappedTask) {
819abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mImageBackend = imageBackend;
820abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            mImageShadowTask = imageShadowTask;
8210f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            mWrappedTask = wrappedTask;
822abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
823ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
824abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        /**
8250f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin         * Adds instrumentation that runs when a TaskImageContainer completes.
826abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin         */
827abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        @Override
828abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public void run() {
8290f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            mWrappedTask.run();
830abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            // Decrement count
8310f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            if (mImageBackend.decrementTaskDone(mImageShadowTask)) {
8320f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                // If you're the last one...
8330f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                Runnable doneRunnable = mImageShadowTask.getRunnableWhenDone();
8340f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                if (doneRunnable != null) {
8350f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                    if (mWrappedTask.mExecutor == null) {
8360f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                        doneRunnable.run();
8370f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                    } else {
8380f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                        mWrappedTask.mExecutor.execute(doneRunnable);
8390f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                    }
8400f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin                }
8410f0329889f69182648fe8f535335e48978d63cc0I-Jong Lin            }
842abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
843abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
844ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
845abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
846abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * Encapsulates all synchronization for semaphore signaling and blocking.
847abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
848abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    static public class BlockSignalProtocol {
849ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        private int count;
850ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
851ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        private final ReentrantLock mLock = new ReentrantLock();
852ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
853ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        private Condition mSignal;
854ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
855ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public void setCount(int value) {
856ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
857ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            count = value;
858ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.unlock();
859ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
860ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
861ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public int getCount() {
862ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            int value;
863ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
864ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            value = count;
865ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.unlock();
866ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return value;
867ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
868ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
869abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public int addCount(int value) {
870ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
871abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            try {
872abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                count += value;
873abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                return count;
874abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            } finally {
875abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin                mLock.unlock();
876abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            }
877ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
878ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
879abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        BlockSignalProtocol() {
880ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            count = 0;
881ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mSignal = mLock.newCondition();
882ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
883ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
884ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public void block() throws InterruptedException {
885ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
886ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            try {
887ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                while (count != 0) {
888ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    // Spin to deal with spurious signals.
889ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                    mSignal.await();
890ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                }
891ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            } catch (InterruptedException e) {
892ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                // TODO: on interruption, figure out what to do.
893ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin                throw (e);
8943c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin            } finally {
8953c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin                mLock.unlock();
896ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            }
897ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
898ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
899ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public void signal() {
900ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.lock();
901ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mSignal.signal();
902ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            mLock.unlock();
903ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
904ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
905ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
906ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
907abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    /**
908abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * A simple tuple class to keep track of image reference, and whether to
909abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * block and/or close on final image release. Instantiated on every task
910abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     * submission call.
911abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin     */
912abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    static public class ImageReleaseProtocol extends BlockSignalProtocol {
913abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
914abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public final boolean blockUntilRelease;
915abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
916abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        public final boolean closeOnRelease;
917abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
918abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        ImageReleaseProtocol(boolean block, boolean close) {
919abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            super();
920abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            blockUntilRelease = block;
921abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin            closeOnRelease = close;
922abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin        }
923abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
924abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin    }
925abac2a23443f13b58951d88bf4b972f31a562e3dI-Jong Lin
926ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    // Thread factories for a default constructor
927ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private class FastThreadFactory implements ThreadFactory {
928ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling        @Override
929ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public Thread newThread(Runnable r) {
930ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling            Thread t = new AndroidPriorityThread(FAST_THREAD_PRIORITY, r);
931ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return t;
932ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
933ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
934ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
935e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin    private class AverageThreadFactory implements ThreadFactory {
936ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling        @Override
937e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin        public Thread newThread(Runnable r) {
938ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling            Thread t = new AndroidPriorityThread(AVERAGE_THREAD_PRIORITY, r);
939e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin            return t;
940e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin        }
941e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin    }
942e1bfecfb5efaa29419705b35706decece4985126I-Jong Lin
943ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    private class SlowThreadFactory implements ThreadFactory {
944ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling        @Override
945ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        public Thread newThread(Runnable r) {
946ae474d0b2967b3995a4e1e334d6689b0cd9a38c9Sascha Haeberling            Thread t = new AndroidPriorityThread(SLOW_THREAD_PRIORITY, r);
947ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin            return t;
948ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        }
949ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
950ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
951ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin}
952