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