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