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