CameraDeviceState.java revision d85e1a6ced452c9bd0d805f6ce19f50c9ea9b0a6
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 int mCurrentState = STATE_UNCONFIGURED;
53    private int mCurrentError = CameraBinderDecorator.NO_ERROR;
54
55    private RequestHolder mCurrentRequest = null;
56
57    private Handler mCurrentHandler = null;
58    private CameraDeviceStateListener mCurrentListener = null;
59
60
61    /**
62     * CameraDeviceStateListener callbacks to be called after state transitions.
63     */
64    public interface CameraDeviceStateListener {
65        void onError(int errorCode, RequestHolder holder);
66        void onConfiguring();
67        void onIdle();
68        void onCaptureStarted(RequestHolder holder);
69        void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
70    }
71
72    /**
73     * Transition to the {@code ERROR} state.
74     *
75     * <p>
76     * The device cannot exit the {@code ERROR} state.  If the device was not already in the
77     * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
78     * called.
79     * </p>
80     *
81     * @param error the error to set.  Should be one of the error codes defined in
82     *      {@link android.hardware.camera2.utils.CameraBinderDecorator}.
83     */
84    public synchronized void setError(int error) {
85        mCurrentError = error;
86        doStateTransition(STATE_ERROR);
87    }
88
89    /**
90     * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
91     *
92     * <p>
93     * If the device was not already in the {@code CONFIGURING} state,
94     * {@link CameraDeviceStateListener#onConfiguring()} will be called.
95     * </p>
96     *
97     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
98     */
99    public synchronized int setConfiguring() {
100        doStateTransition(STATE_CONFIGURING);
101        return mCurrentError;
102    }
103
104    /**
105     * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
106     *
107     * <p>
108     * If the device was not already in the {@code IDLE} state,
109     * {@link CameraDeviceStateListener#onIdle()} will be called.
110     * </p>
111     *
112     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
113     */
114    public synchronized int setIdle() {
115        doStateTransition(STATE_IDLE);
116        return mCurrentError;
117    }
118
119    /**
120     * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
121     *
122     * <p>
123     * If the device was not already in the {@code CAPTURING} state,
124     * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
125     * </p>
126     *
127     * @param request A {@link RequestHolder} containing the request for the current capture.
128     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
129     */
130    public synchronized int setCaptureStart(final RequestHolder request) {
131        mCurrentRequest = request;
132        doStateTransition(STATE_CAPTURING);
133        return mCurrentError;
134    }
135
136    /**
137     * Set the result for a capture.
138     *
139     * <p>
140     * If the device was in the {@code CAPTURING} state,
141     * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
142     * be called with the given result, otherwise this will result in the device transitioning to
143     * the {@code ERROR} state,
144     * </p>
145     *
146     * @param request the {@link RequestHolder} request that created this result.
147     * @param result the {@link CameraMetadataNative} result to set.
148     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
149     */
150    public synchronized int setCaptureResult(final RequestHolder request,
151                                             final CameraMetadataNative result) {
152        if (mCurrentState != STATE_CAPTURING) {
153            Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
154            mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
155            doStateTransition(STATE_ERROR);
156            return mCurrentError;
157        }
158
159        if (mCurrentHandler != null && mCurrentListener != null) {
160            mCurrentHandler.post(new Runnable() {
161                @Override
162                public void run() {
163                    mCurrentListener.onCaptureResult(result, request);
164                }
165            });
166        }
167        return mCurrentError;
168    }
169
170    /**
171     * Set the listener for state transition callbacks.
172     *
173     * @param handler handler on which to call the callbacks.
174     * @param listener the {@link CameraDeviceStateListener} callbacks to call.
175     */
176    public synchronized void setCameraDeviceCallbacks(Handler handler,
177                                                      CameraDeviceStateListener listener) {
178        mCurrentHandler = handler;
179        mCurrentListener = listener;
180    }
181
182    private void doStateTransition(int newState) {
183        if (DEBUG) {
184            if (newState != mCurrentState) {
185                Log.d(TAG, "Transitioning to state " + newState);
186            }
187        }
188        switch(newState) {
189            case STATE_ERROR:
190                if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
191                        mCurrentListener != null) {
192                    mCurrentHandler.post(new Runnable() {
193                        @Override
194                        public void run() {
195                            mCurrentListener.onError(mCurrentError, mCurrentRequest);
196                        }
197                    });
198                }
199                mCurrentState = STATE_ERROR;
200                break;
201            case STATE_CONFIGURING:
202                if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
203                    Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
204                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
205                    doStateTransition(STATE_ERROR);
206                    break;
207                }
208                if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
209                        mCurrentListener != null) {
210                    mCurrentHandler.post(new Runnable() {
211                        @Override
212                        public void run() {
213                            mCurrentListener.onConfiguring();
214                        }
215                    });
216                }
217                mCurrentState = STATE_CONFIGURING;
218                break;
219            case STATE_IDLE:
220                if (mCurrentState == STATE_IDLE) {
221                    break;
222                }
223
224                if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
225                    Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
226                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
227                    doStateTransition(STATE_ERROR);
228                    break;
229                }
230
231                if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
232                        mCurrentListener != null) {
233                    mCurrentHandler.post(new Runnable() {
234                        @Override
235                        public void run() {
236                            mCurrentListener.onIdle();
237                        }
238                    });
239                }
240                mCurrentState = STATE_IDLE;
241                break;
242            case STATE_CAPTURING:
243                if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
244                    Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
245                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
246                    doStateTransition(STATE_ERROR);
247                    break;
248                }
249                if (mCurrentHandler != null && mCurrentListener != null) {
250                    mCurrentHandler.post(new Runnable() {
251                        @Override
252                        public void run() {
253                            mCurrentListener.onCaptureStarted(mCurrentRequest);
254                        }
255                    });
256                }
257                mCurrentState = STATE_CAPTURING;
258                break;
259            default:
260                throw new IllegalStateException("Transition to unknown state: " + newState);
261        }
262    }
263
264
265}
266