blob: b9507d722aff13664a88d582f3082a29040b13f7 [file] [log] [blame]
Igor Murashkin0b27d342014-05-30 09:45:05 -07001/*
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 */
16package android.hardware.camera2.impl;
17
18import android.hardware.camera2.CameraAccessException;
19import android.hardware.camera2.CameraCaptureSession;
20import android.hardware.camera2.CameraDevice;
21import android.hardware.camera2.CaptureRequest;
Igor Murashkin21547d62014-06-04 15:21:42 -070022import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
Igor Murashkin0b27d342014-05-30 09:45:05 -070023import android.hardware.camera2.dispatch.BroadcastDispatcher;
24import android.hardware.camera2.dispatch.Dispatchable;
25import android.hardware.camera2.dispatch.DuckTypingDispatcher;
26import android.hardware.camera2.dispatch.HandlerDispatcher;
27import android.hardware.camera2.dispatch.InvokeDispatcher;
28import android.hardware.camera2.dispatch.NullDispatcher;
29import android.hardware.camera2.utils.TaskDrainer;
30import android.hardware.camera2.utils.TaskSingleDrainer;
31import android.os.Handler;
32import android.util.Log;
33import android.view.Surface;
34
35import java.util.Arrays;
36import java.util.List;
37
Igor Murashkin21547d62014-06-04 15:21:42 -070038import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
Igor Murashkin0b27d342014-05-30 09:45:05 -070039import static com.android.internal.util.Preconditions.*;
40
41public class CameraCaptureSessionImpl extends CameraCaptureSession {
42 private static final String TAG = "CameraCaptureSession";
43 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
44
Eino-Ville Talvala95037852014-09-14 14:07:05 -070045 /** Simple integer ID for session for debugging */
46 private final int mId;
47 private final String mIdString;
48
Igor Murashkin0b27d342014-05-30 09:45:05 -070049 /** User-specified set of surfaces used as the configuration outputs */
50 private final List<Surface> mOutputs;
51 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070052 * User-specified state callback, used for outgoing events; calls to this object will be
Igor Murashkin0b27d342014-05-30 09:45:05 -070053 * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
54 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070055 private final CameraCaptureSession.StateCallback mStateCallback;
56 /** User-specified state handler used for outgoing state callback events */
Igor Murashkin0b27d342014-05-30 09:45:05 -070057 private final Handler mStateHandler;
58
59 /** Internal camera device; used to translate calls into existing deprecated API */
Igor Murashkin21547d62014-06-04 15:21:42 -070060 private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
Igor Murashkin0b27d342014-05-30 09:45:05 -070061 /** Internal handler; used for all incoming events to preserve total order */
62 private final Handler mDeviceHandler;
63
64 /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
65 private final TaskDrainer<Integer> mSequenceDrainer;
66 /** Drain state transitions from ACTIVE -> IDLE */
67 private final TaskSingleDrainer mIdleDrainer;
68 /** Drain state transitions from BUSY -> IDLE */
69 private final TaskSingleDrainer mAbortDrainer;
70 /** Drain the UNCONFIGURED state transition */
71 private final TaskSingleDrainer mUnconfigureDrainer;
72
73 /** This session is closed; all further calls will throw ISE */
74 private boolean mClosed = false;
Igor Murashkin10fbbbb2014-08-19 16:19:30 -070075 /** This session failed to be configured successfully */
76 private final boolean mConfigureSuccess;
Igor Murashkin0b27d342014-05-30 09:45:05 -070077 /** Do not unconfigure if this is set; another session will overwrite configuration */
78 private boolean mSkipUnconfigure = false;
79
80 /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -070081 private volatile boolean mAborting;
Igor Murashkin0b27d342014-05-30 09:45:05 -070082
83 /**
84 * Create a new CameraCaptureSession.
85 *
86 * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
87 * There must be no pending actions
88 * (e.g. no pending captures, no repeating requests, no flush).</p>
89 */
Eino-Ville Talvala95037852014-09-14 14:07:05 -070090 CameraCaptureSessionImpl(int id, List<Surface> outputs,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070091 CameraCaptureSession.StateCallback callback, Handler stateHandler,
Igor Murashkin21547d62014-06-04 15:21:42 -070092 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Igor Murashkin0b27d342014-05-30 09:45:05 -070093 Handler deviceStateHandler, boolean configureSuccess) {
94 if (outputs == null || outputs.isEmpty()) {
95 throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070096 } else if (callback == null) {
97 throw new IllegalArgumentException("callback must not be null");
Igor Murashkin0b27d342014-05-30 09:45:05 -070098 }
99
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700100 mId = id;
101 mIdString = String.format("Session %d: ", mId);
102
Igor Murashkin0b27d342014-05-30 09:45:05 -0700103 // TODO: extra verification of outputs
104 mOutputs = outputs;
105 mStateHandler = checkHandler(stateHandler);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700106 mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700107
108 mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
109 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
110
111 /*
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700112 * Use the same handler as the device's StateCallback for all the internal coming events
Igor Murashkin0b27d342014-05-30 09:45:05 -0700113 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700114 * This ensures total ordering between CameraDevice.StateCallback and
115 * CameraDeviceImpl.CaptureCallback events.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700116 */
117 mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
118 /*name*/"seq");
119 mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
120 /*name*/"idle");
121 mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
122 /*name*/"abort");
123 mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(),
124 /*name*/"unconf");
125
126 // CameraDevice should call configureOutputs and have it finish before constructing us
127
128 if (configureSuccess) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700129 mStateCallback.onConfigured(this);
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700130 if (VERBOSE) Log.v(TAG, mIdString + "Created session successfully");
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700131 mConfigureSuccess = true;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700132 } else {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700133 mStateCallback.onConfigureFailed(this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700134 mClosed = true; // do not fire any other callbacks, do not allow any other work
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700135 Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700136 mConfigureSuccess = false;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700137 }
138 }
139
140 @Override
141 public CameraDevice getDevice() {
142 return mDeviceImpl;
143 }
144
145 @Override
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700146 public synchronized int capture(CaptureRequest request, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700147 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700148 if (request == null) {
149 throw new IllegalArgumentException("request must not be null");
150 }
151
Igor Murashkin0b27d342014-05-30 09:45:05 -0700152 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700153
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700154 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700155
156 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700157 Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
158 " handler " + handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700159 }
160
161 return addPendingSequence(mDeviceImpl.capture(request,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700162 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700163 }
164
165 @Override
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700166 public synchronized int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700167 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700168 if (requests == null) {
169 throw new IllegalArgumentException("requests must not be null");
170 } else if (requests.isEmpty()) {
171 throw new IllegalArgumentException("requests must have at least one element");
172 }
173
Igor Murashkin0b27d342014-05-30 09:45:05 -0700174 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700175
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700176 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700177
178 if (VERBOSE) {
179 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700180 Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
181 ", callback " + callback + " handler " + handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700182 }
183
184 return addPendingSequence(mDeviceImpl.captureBurst(requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700185 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700186 }
187
188 @Override
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700189 public synchronized int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700190 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700191 if (request == null) {
192 throw new IllegalArgumentException("request must not be null");
193 }
194
Igor Murashkin0b27d342014-05-30 09:45:05 -0700195 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700196
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700197 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700198
Igor Murashkine1442202014-06-09 17:51:24 -0700199 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700200 Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
201 callback + " handler" + " " + handler);
Igor Murashkine1442202014-06-09 17:51:24 -0700202 }
203
Igor Murashkin0b27d342014-05-30 09:45:05 -0700204 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700205 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700206 }
207
208 @Override
209 public synchronized int setRepeatingBurst(List<CaptureRequest> requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700210 CaptureCallback callback, Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700211 if (requests == null) {
212 throw new IllegalArgumentException("requests must not be null");
213 } else if (requests.isEmpty()) {
214 throw new IllegalArgumentException("requests must have at least one element");
215 }
216
Igor Murashkin0b27d342014-05-30 09:45:05 -0700217 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700218
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700219 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700220
221 if (VERBOSE) {
222 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700223 Log.v(TAG, mIdString + "setRepeatingBurst - requests " + Arrays.toString(requestArray) +
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700224 ", callback " + callback + " handler" + "" + handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700225 }
226
227 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700228 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700229 }
230
231 @Override
232 public synchronized void stopRepeating() throws CameraAccessException {
233 checkNotClosed();
234
235 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700236 Log.v(TAG, mIdString + "stopRepeating");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700237 }
238
239 mDeviceImpl.stopRepeating();
240 }
241
242 @Override
243 public synchronized void abortCaptures() throws CameraAccessException {
244 checkNotClosed();
245
246 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700247 Log.v(TAG, mIdString + "abortCaptures");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700248 }
249
250 if (mAborting) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700251 Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700252 return;
253 }
254
255 mAborting = true;
256 mAbortDrainer.taskStarted();
257
258 mDeviceImpl.flush();
259 // The next BUSY -> IDLE set of transitions will mark the end of the abort.
260 }
261
262 /**
263 * Replace this session with another session.
264 *
265 * <p>This is an optimization to avoid unconfiguring and then immediately having to
266 * reconfigure again.</p>
267 *
268 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
269 * <p>
270 *
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700271 * <p>After this call completes, the session will not call any further methods on the camera
272 * device.</p>
273 *
Igor Murashkin0b27d342014-05-30 09:45:05 -0700274 * @see CameraCaptureSession#close
275 */
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700276 synchronized void replaceSessionClose() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700277 /*
278 * In order for creating new sessions to be fast, the new session should be created
279 * before the old session is closed.
280 *
281 * Otherwise the old session will always unconfigure if there is no new session to
282 * replace it.
283 *
284 * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
285 * to skip unconfigure if a new session is created before the captures are all drained,
286 * but this would introduce nondeterministic behavior.
287 */
288
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700289 if (VERBOSE) Log.v(TAG, mIdString + "replaceSessionClose");
Igor Murashkine1442202014-06-09 17:51:24 -0700290
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700291 // Set up fast shutdown. Possible alternative paths:
292 // - This session is active, so close() below starts the shutdown drain
293 // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
294 // - This session is already closed and has executed the idle drain listener, and
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700295 // configureOutputsChecked(null) has already been called.
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700296 //
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700297 // Do not call configureOutputsChecked(null) going forward, since it would race with the
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700298 // configuration for the new session. If it was already called, then we don't care, since it
299 // won't get called again.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700300 mSkipUnconfigure = true;
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700301
Igor Murashkin0b27d342014-05-30 09:45:05 -0700302 close();
303 }
304
305 @Override
306 public synchronized void close() {
Igor Murashkine1442202014-06-09 17:51:24 -0700307
Igor Murashkin0b27d342014-05-30 09:45:05 -0700308 if (mClosed) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700309 if (VERBOSE) Log.v(TAG, mIdString + "close - reentering");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700310 return;
311 }
312
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700313 if (VERBOSE) Log.v(TAG, mIdString + "close - first time");
Igor Murashkine1442202014-06-09 17:51:24 -0700314
Igor Murashkin0b27d342014-05-30 09:45:05 -0700315 mClosed = true;
316
317 /*
318 * Flush out any repeating request. Since camera is closed, no new requests
319 * can be queued, and eventually the entire request queue will be drained.
320 *
Igor Murashkine1442202014-06-09 17:51:24 -0700321 * If the camera device was already closed, short circuit and do nothing; since
322 * no more internal device callbacks will fire anyway.
323 *
324 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
325 * camera. Once that's done, fire #onClosed.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700326 */
327 try {
328 mDeviceImpl.stopRepeating();
Igor Murashkine1442202014-06-09 17:51:24 -0700329 } catch (IllegalStateException e) {
330 // OK: Camera device may already be closed, nothing else to do
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700331 Log.w(TAG, mIdString + "The camera device was already closed: ", e);
Igor Murashkine1442202014-06-09 17:51:24 -0700332
333 // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
334 // or just suppress the ISE only and rely onClosed.
335 // Also skip any of the draining work if this is already closed.
336
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700337 // Short-circuit; queue callback immediately and return
338 mStateCallback.onClosed(this);
Igor Murashkine1442202014-06-09 17:51:24 -0700339 return;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700340 } catch (CameraAccessException e) {
341 // OK: close does not throw checked exceptions.
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700342 Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700343
344 // TODO: call onError instead of onClosed if this happens
345 }
346
347 // If no sequences are pending, fire #onClosed immediately
348 mSequenceDrainer.beginDrain();
349 }
350
351 /**
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700352 * Whether currently in mid-abort.
353 *
354 * <p>This is used by the implementation to set the capture failure
355 * reason, in lieu of more accurate error codes from the camera service.
356 * Unsynchronized to avoid deadlocks between simultaneous session->device,
357 * device->session calls.</p>
358 *
359 * <p>Package-private.</p>
360 */
361 boolean isAborting() {
362 return mAborting;
363 }
364
365 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700366 * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700367 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700368 private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
369 InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
370 HandlerDispatcher<StateCallback> handlerPassthrough =
371 new HandlerDispatcher<>(userCallbackSink, handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700372
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700373 return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700374 }
375
376 /**
377 * Forward callbacks from
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700378 * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700379 *
380 * <p>In particular, all calls are automatically split to go both to our own
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700381 * internal callback, and to the user-specified callback (by transparently posting
Igor Murashkin0b27d342014-05-30 09:45:05 -0700382 * to the user-specified handler).</p>
383 *
384 * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
385 */
386 @SuppressWarnings("deprecation")
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700387 private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
388 Handler handler, CaptureCallback callback) {
389 CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700390 @Override
391 public void onCaptureSequenceCompleted(CameraDevice camera,
392 int sequenceId, long frameNumber) {
393 finishPendingSequence(sequenceId);
394 }
395
396 @Override
397 public void onCaptureSequenceAborted(CameraDevice camera,
398 int sequenceId) {
399 finishPendingSequence(sequenceId);
400 }
401 };
402
403 /*
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700404 * Split the calls from the device callback into local callback and the following chain:
Igor Murashkin21547d62014-06-04 15:21:42 -0700405 * - replace the first CameraDevice arg with a CameraCaptureSession
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700406 * - duck type from device callback to session callback
Igor Murashkin0b27d342014-05-30 09:45:05 -0700407 * - then forward the call to a handler
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700408 * - then finally invoke the destination method on the session callback object
Igor Murashkin0b27d342014-05-30 09:45:05 -0700409 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700410 if (callback == null) {
411 // OK: API allows the user to not specify a callback, and the handler may
Eino-Ville Talvala7875a882014-07-31 12:47:07 -0700412 // also be null in that case. Collapse whole dispatch chain to only call the local
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700413 // callback
414 return localCallback;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700415 }
416
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700417 InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
418 new InvokeDispatcher<>(localCallback);
Eino-Ville Talvala7875a882014-07-31 12:47:07 -0700419
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700420 InvokeDispatcher<CaptureCallback> userCallbackSink =
421 new InvokeDispatcher<>(callback);
422 HandlerDispatcher<CaptureCallback> handlerPassthrough =
423 new HandlerDispatcher<>(userCallbackSink, handler);
424 DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
425 = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
426 ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700427 replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
428 /*argumentIndex*/0, this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700429
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700430 BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
431 new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700432 replaceDeviceWithSession,
433 localSink);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700434
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700435 return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700436 }
437
438 /**
439 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700440 * Create an internal state callback, to be invoked on the mDeviceHandler
Igor Murashkin0b27d342014-05-30 09:45:05 -0700441 *
442 * <p>It has a few behaviors:
443 * <ul>
444 * <li>Convert device state changes into session state changes.
445 * <li>Keep track of async tasks that the session began (idle, abort).
446 * </ul>
447 * </p>
448 * */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700449 CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700450 final CameraCaptureSession session = this;
451
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700452 return new CameraDeviceImpl.StateCallbackKK() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700453 private boolean mBusy = false;
454 private boolean mActive = false;
455
456 @Override
457 public void onOpened(CameraDevice camera) {
458 throw new AssertionError("Camera must already be open before creating a session");
459 }
460
461 @Override
462 public void onDisconnected(CameraDevice camera) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700463 if (VERBOSE) Log.v(TAG, mIdString + "onDisconnected");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700464 close();
465 }
466
467 @Override
468 public void onError(CameraDevice camera, int error) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700469 // Should not be reached, handled by device code
470 Log.wtf(TAG, mIdString + "Got device error " + error);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700471 }
472
473 @Override
474 public void onActive(CameraDevice camera) {
475 mIdleDrainer.taskStarted();
476 mActive = true;
477
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700478 if (VERBOSE) Log.v(TAG, mIdString + "onActive");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700479 mStateCallback.onActive(session);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700480 }
481
482 @Override
483 public void onIdle(CameraDevice camera) {
484 boolean isAborting;
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700485 if (VERBOSE) Log.v(TAG, mIdString + "onIdle");
486
Igor Murashkin0b27d342014-05-30 09:45:05 -0700487 synchronized (session) {
488 isAborting = mAborting;
489 }
490
491 /*
492 * Check which states we transitioned through:
493 *
494 * (ACTIVE -> IDLE)
495 * (BUSY -> IDLE)
496 *
497 * Note that this is also legal:
498 * (ACTIVE -> BUSY -> IDLE)
499 *
500 * and mark those tasks as finished
501 */
502 if (mBusy && isAborting) {
503 mAbortDrainer.taskFinished();
504
505 synchronized (session) {
506 mAborting = false;
507 }
508 }
509
510 if (mActive) {
511 mIdleDrainer.taskFinished();
512 }
513
514 mBusy = false;
515 mActive = false;
516
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700517 mStateCallback.onReady(session);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700518 }
519
520 @Override
521 public void onBusy(CameraDevice camera) {
522 mBusy = true;
523
524 // TODO: Queue captures during abort instead of failing them
525 // since the app won't be able to distinguish the two actives
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700526 // Don't signal the application since there's no clean mapping here
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700527 if (VERBOSE) Log.v(TAG, mIdString + "onBusy");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700528 }
529
530 @Override
531 public void onUnconfigured(CameraDevice camera) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700532 if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigured");
Sol Boucher4b3f8002014-06-05 13:47:24 -0700533 synchronized (session) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700534 // Ignore #onUnconfigured before #close is called.
535 //
536 // Normally, this is reached when this session is closed and no immediate other
537 // activity happens for the camera, in which case the camera is configured to
538 // null streams by this session and the UnconfigureDrainer task is started.
539 // However, we can also end up here if
540 //
541 // 1) Session is closed
542 // 2) New session is created before this session finishes closing, setting
543 // mSkipUnconfigure and therefore this session does not configure null or
544 // start the UnconfigureDrainer task.
545 // 3) And then the new session fails to be created, so onUnconfigured fires
546 // _anyway_.
547 // In this second case, need to not finish a task that was never started, so
548 // guard with mSkipUnconfigure
549 if (mClosed && mConfigureSuccess && !mSkipUnconfigure) {
Sol Boucher4b3f8002014-06-05 13:47:24 -0700550 mUnconfigureDrainer.taskFinished();
551 }
552 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700553 }
554 };
555
556 }
557
558 @Override
559 protected void finalize() throws Throwable {
560 try {
561 close();
562 } finally {
563 super.finalize();
564 }
565 }
566
Igor Murashkin0b27d342014-05-30 09:45:05 -0700567 private void checkNotClosed() {
568 if (mClosed) {
569 throw new IllegalStateException(
570 "Session has been closed; further changes are illegal.");
571 }
572 }
573
574 /**
575 * Notify the session that a pending capture sequence has just been queued.
576 *
577 * <p>During a shutdown/close, the session waits until all pending sessions are finished
578 * before taking any further steps to shut down itself.</p>
579 *
580 * @see #finishPendingSequence
581 */
582 private int addPendingSequence(int sequenceId) {
583 mSequenceDrainer.taskStarted(sequenceId);
584 return sequenceId;
585 }
586
587 /**
588 * Notify the session that a pending capture sequence is now finished.
589 *
590 * <p>During a shutdown/close, once all pending sequences finish, it is safe to
591 * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
592 */
593 private void finishPendingSequence(int sequenceId) {
594 mSequenceDrainer.taskFinished(sequenceId);
595 }
596
597 private class SequenceDrainListener implements TaskDrainer.DrainListener {
598 @Override
599 public void onDrained() {
600 /*
601 * No repeating request is set; and the capture queue has fully drained.
602 *
603 * If no captures were queued to begin with, and an abort was queued,
604 * it's still possible to get another BUSY before the last IDLE.
605 *
606 * If the camera is already "IDLE" and no aborts are pending,
607 * then the drain immediately finishes.
608 */
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700609 if (VERBOSE) Log.v(TAG, mIdString + "onSequenceDrained");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700610 mAbortDrainer.beginDrain();
611 }
612 }
613
614 private class AbortDrainListener implements TaskDrainer.DrainListener {
615 @Override
616 public void onDrained() {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700617 if (VERBOSE) Log.v(TAG, mIdString + "onAbortDrained");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700618 synchronized (CameraCaptureSessionImpl.this) {
619 /*
620 * Any queued aborts have now completed.
621 *
622 * It's now safe to wait to receive the final "IDLE" event, as the camera device
623 * will no longer again transition to "ACTIVE" by itself.
624 *
625 * If the camera is already "IDLE", then the drain immediately finishes.
626 */
627 mIdleDrainer.beginDrain();
628 }
629 }
630 }
631
632 private class IdleDrainListener implements TaskDrainer.DrainListener {
633 @Override
634 public void onDrained() {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700635 if (VERBOSE) Log.v(TAG, mIdString + "onIdleDrained");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700636 synchronized (CameraCaptureSessionImpl.this) {
637 /*
638 * The device is now IDLE, and has settled. It will not transition to
639 * ACTIVE or BUSY again by itself.
640 *
641 * It's now safe to unconfigure the outputs and after it's done invoke #onClosed.
642 *
643 * This operation is idempotent; a session will not be closed twice.
644 */
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700645 if (VERBOSE) Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700646 mSkipUnconfigure);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700647
648 // Fast path: A new capture session has replaced this one; don't unconfigure.
649 if (mSkipUnconfigure) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700650 mStateCallback.onClosed(CameraCaptureSessionImpl.this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700651 return;
652 }
653
654 // Slow path: #close was called explicitly on this session; unconfigure first
655
656 try {
657 mUnconfigureDrainer.taskStarted();
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700658 mDeviceImpl.configureOutputsChecked(null); // begin transition to unconfigured
Igor Murashkin0b27d342014-05-30 09:45:05 -0700659 } catch (CameraAccessException e) {
660 // OK: do not throw checked exceptions.
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700661 Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700662
663 // TODO: call onError instead of onClosed if this happens
664 }
665
666 mUnconfigureDrainer.beginDrain();
667 }
668 }
669 }
670
671 private class UnconfigureDrainListener implements TaskDrainer.DrainListener {
672 @Override
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700673
Igor Murashkin0b27d342014-05-30 09:45:05 -0700674 public void onDrained() {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700675 if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigureDrained");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700676 synchronized (CameraCaptureSessionImpl.this) {
677 // The device has finished unconfiguring. It's now fully closed.
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700678 mStateCallback.onClosed(CameraCaptureSessionImpl.this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700679 }
680 }
681 }
682}