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.camera2.impl.CameraDeviceImpl;
20import android.hardware.camera2.impl.CameraMetadataNative;
21import android.os.Handler;
22import android.util.Log;
23
24/**
25 * Emulates a the state of a single Camera2 device.
26 *
27 * <p>
28 * This class acts as the state machine for a camera device.  Valid state transitions are given
29 * in the table below:
30 * </p>
31 *
32 * <ul>
33 *      <li>{@code UNCONFIGURED -> CONFIGURING}</li>
34 *      <li>{@code CONFIGURING -> IDLE}</li>
35 *      <li>{@code IDLE -> CONFIGURING}</li>
36 *      <li>{@code IDLE -> CAPTURING}</li>
37 *      <li>{@code IDLE -> IDLE}</li>
38 *      <li>{@code CAPTURING -> IDLE}</li>
39 *      <li>{@code ANY -> ERROR}</li>
40 * </ul>
41 */
42public class CameraDeviceState {
43    private static final String TAG = "CameraDeviceState";
44    private static final boolean DEBUG = false;
45
46    private static final int STATE_ERROR = 0;
47    private static final int STATE_UNCONFIGURED = 1;
48    private static final int STATE_CONFIGURING = 2;
49    private static final int STATE_IDLE = 3;
50    private static final int STATE_CAPTURING = 4;
51
52    private static final String[] sStateNames = { "ERROR", "UNCONFIGURED", "CONFIGURING", "IDLE",
53            "CAPTURING"};
54
55    private int mCurrentState = STATE_UNCONFIGURED;
56    private int mCurrentError = NO_CAPTURE_ERROR;
57
58    private RequestHolder mCurrentRequest = null;
59
60    private Handler mCurrentHandler = null;
61    private CameraDeviceStateListener mCurrentListener = null;
62
63    /**
64     * Error code used by {@link #setCaptureStart} and {@link #setCaptureResult} to indicate that no
65     * error has occurred.
66     */
67    public static final int NO_CAPTURE_ERROR = -1;
68
69    /**
70     * CameraDeviceStateListener callbacks to be called after state transitions.
71     */
72    public interface CameraDeviceStateListener {
73        void onError(int errorCode, Object errorArg, RequestHolder holder);
74        void onConfiguring();
75        void onIdle();
76        void onBusy();
77        void onCaptureStarted(RequestHolder holder, long timestamp);
78        void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
79        void onRepeatingRequestError(long lastFrameNumber);
80    }
81
82    /**
83     * Transition to the {@code ERROR} state.
84     *
85     * <p>
86     * The device cannot exit the {@code ERROR} state.  If the device was not already in the
87     * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
88     * called.
89     * </p>
90     *
91     * @param error the error to set.  Should be one of the error codes defined in
92     *      {@link CameraDeviceImpl.CameraDeviceCallbacks}.
93     */
94    public synchronized void setError(int error) {
95        mCurrentError = error;
96        doStateTransition(STATE_ERROR);
97    }
98
99    /**
100     * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
101     *
102     * <p>
103     * If the device was not already in the {@code CONFIGURING} state,
104     * {@link CameraDeviceStateListener#onConfiguring()} will be called.
105     * </p>
106     *
107     * @return {@code false} if an error has occurred.
108     */
109    public synchronized boolean setConfiguring() {
110        doStateTransition(STATE_CONFIGURING);
111        return mCurrentError == NO_CAPTURE_ERROR;
112    }
113
114    /**
115     * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
116     *
117     * <p>
118     * If the device was not already in the {@code IDLE} state,
119     * {@link CameraDeviceStateListener#onIdle()} will be called.
120     * </p>
121     *
122     * @return {@code false} if an error has occurred.
123     */
124    public synchronized boolean setIdle() {
125        doStateTransition(STATE_IDLE);
126        return mCurrentError == NO_CAPTURE_ERROR;
127    }
128
129    /**
130     * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
131     *
132     * <p>
133     * If the device was not already in the {@code CAPTURING} state,
134     * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
135     * </p>
136     *
137     * @param request A {@link RequestHolder} containing the request for the current capture.
138     * @param timestamp The timestamp of the capture start in nanoseconds.
139     * @param captureError Report a recoverable error for a single request using a valid
140     *                     error code for {@code ICameraDeviceCallbacks}, or
141     *                     {@link #NO_CAPTURE_ERROR}
142     * @return {@code false} if an error has occurred.
143     */
144    public synchronized boolean setCaptureStart(final RequestHolder request, long timestamp,
145                                            int captureError) {
146        mCurrentRequest = request;
147        doStateTransition(STATE_CAPTURING, timestamp, captureError);
148        return mCurrentError == NO_CAPTURE_ERROR;
149    }
150
151    /**
152     * Set the result for a capture.
153     *
154     * <p>
155     * If the device was in the {@code CAPTURING} state,
156     * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
157     * be called with the given result, otherwise this will result in the device transitioning to
158     * the {@code ERROR} state,
159     * </p>
160     *
161     * @param request The {@link RequestHolder} request that created this result.
162     * @param result The {@link CameraMetadataNative} result to set.
163     * @param captureError Report a recoverable error for a single buffer or result using a valid
164     *                     error code for {@code ICameraDeviceCallbacks}, or
165     *                     {@link #NO_CAPTURE_ERROR}.
166     * @param captureErrorArg An argument for some error captureError codes.
167     * @return {@code false} if an error has occurred.
168     */
169    public synchronized boolean setCaptureResult(final RequestHolder request,
170            final CameraMetadataNative result,
171            final int captureError, final Object captureErrorArg) {
172        if (mCurrentState != STATE_CAPTURING) {
173            Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
174            mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
175            doStateTransition(STATE_ERROR);
176            return mCurrentError == NO_CAPTURE_ERROR;
177        }
178
179        if (mCurrentHandler != null && mCurrentListener != null) {
180            if (captureError != NO_CAPTURE_ERROR) {
181                mCurrentHandler.post(new Runnable() {
182                    @Override
183                    public void run() {
184                        mCurrentListener.onError(captureError, captureErrorArg, request);
185                    }
186                });
187            } else {
188                mCurrentHandler.post(new Runnable() {
189                    @Override
190                    public void run() {
191                        mCurrentListener.onCaptureResult(result, request);
192                    }
193                });
194            }
195        }
196        return mCurrentError == NO_CAPTURE_ERROR;
197    }
198
199    public synchronized boolean setCaptureResult(final RequestHolder request,
200            final CameraMetadataNative result) {
201        return setCaptureResult(request, result, NO_CAPTURE_ERROR, /*errorArg*/null);
202    }
203
204    /**
205     * Set repeating request error.
206     *
207     * <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
208     *
209     * @param lastFrameNumber Frame number of the last repeating request before it is stopped.
210     */
211    public synchronized void setRepeatingRequestError(final long lastFrameNumber) {
212        mCurrentHandler.post(new Runnable() {
213            @Override
214            public void run() {
215                mCurrentListener.onRepeatingRequestError(lastFrameNumber);
216            }
217        });
218    }
219
220    /**
221     * Set the listener for state transition callbacks.
222     *
223     * @param handler handler on which to call the callbacks.
224     * @param listener the {@link CameraDeviceStateListener} callbacks to call.
225     */
226    public synchronized void setCameraDeviceCallbacks(Handler handler,
227                                                      CameraDeviceStateListener listener) {
228        mCurrentHandler = handler;
229        mCurrentListener = listener;
230    }
231
232    private void doStateTransition(int newState) {
233        doStateTransition(newState, /*timestamp*/0, NO_CAPTURE_ERROR);
234    }
235
236    private void doStateTransition(int newState, final long timestamp, final int error) {
237        if (newState != mCurrentState) {
238            String stateName = "UNKNOWN";
239            if (newState >= 0 && newState < sStateNames.length) {
240                stateName = sStateNames[newState];
241            }
242            Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
243        }
244
245        // If we transitioned into a non-IDLE/non-ERROR state then mark the device as busy
246        if(newState != STATE_ERROR && newState != STATE_IDLE) {
247            if (mCurrentState != newState && mCurrentHandler != null &&
248                    mCurrentListener != null) {
249                mCurrentHandler.post(new Runnable() {
250                    @Override
251                    public void run() {
252                        mCurrentListener.onBusy();
253                    }
254                });
255            }
256        }
257
258        switch(newState) {
259            case STATE_ERROR:
260                if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
261                        mCurrentListener != null) {
262                    mCurrentHandler.post(new Runnable() {
263                        @Override
264                        public void run() {
265                            mCurrentListener.onError(mCurrentError, /*errorArg*/null, mCurrentRequest);
266                        }
267                    });
268                }
269                mCurrentState = STATE_ERROR;
270                break;
271            case STATE_CONFIGURING:
272                if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
273                    Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
274                    mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
275                    doStateTransition(STATE_ERROR);
276                    break;
277                }
278                if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
279                        mCurrentListener != null) {
280                    mCurrentHandler.post(new Runnable() {
281                        @Override
282                        public void run() {
283                            mCurrentListener.onConfiguring();
284                        }
285                    });
286                }
287                mCurrentState = STATE_CONFIGURING;
288                break;
289            case STATE_IDLE:
290                if (mCurrentState == STATE_IDLE) {
291                    break;
292                }
293
294                if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
295                    Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
296                    mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
297                    doStateTransition(STATE_ERROR);
298                    break;
299                }
300
301                if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
302                        mCurrentListener != null) {
303                    mCurrentHandler.post(new Runnable() {
304                        @Override
305                        public void run() {
306                            mCurrentListener.onIdle();
307                        }
308                    });
309                }
310                mCurrentState = STATE_IDLE;
311                break;
312            case STATE_CAPTURING:
313                if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
314                    Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
315                    mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
316                    doStateTransition(STATE_ERROR);
317                    break;
318                }
319
320                if (mCurrentHandler != null && mCurrentListener != null) {
321                    if (error != NO_CAPTURE_ERROR) {
322                        mCurrentHandler.post(new Runnable() {
323                            @Override
324                            public void run() {
325                                mCurrentListener.onError(error, /*errorArg*/null, mCurrentRequest);
326                            }
327                        });
328                    } else {
329                        mCurrentHandler.post(new Runnable() {
330                            @Override
331                            public void run() {
332                                mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
333                            }
334                        });
335                    }
336                }
337                mCurrentState = STATE_CAPTURING;
338                break;
339            default:
340                throw new IllegalStateException("Transition to unknown state: " + newState);
341        }
342    }
343
344
345}
346