CameraDeviceState.java revision e663cb77281c4c76241b820f6126543f1c2d859f
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.CameraMetadataNative;
20import android.hardware.camera2.utils.CameraBinderDecorator;
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 = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
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 = CameraBinderDecorator.NO_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, RequestHolder holder);
74        void onConfiguring();
75        void onIdle();
76        void onCaptureStarted(RequestHolder holder, long timestamp);
77        void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
78    }
79
80    /**
81     * Transition to the {@code ERROR} state.
82     *
83     * <p>
84     * The device cannot exit the {@code ERROR} state.  If the device was not already in the
85     * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
86     * called.
87     * </p>
88     *
89     * @param error the error to set.  Should be one of the error codes defined in
90     *      {@link android.hardware.camera2.utils.CameraBinderDecorator}.
91     */
92    public synchronized void setError(int error) {
93        mCurrentError = error;
94        doStateTransition(STATE_ERROR);
95    }
96
97    /**
98     * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
99     *
100     * <p>
101     * If the device was not already in the {@code CONFIGURING} state,
102     * {@link CameraDeviceStateListener#onConfiguring()} will be called.
103     * </p>
104     *
105     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
106     */
107    public synchronized int setConfiguring() {
108        doStateTransition(STATE_CONFIGURING);
109        return mCurrentError;
110    }
111
112    /**
113     * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
114     *
115     * <p>
116     * If the device was not already in the {@code IDLE} state,
117     * {@link CameraDeviceStateListener#onIdle()} will be called.
118     * </p>
119     *
120     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
121     */
122    public synchronized int setIdle() {
123        doStateTransition(STATE_IDLE);
124        return mCurrentError;
125    }
126
127    /**
128     * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
129     *
130     * <p>
131     * If the device was not already in the {@code CAPTURING} state,
132     * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
133     * </p>
134     *
135     * @param request A {@link RequestHolder} containing the request for the current capture.
136     * @param timestamp The timestamp of the capture start in nanoseconds.
137     * @param captureError Report a recoverable error for a single request using a valid
138     *                     error code for {@code ICameraDeviceCallbacks}, or
139     *                     {@link #NO_CAPTURE_ERROR}
140     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
141     */
142    public synchronized int setCaptureStart(final RequestHolder request, long timestamp,
143                                            int captureError) {
144        mCurrentRequest = request;
145        doStateTransition(STATE_CAPTURING, timestamp, captureError);
146        return mCurrentError;
147    }
148
149    /**
150     * Set the result for a capture.
151     *
152     * <p>
153     * If the device was in the {@code CAPTURING} state,
154     * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
155     * be called with the given result, otherwise this will result in the device transitioning to
156     * the {@code ERROR} state,
157     * </p>
158     *
159     * @param request The {@link RequestHolder} request that created this result.
160     * @param result The {@link CameraMetadataNative} result to set.
161     * @param captureError Report a recoverable error for a single buffer or result using a valid
162     *                     error code for {@code ICameraDeviceCallbacks}, or
163     *                     {@link #NO_CAPTURE_ERROR}.
164     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
165     */
166    public synchronized int setCaptureResult(final RequestHolder request,
167                                             final CameraMetadataNative result,
168                                             final int captureError) {
169        if (mCurrentState != STATE_CAPTURING) {
170            Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
171            mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
172            doStateTransition(STATE_ERROR);
173            return mCurrentError;
174        }
175
176        if (mCurrentHandler != null && mCurrentListener != null) {
177            if (captureError != NO_CAPTURE_ERROR) {
178                mCurrentHandler.post(new Runnable() {
179                    @Override
180                    public void run() {
181                        mCurrentListener.onError(captureError, request);
182                    }
183                });
184            } else {
185                mCurrentHandler.post(new Runnable() {
186                    @Override
187                    public void run() {
188                        mCurrentListener.onCaptureResult(result, request);
189                    }
190                });
191            }
192        }
193        return mCurrentError;
194    }
195
196    /**
197     * Set the listener for state transition callbacks.
198     *
199     * @param handler handler on which to call the callbacks.
200     * @param listener the {@link CameraDeviceStateListener} callbacks to call.
201     */
202    public synchronized void setCameraDeviceCallbacks(Handler handler,
203                                                      CameraDeviceStateListener listener) {
204        mCurrentHandler = handler;
205        mCurrentListener = listener;
206    }
207
208    private void doStateTransition(int newState) {
209        doStateTransition(newState, /*timestamp*/0, CameraBinderDecorator.NO_ERROR);
210    }
211
212    private void doStateTransition(int newState, final long timestamp, final int error) {
213        if (newState != mCurrentState) {
214            String stateName = "UNKNOWN";
215            if (newState >= 0 && newState < sStateNames.length) {
216                stateName = sStateNames[newState];
217            }
218            Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
219        }
220        switch(newState) {
221            case STATE_ERROR:
222                if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
223                        mCurrentListener != null) {
224                    mCurrentHandler.post(new Runnable() {
225                        @Override
226                        public void run() {
227                            mCurrentListener.onError(mCurrentError, mCurrentRequest);
228                        }
229                    });
230                }
231                mCurrentState = STATE_ERROR;
232                break;
233            case STATE_CONFIGURING:
234                if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
235                    Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
236                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
237                    doStateTransition(STATE_ERROR);
238                    break;
239                }
240                if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
241                        mCurrentListener != null) {
242                    mCurrentHandler.post(new Runnable() {
243                        @Override
244                        public void run() {
245                            mCurrentListener.onConfiguring();
246                        }
247                    });
248                }
249                mCurrentState = STATE_CONFIGURING;
250                break;
251            case STATE_IDLE:
252                if (mCurrentState == STATE_IDLE) {
253                    break;
254                }
255
256                if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
257                    Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
258                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
259                    doStateTransition(STATE_ERROR);
260                    break;
261                }
262
263                if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
264                        mCurrentListener != null) {
265                    mCurrentHandler.post(new Runnable() {
266                        @Override
267                        public void run() {
268                            mCurrentListener.onIdle();
269                        }
270                    });
271                }
272                mCurrentState = STATE_IDLE;
273                break;
274            case STATE_CAPTURING:
275                if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
276                    Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
277                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
278                    doStateTransition(STATE_ERROR);
279                    break;
280                }
281
282                if (mCurrentHandler != null && mCurrentListener != null) {
283                    if (error != NO_CAPTURE_ERROR) {
284                        mCurrentHandler.post(new Runnable() {
285                            @Override
286                            public void run() {
287                                mCurrentListener.onError(error, mCurrentRequest);
288                            }
289                        });
290                    } else {
291                        mCurrentHandler.post(new Runnable() {
292                            @Override
293                            public void run() {
294                                mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
295                            }
296                        });
297                    }
298                }
299                mCurrentState = STATE_CAPTURING;
300                break;
301            default:
302                throw new IllegalStateException("Transition to unknown state: " + newState);
303        }
304    }
305
306
307}
308