1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.legacy;
18
19import android.os.ConditionVariable;
20import android.os.Handler;
21import android.os.HandlerThread;
22import android.os.Looper;
23import android.os.MessageQueue;
24
25public class RequestHandlerThread extends HandlerThread {
26
27    /**
28     * Ensure that the MessageQueue's idle handler gets run by poking the message queue;
29     * normally if the message queue is already idle, the idle handler won't get invoked.
30     *
31     * <p>Users of this handler thread should ignore this message.</p>
32     */
33    public final static int MSG_POKE_IDLE_HANDLER = -1;
34
35    private final ConditionVariable mStarted = new ConditionVariable(false);
36    private final ConditionVariable mIdle = new ConditionVariable(true);
37    private Handler.Callback mCallback;
38    private volatile Handler mHandler;
39
40    public RequestHandlerThread(String name, Handler.Callback callback) {
41        super(name, Thread.MAX_PRIORITY);
42        mCallback = callback;
43    }
44
45    @Override
46    protected void onLooperPrepared() {
47        mHandler = new Handler(getLooper(), mCallback);
48        mStarted.open();
49    }
50
51    // Blocks until thread has started
52    public void waitUntilStarted() {
53        mStarted.block();
54    }
55
56    // May return null if the handler is not set up yet.
57    public Handler getHandler() {
58        return mHandler;
59    }
60
61    // Blocks until thread has started
62    public Handler waitAndGetHandler() {
63        waitUntilStarted();
64        return getHandler();
65    }
66
67    // Atomic multi-type message existence check
68    public boolean hasAnyMessages(int[] what) {
69        synchronized (mHandler.getLooper().getQueue()) {
70            for (int i : what) {
71                if (mHandler.hasMessages(i)) {
72                    return true;
73                }
74            }
75        }
76        return false;
77    }
78
79    // Atomic multi-type message remove
80    public void removeMessages(int[] what) {
81        synchronized (mHandler.getLooper().getQueue()) {
82            for (int i : what) {
83                mHandler.removeMessages(i);
84            }
85        }
86    }
87
88    private final MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
89        @Override
90        public boolean queueIdle() {
91            mIdle.open();
92            return false;
93        }
94    };
95
96    // Blocks until thread is idling
97    public void waitUntilIdle() {
98        Handler handler = waitAndGetHandler();
99        MessageQueue queue = handler.getLooper().getQueue();
100        if (queue.isIdle()) {
101            return;
102        }
103        mIdle.close();
104        queue.addIdleHandler(mIdleHandler);
105        // Ensure that the idle handler gets run even if the looper already went idle
106        handler.sendEmptyMessage(MSG_POKE_IDLE_HANDLER);
107        if (queue.isIdle()) {
108            return;
109        }
110        mIdle.block();
111    }
112
113}
114