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