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.hardware.Camera;
20import android.hardware.Camera.Parameters;
21import android.hardware.camera2.impl.CameraMetadataNative;
22import android.hardware.camera2.CaptureRequest;
23import android.hardware.camera2.CaptureResult;
24import android.hardware.camera2.utils.ParamsUtils;
25import android.util.Log;
26
27import java.util.Objects;
28
29import static android.hardware.camera2.CaptureRequest.*;
30import static com.android.internal.util.Preconditions.*;
31
32/**
33 * Map capture request data into legacy focus state transitions.
34 *
35 * <p>This object will asynchronously process auto-focus changes, so no interaction
36 * with it is necessary beyond reading the current state and updating with the latest trigger.</p>
37 */
38@SuppressWarnings("deprecation")
39public class LegacyFocusStateMapper {
40    private static String TAG = "LegacyFocusStateMapper";
41    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
42
43    private final Camera mCamera;
44
45    private int mAfStatePrevious = CONTROL_AF_STATE_INACTIVE;
46    private String mAfModePrevious = null;
47
48    /** Guard mAfRun and mAfState */
49    private final Object mLock = new Object();
50    /** Guard access with mLock */
51    private int mAfRun = 0;
52    /** Guard access with mLock */
53    private int mAfState = CONTROL_AF_STATE_INACTIVE;
54
55    /**
56     * Instantiate a new focus state mapper.
57     *
58     * @param camera a non-{@code null} camera1 device
59     *
60     * @throws NullPointerException if any of the args were {@code null}
61     */
62    public LegacyFocusStateMapper(Camera camera) {
63        mCamera = checkNotNull(camera, "camera must not be null");
64    }
65
66    /**
67     * Process the AF triggers from the request as a camera1 autofocus routine.
68     *
69     * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
70     * with the request.</p>
71     *
72     * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers}
73     * will have the latest AF state as reflected by the camera1 callbacks.</p>
74     *
75     * <p>None of the arguments will be mutated.</p>
76     *
77     * @param captureRequest a non-{@code null} request
78     * @param parameters a non-{@code null} parameters corresponding to this request (read-only)
79     */
80    public void processRequestTriggers(CaptureRequest captureRequest,
81            Camera.Parameters parameters) {
82        checkNotNull(captureRequest, "captureRequest must not be null");
83
84        /*
85         * control.afTrigger
86         */
87        int afTrigger = ParamsUtils.getOrDefault(captureRequest, CONTROL_AF_TRIGGER,
88                CONTROL_AF_TRIGGER_IDLE);
89
90        final String afMode = parameters.getFocusMode();
91
92        if (!Objects.equals(mAfModePrevious, afMode)) {
93            if (VERBOSE) {
94                Log.v(TAG, "processRequestTriggers - AF mode switched from " + mAfModePrevious +
95                        " to " + afMode);
96            }
97
98            // Switching modes always goes back to INACTIVE; ignore callbacks from previous modes
99
100            synchronized (mLock) {
101                ++mAfRun;
102                mAfState = CONTROL_AF_STATE_INACTIVE;
103            }
104            mCamera.cancelAutoFocus();
105        }
106
107        mAfModePrevious = afMode;
108
109        // Passive AF Scanning
110        {
111            final int currentAfRun;
112
113            synchronized (mLock) {
114                currentAfRun = mAfRun;
115            }
116
117            Camera.AutoFocusMoveCallback afMoveCallback = new Camera.AutoFocusMoveCallback() {
118                @Override
119                public void onAutoFocusMoving(boolean start, Camera camera) {
120                    synchronized (mLock) {
121                        int latestAfRun = mAfRun;
122
123                        if (VERBOSE) {
124                            Log.v(TAG,
125                                    "onAutoFocusMoving - start " + start + " latest AF run " +
126                                            latestAfRun + ", last AF run " + currentAfRun
127                            );
128                        }
129
130                        if (currentAfRun != latestAfRun) {
131                            Log.d(TAG,
132                                    "onAutoFocusMoving - ignoring move callbacks from old af run"
133                                            + currentAfRun
134                            );
135                            return;
136                        }
137
138                        int newAfState = start ?
139                                CONTROL_AF_STATE_PASSIVE_SCAN :
140                                CONTROL_AF_STATE_PASSIVE_FOCUSED;
141                        // We never send CONTROL_AF_STATE_PASSIVE_UNFOCUSED
142
143                        switch (afMode) {
144                            case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
145                            case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
146                                break;
147                            // This callback should never be sent in any other AF mode
148                            default:
149                                Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
150                                        + afMode);
151
152                        }
153
154                        mAfState = newAfState;
155                    }
156                }
157            };
158
159            // Only set move callback if we can call autofocus.
160            switch (afMode) {
161                case Parameters.FOCUS_MODE_AUTO:
162                case Parameters.FOCUS_MODE_MACRO:
163                case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
164                case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
165                    mCamera.setAutoFocusMoveCallback(afMoveCallback);
166            }
167        }
168
169
170        // AF Locking
171        switch (afTrigger) {
172            case CONTROL_AF_TRIGGER_START:
173
174                int afStateAfterStart;
175                switch (afMode) {
176                    case Parameters.FOCUS_MODE_AUTO:
177                    case Parameters.FOCUS_MODE_MACRO:
178                        afStateAfterStart = CONTROL_AF_STATE_ACTIVE_SCAN;
179                        break;
180                    case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
181                    case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
182                        afStateAfterStart = CONTROL_AF_STATE_PASSIVE_SCAN;
183                        break;
184                    default:
185                        // EDOF, INFINITY
186                        afStateAfterStart = CONTROL_AF_STATE_INACTIVE;
187                }
188
189                final int currentAfRun;
190                synchronized (mLock) {
191                    currentAfRun = ++mAfRun;
192                    mAfState = afStateAfterStart;
193                }
194
195                if (VERBOSE) {
196                    Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_START, " +
197                            "new AF run is " + currentAfRun);
198                }
199
200                // Avoid calling autofocus unless we are in a state that supports calling this.
201                if (afStateAfterStart == CONTROL_AF_STATE_INACTIVE) {
202                    break;
203                }
204
205                mCamera.autoFocus(new Camera.AutoFocusCallback() {
206                    @Override
207                    public void onAutoFocus(boolean success, Camera camera) {
208                        synchronized (mLock) {
209                            int latestAfRun = mAfRun;
210
211                            if (VERBOSE) {
212                                Log.v(TAG, "onAutoFocus - success " + success + " latest AF run " +
213                                        latestAfRun + ", last AF run " + currentAfRun);
214                            }
215
216                            // Ignore old auto-focus results, since another trigger was requested
217                            if (latestAfRun != currentAfRun) {
218                                Log.d(TAG, String.format("onAutoFocus - ignoring AF callback " +
219                                        "(old run %d, new run %d)", currentAfRun, latestAfRun));
220
221                                return;
222                            }
223
224                            int newAfState = success ?
225                                    CONTROL_AF_STATE_FOCUSED_LOCKED :
226                                    CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
227
228                            switch (afMode) {
229                                case Parameters.FOCUS_MODE_AUTO:
230                                case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
231                                case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
232                                case Parameters.FOCUS_MODE_MACRO:
233                                    break;
234                                // This callback should never be sent in any other AF mode
235                                default:
236                                    Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
237                                            + afMode);
238
239                            }
240
241                            mAfState = newAfState;
242                        }
243                    }
244                });
245
246                break;
247            case CONTROL_AF_TRIGGER_CANCEL:
248                synchronized (mLock) {
249                    int updatedAfRun;
250
251                    synchronized (mLock) {
252                        updatedAfRun = ++mAfRun;
253                        mAfState = CONTROL_AF_STATE_INACTIVE;
254                    }
255
256                    mCamera.cancelAutoFocus();
257
258                    if (VERBOSE) {
259                        Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_CANCEL, " +
260                                "new AF run is " + updatedAfRun);
261                    }
262                }
263
264                break;
265            case CONTROL_AF_TRIGGER_IDLE:
266                // No action necessary. The callbacks will handle transitions.
267                break;
268            default:
269                Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = "
270                        + afTrigger);
271        }
272    }
273
274    /**
275     * Update the {@code result} camera metadata map with the new value for the
276     * {@code control.afState}.
277     *
278     * <p>AF callbacks are processed in the background, and each call to {@link #mapResultTriggers}
279     * will have the latest AF state as reflected by the camera1 callbacks.</p>
280     *
281     * @param result a non-{@code null} result
282     */
283    public void mapResultTriggers(CameraMetadataNative result) {
284        checkNotNull(result, "result must not be null");
285
286        int newAfState;
287        synchronized (mLock) {
288            newAfState = mAfState;
289        }
290
291        if (VERBOSE && newAfState != mAfStatePrevious) {
292            Log.v(TAG, String.format("mapResultTriggers - afState changed from %s to %s",
293                    afStateToString(mAfStatePrevious), afStateToString(newAfState)));
294        }
295
296        result.set(CaptureResult.CONTROL_AF_STATE, newAfState);
297
298        mAfStatePrevious = newAfState;
299    }
300
301    private static String afStateToString(int afState) {
302        switch (afState) {
303            case CONTROL_AF_STATE_ACTIVE_SCAN:
304                return "ACTIVE_SCAN";
305            case CONTROL_AF_STATE_FOCUSED_LOCKED:
306                return "FOCUSED_LOCKED";
307            case CONTROL_AF_STATE_INACTIVE:
308                return "INACTIVE";
309            case CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
310                return "NOT_FOCUSED_LOCKED";
311            case CONTROL_AF_STATE_PASSIVE_FOCUSED:
312                return "PASSIVE_FOCUSED";
313            case CONTROL_AF_STATE_PASSIVE_SCAN:
314                return "PASSIVE_SCAN";
315            case CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
316                return "PASSIVE_UNFOCUSED";
317            default :
318                return "UNKNOWN(" + afState + ")";
319        }
320    }
321}
322