• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package com.android.ex.camera2.pos;
17 
18 import android.hardware.camera2.CameraDevice;
19 import android.hardware.camera2.CaptureResult.Key;
20 import android.hardware.camera2.CaptureRequest;
21 import android.hardware.camera2.CaptureResult;
22 import android.util.Log;
23 
24 import 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  */
32 public 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          */
onAutoFocusSuccess(CaptureResult result, boolean locked)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          */
onAutoFocusFail(CaptureResult result, boolean locked)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          */
onAutoFocusScan(CaptureResult result)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          */
onAutoFocusInactive(CaptureResult result)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 
AutoFocusStateMachine(AutoFocusStateListener listener)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      */
onCaptureCompleted(CaptureResult result)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      */
resetState()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      */
lockAutoFocus(CaptureRequest.Builder repeatingBuilder, CaptureRequest.Builder requestBuilder)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      */
unlockAutoFocus(CaptureRequest.Builder repeatingBuilder, CaptureRequest.Builder requestBuilder)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      */
setActiveAutoFocus(CaptureRequest.Builder repeatingBuilder, CaptureRequest.Builder requestBuilder)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      */
setPassiveAutoFocus(boolean picture, CaptureRequest.Builder repeatingBuilder)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 
beginTraceAsync(String sectionName)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 
endTraceAsync()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      */
updateCaptureRequest(CaptureRequest.Builder repeatingBuilder)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