AutoFocusStateMachine.java revision 806d114d5975c89843c9ba16eadfcaf143afdebb
1/*
2 * Copyright 2013 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 */
16package com.android.ex.camera2.pos;
17
18import android.hardware.camera2.CameraDevice;
19import android.hardware.camera2.CameraMetadata.Key;
20import android.hardware.camera2.CaptureRequest;
21import android.hardware.camera2.CaptureResult;
22import android.util.Log;
23
24/**
25 * Manage the auto focus state machine for CameraDevice.
26 *
27 * <p>Requests are created only when the AF needs to be manipulated from the user,
28 * but automatic camera-caused AF state changes are broadcasted from any new result.</p>
29 */
30public class AutoFocusStateMachine {
31
32    public interface AutoFocusStateListener {
33        /**
34         * The camera is currently focused (either active or passive).
35         *
36         * @param locked True if the lens has been locked from moving, false otherwise.
37         */
38        void onAutoFocusSuccess(CaptureResult result, boolean locked);
39
40        /**
41         * The camera is currently not focused (either active or passive).
42         *
43         * @param locked False if the AF is still scanning, true if needs a restart.
44         */
45        void onAutoFocusFail(CaptureResult result, boolean locked);
46
47        /**
48         * The camera is currently scanning (either active or passive)
49         * and has not yet converged.
50         *
51         * <p>This is not called for results where the AF either succeeds or fails.</p>
52         */
53        void onAutoFocusScan(CaptureResult result);
54
55        /**
56         * The camera is currently not doing anything with the autofocus.
57         *
58         * <p>Autofocus could be off, or this could be an intermediate state transition as
59         * scanning restarts.</p>
60         */
61        void onAutoFocusInactive(CaptureResult result);
62    }
63
64    private static final String TAG = "AutoFocusStateMachine";
65    private static final boolean DEBUG_LOGGING = Log.isLoggable(TAG, Log.DEBUG);
66    private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
67    private static final int AF_UNINITIALIZED = -1;
68
69    private final AutoFocusStateListener mListener;
70    private int mLastAfState = AF_UNINITIALIZED;
71    private int mLastAfMode = AF_UNINITIALIZED;
72    private int mCurrentAfMode = AF_UNINITIALIZED;
73    private int mCurrentAfTrigger = AF_UNINITIALIZED;
74
75    public AutoFocusStateMachine(AutoFocusStateListener listener) {
76        if (listener == null) {
77            throw new IllegalArgumentException("listener should not be null");
78        }
79        mListener = listener;
80    }
81
82    /**
83     * Invoke every time we get a new CaptureResult via
84     * {@link CameraDevice.CaptureListener#onCaptureCompleted}.
85     *
86     * <p>This function is responsible for dispatching updates via the
87     * {@link AutoFocusStateListener} so without calling this on a regular basis, no
88     * AF changes will be observed.</p>
89     *
90     * @param result CaptureResult
91     */
92    public synchronized void onCaptureCompleted(CaptureResult result) {
93
94        /**
95         * Work-around for b/11269834
96         * Although these should never-ever happen, harden for ship
97         */
98        if (result == null) {
99            Log.w(TAG, "onCaptureCompleted - missing result, skipping AF update");
100            return;
101        }
102
103        Key<Integer> keyAfState = CaptureResult.CONTROL_AF_STATE;
104        if (keyAfState == null) {
105            Log.e(TAG, "onCaptureCompleted - missing android.control.afState key, " +
106                    "skipping AF update");
107            return;
108        }
109
110        Key<Integer> keyAfMode = CaptureResult.CONTROL_AF_MODE;
111        if (keyAfMode == null) {
112            Log.e(TAG, "onCaptureCompleted - missing android.control.afMode key, " +
113                    "skipping AF update");
114            return;
115        }
116
117        Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
118        Integer afMode = result.get(CaptureResult.CONTROL_AF_MODE);
119
120        /**
121         * Work-around for b/11238865
122         * This is a HAL bug as these fields should be there always.
123         */
124        if (afState == null) {
125            Log.w(TAG, "onCaptureCompleted - missing android.control.afState !");
126            return;
127        } else if (afMode == null) {
128            Log.w(TAG, "onCaptureCompleted - missing android.control.afMode !");
129            return;
130        }
131
132        if (DEBUG_LOGGING) Log.d(TAG, "onCaptureCompleted - new AF mode = " + afMode +
133                " new AF state = " + afState);
134
135        if (mLastAfState == afState && afMode == mLastAfMode) {
136            // Same AF state as last time, nothing else needs to be done.
137            return;
138        }
139
140        if (VERBOSE_LOGGING) Log.v(TAG, "onCaptureCompleted - new AF mode = " + afMode +
141                " new AF state = " + afState);
142
143        mLastAfState = afState;
144        mLastAfMode = afMode;
145
146        switch (afState) {
147            case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
148                mListener.onAutoFocusSuccess(result, /*locked*/true);
149                break;
150            case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
151                mListener.onAutoFocusFail(result, /*locked*/true);
152                break;
153            case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED:
154                mListener.onAutoFocusSuccess(result, /*locked*/false);
155                break;
156            case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
157                mListener.onAutoFocusFail(result, /*locked*/false);
158                break;
159            case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN:
160                mListener.onAutoFocusScan(result);
161                break;
162            case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN:
163                mListener.onAutoFocusScan(result);
164                break;
165            case CaptureResult.CONTROL_AF_STATE_INACTIVE:
166                mListener.onAutoFocusInactive(result);
167                break;
168        }
169    }
170
171    /**
172     * Lock the lens from moving. Typically used before taking a picture.
173     *
174     * <p>After calling this function, submit the new requestBuilder as a separate capture.
175     * Do not submit it as a repeating request or the AF lock will be repeated every time.</p>
176     *
177     * <p>Create a new repeating request from repeatingBuilder and set that as the updated
178     * repeating request.</p>
179     *
180     * <p>If the lock succeeds, {@link AutoFocusStateListener#onAutoFocusSuccess} with
181     * {@code locked == true} will be invoked. If the lock fails,
182     * {@link AutoFocusStateListener#onAutoFocusFail} with {@code scanning == false} will be
183     * invoked.</p>
184     *
185     * @param repeatingBuilder Builder for a repeating request.
186     * @param requestBuilder Builder for a non-repeating request.
187     *
188     */
189    public synchronized void lockAutoFocus(CaptureRequest.Builder repeatingBuilder,
190            CaptureRequest.Builder requestBuilder) {
191
192        if (VERBOSE_LOGGING) Log.v(TAG, "lockAutoFocus");
193
194        if (mCurrentAfMode == AF_UNINITIALIZED) {
195            throw new IllegalStateException("AF mode was not enabled");
196        }
197
198        mCurrentAfTrigger = CaptureRequest.CONTROL_AF_TRIGGER_START;
199
200        repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
201        requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
202
203        repeatingBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
204                CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
205        requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
206                CaptureRequest.CONTROL_AF_TRIGGER_START);
207    }
208
209    /**
210     * Unlock the lens, allowing it to move again. Typically used after taking a picture.
211     *
212     * <p>After calling this function, submit the new requestBuilder as a separate capture.
213     * Do not submit it as a repeating request or the AF lock will be repeated every time.</p>
214     *
215     * <p>Create a new repeating request from repeatingBuilder and set that as the updated
216     * repeating request.</p>
217     *
218     * <p>Once the unlock takes effect, {@link AutoFocusStateListener#onAutoFocusInactive} is
219     * invoked, and after that the effects depend on which mode you were in:
220     * <ul>
221     * <li>Passive - Scanning restarts with {@link AutoFocusStateListener#onAutoFocusScan}</li>
222     * <li>Active - The lens goes back to a default position (no callbacks)</li>
223     * </ul>
224     * </p>
225     *
226     * @param repeatingBuilder Builder for a repeating request.
227     * @param requestBuilder Builder for a non-repeating request.
228     *
229     */
230    public synchronized void unlockAutoFocus(CaptureRequest.Builder repeatingBuilder,
231            CaptureRequest.Builder requestBuilder) {
232
233        if (VERBOSE_LOGGING) Log.v(TAG, "unlockAutoFocus");
234
235        if (mCurrentAfMode == AF_UNINITIALIZED) {
236            throw new IllegalStateException("AF mode was not enabled");
237        }
238
239        mCurrentAfTrigger = CaptureRequest.CONTROL_AF_TRIGGER_CANCEL;
240
241        repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
242        requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
243
244        repeatingBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
245                CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
246        requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
247                CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
248    }
249
250    /**
251     * Enable active auto focus, immediately triggering a converging scan.
252     *
253     * <p>This is typically only used when locking the passive AF has failed.</p>
254     *
255     * <p>Once active AF scanning starts, {@link AutoFocusStateListener#onAutoFocusScan} will be
256     * invoked.</p>
257     *
258     * <p>If the active scan succeeds, {@link AutoFocusStateListener#onAutoFocusSuccess} with
259     * {@code locked == true} will be invoked. If the active scan fails,
260     * {@link AutoFocusStateListener#onAutoFocusFail} with {@code scanning == false} will be
261     * invoked.</p>
262     *
263     * <p>After calling this function, submit the new requestBuilder as a separate capture.
264     * Do not submit it as a repeating request or the AF trigger will be repeated every time.</p>
265     *
266     * <p>Create a new repeating request from repeatingBuilder and set that as the updated
267     * repeating request.</p>
268     *
269     * @param repeatingBuilder Builder for a repeating request.
270     * @param requestBuilder Builder for a non-repeating request.
271     *
272     * @param repeatingBuilder Builder for a repeating request.
273     */
274    public synchronized void setActiveAutoFocus(CaptureRequest.Builder repeatingBuilder,
275            CaptureRequest.Builder requestBuilder) {
276        if (VERBOSE_LOGGING) Log.v(TAG, "setActiveAutoFocus");
277
278        mCurrentAfMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
279
280        repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
281        requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
282
283        repeatingBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
284                CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
285        requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
286                CaptureRequest.CONTROL_AF_TRIGGER_START);
287    }
288
289    /**
290     * Enable passive autofocus, immediately triggering a non-converging scan.
291     *
292     * <p>While passive autofocus is enabled, use {@link #lockAutoFocus} to lock
293     * the lens before taking a picture. Once a picture is taken, use {@link #unlockAutoFocus}
294     * to let the lens go back into passive scanning.</p>
295     *
296     * <p>Once passive AF scanning starts, {@link AutoFocusStateListener#onAutoFocusScan} will be
297     * invoked.</p>
298     *
299     * @param repeatingBuilder Builder for a repeating request.
300     * @param picture True for still capture AF, false for video AF.
301     */
302    public synchronized void setPassiveAutoFocus(boolean picture,
303            CaptureRequest.Builder repeatingBuilder) {
304        if (VERBOSE_LOGGING) Log.v(TAG, "setPassiveAutoFocus - picture " + picture);
305
306        if (picture) {
307            mCurrentAfMode = CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
308        } else {
309            mCurrentAfMode = CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
310        }
311
312        repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode);
313    }
314}
315