blob: 616fc506612d9a71d57ba45eab574e85a6e4b3f9 [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;
Igor Murashkin0b27d342014-05-30 09:45:05 -070024import android.hardware.camera2.dispatch.DuckTypingDispatcher;
25import android.hardware.camera2.dispatch.HandlerDispatcher;
26import android.hardware.camera2.dispatch.InvokeDispatcher;
Igor Murashkin0b27d342014-05-30 09:45:05 -070027import android.hardware.camera2.utils.TaskDrainer;
28import android.hardware.camera2.utils.TaskSingleDrainer;
29import android.os.Handler;
30import android.util.Log;
31import android.view.Surface;
32
33import java.util.Arrays;
34import java.util.List;
35
Igor Murashkin21547d62014-06-04 15:21:42 -070036import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
Igor Murashkin0b27d342014-05-30 09:45:05 -070037import static com.android.internal.util.Preconditions.*;
38
39public class CameraCaptureSessionImpl extends CameraCaptureSession {
40 private static final String TAG = "CameraCaptureSession";
41 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
42
Eino-Ville Talvala95037852014-09-14 14:07:05 -070043 /** Simple integer ID for session for debugging */
44 private final int mId;
45 private final String mIdString;
46
Chien-Yu Chen5398a672015-03-19 14:48:43 -070047 /** Input surface configured by native camera framework based on user-specified configuration */
48 private final Surface mInput;
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;
Igor Murashkin0b27d342014-05-30 09:45:05 -070070
71 /** This session is closed; all further calls will throw ISE */
72 private boolean mClosed = false;
Igor Murashkin10fbbbb2014-08-19 16:19:30 -070073 /** This session failed to be configured successfully */
74 private final boolean mConfigureSuccess;
Igor Murashkin0b27d342014-05-30 09:45:05 -070075 /** Do not unconfigure if this is set; another session will overwrite configuration */
76 private boolean mSkipUnconfigure = false;
77
78 /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -070079 private volatile boolean mAborting;
Igor Murashkin0b27d342014-05-30 09:45:05 -070080
81 /**
82 * Create a new CameraCaptureSession.
83 *
84 * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
85 * There must be no pending actions
86 * (e.g. no pending captures, no repeating requests, no flush).</p>
87 */
Chien-Yu Chen5398a672015-03-19 14:48:43 -070088 CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070089 CameraCaptureSession.StateCallback callback, Handler stateHandler,
Igor Murashkin21547d62014-06-04 15:21:42 -070090 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Igor Murashkin0b27d342014-05-30 09:45:05 -070091 Handler deviceStateHandler, boolean configureSuccess) {
92 if (outputs == null || outputs.isEmpty()) {
93 throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070094 } else if (callback == null) {
95 throw new IllegalArgumentException("callback must not be null");
Igor Murashkin0b27d342014-05-30 09:45:05 -070096 }
97
Eino-Ville Talvala95037852014-09-14 14:07:05 -070098 mId = id;
99 mIdString = String.format("Session %d: ", mId);
100
Igor Murashkin0b27d342014-05-30 09:45:05 -0700101 // TODO: extra verification of outputs
102 mOutputs = outputs;
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700103 mInput = input;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700104 mStateHandler = checkHandler(stateHandler);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700105 mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700106
107 mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
108 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
109
110 /*
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700111 * Use the same handler as the device's StateCallback for all the internal coming events
Igor Murashkin0b27d342014-05-30 09:45:05 -0700112 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700113 * This ensures total ordering between CameraDevice.StateCallback and
114 * CameraDeviceImpl.CaptureCallback events.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700115 */
116 mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
117 /*name*/"seq");
118 mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
119 /*name*/"idle");
120 mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
121 /*name*/"abort");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700122
123 // CameraDevice should call configureOutputs and have it finish before constructing us
124
125 if (configureSuccess) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700126 mStateCallback.onConfigured(this);
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700127 if (VERBOSE) Log.v(TAG, mIdString + "Created session successfully");
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700128 mConfigureSuccess = true;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700129 } else {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700130 mStateCallback.onConfigureFailed(this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700131 mClosed = true; // do not fire any other callbacks, do not allow any other work
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700132 Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700133 mConfigureSuccess = false;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700134 }
135 }
136
137 @Override
138 public CameraDevice getDevice() {
139 return mDeviceImpl;
140 }
141
142 @Override
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700143 public void prepare(Surface surface) throws CameraAccessException {
144 mDeviceImpl.prepare(surface);
145 }
146
147 @Override
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700148 public synchronized int capture(CaptureRequest request, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700149 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700150 if (request == null) {
151 throw new IllegalArgumentException("request must not be null");
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700152 } else if (request.isReprocess() && !isReprocessable()) {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700153 throw new IllegalArgumentException("this capture session cannot handle reprocess " +
154 "requests");
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700155 } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -0700156 throw new IllegalArgumentException("capture request was created for another session");
Igor Murashkine1442202014-06-09 17:51:24 -0700157 }
158
Igor Murashkin0b27d342014-05-30 09:45:05 -0700159 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700160
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700161 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700162
163 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700164 Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
165 " handler " + handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700166 }
167
168 return addPendingSequence(mDeviceImpl.capture(request,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700169 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700170 }
171
172 @Override
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700173 public synchronized int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700174 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700175 if (requests == null) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700176 throw new IllegalArgumentException("Requests must not be null");
Igor Murashkine1442202014-06-09 17:51:24 -0700177 } else if (requests.isEmpty()) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700178 throw new IllegalArgumentException("Requests must have at least one element");
Igor Murashkine1442202014-06-09 17:51:24 -0700179 }
180
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700181 for (CaptureRequest request : requests) {
182 if (request.isReprocess()) {
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700183 if (!isReprocessable()) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700184 throw new IllegalArgumentException("This capture session cannot handle " +
185 "reprocess requests");
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700186 } else if (request.getReprocessableSessionId() != mId) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700187 throw new IllegalArgumentException("Capture request was created for another " +
188 "session");
189 }
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700190 }
191 }
192
Igor Murashkin0b27d342014-05-30 09:45:05 -0700193 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700194
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700195 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700196
197 if (VERBOSE) {
198 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700199 Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
200 ", callback " + callback + " handler " + handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700201 }
202
203 return addPendingSequence(mDeviceImpl.captureBurst(requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700204 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700205 }
206
207 @Override
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700208 public synchronized int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700209 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700210 if (request == null) {
211 throw new IllegalArgumentException("request must not be null");
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700212 } else if (request.isReprocess()) {
213 throw new IllegalArgumentException("repeating reprocess requests are not supported");
Igor Murashkine1442202014-06-09 17:51:24 -0700214 }
215
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700216
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
Igor Murashkine1442202014-06-09 17:51:24 -0700221 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700222 Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
223 callback + " handler" + " " + handler);
Igor Murashkine1442202014-06-09 17:51:24 -0700224 }
225
Igor Murashkin0b27d342014-05-30 09:45:05 -0700226 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700227 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700228 }
229
230 @Override
231 public synchronized int setRepeatingBurst(List<CaptureRequest> requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700232 CaptureCallback callback, Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700233 if (requests == null) {
234 throw new IllegalArgumentException("requests must not be null");
235 } else if (requests.isEmpty()) {
236 throw new IllegalArgumentException("requests must have at least one element");
237 }
238
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700239 for (CaptureRequest r : requests) {
240 if (r.isReprocess()) {
241 throw new IllegalArgumentException("repeating reprocess burst requests are not " +
242 "supported");
243 }
244 }
245
Igor Murashkin0b27d342014-05-30 09:45:05 -0700246 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700247
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700248 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700249
250 if (VERBOSE) {
251 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700252 Log.v(TAG, mIdString + "setRepeatingBurst - requests " + Arrays.toString(requestArray) +
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700253 ", callback " + callback + " handler" + "" + handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700254 }
255
256 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700257 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700258 }
259
260 @Override
261 public synchronized void stopRepeating() throws CameraAccessException {
262 checkNotClosed();
263
264 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700265 Log.v(TAG, mIdString + "stopRepeating");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700266 }
267
268 mDeviceImpl.stopRepeating();
269 }
270
271 @Override
272 public synchronized void abortCaptures() throws CameraAccessException {
273 checkNotClosed();
274
275 if (VERBOSE) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700276 Log.v(TAG, mIdString + "abortCaptures");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700277 }
278
279 if (mAborting) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700280 Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700281 return;
282 }
283
284 mAborting = true;
285 mAbortDrainer.taskStarted();
286
287 mDeviceImpl.flush();
288 // The next BUSY -> IDLE set of transitions will mark the end of the abort.
289 }
290
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700291 @Override
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700292 public boolean isReprocessable() {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700293 return mInput != null;
294 }
295
296 @Override
297 public Surface getInputSurface() {
298 return mInput;
299 }
300
Igor Murashkin0b27d342014-05-30 09:45:05 -0700301 /**
302 * Replace this session with another session.
303 *
304 * <p>This is an optimization to avoid unconfiguring and then immediately having to
305 * reconfigure again.</p>
306 *
307 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
308 * <p>
309 *
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700310 * <p>After this call completes, the session will not call any further methods on the camera
311 * device.</p>
312 *
Igor Murashkin0b27d342014-05-30 09:45:05 -0700313 * @see CameraCaptureSession#close
314 */
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700315 synchronized void replaceSessionClose() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700316 /*
317 * In order for creating new sessions to be fast, the new session should be created
318 * before the old session is closed.
319 *
320 * Otherwise the old session will always unconfigure if there is no new session to
321 * replace it.
322 *
323 * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
324 * to skip unconfigure if a new session is created before the captures are all drained,
325 * but this would introduce nondeterministic behavior.
326 */
327
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700328 if (VERBOSE) Log.v(TAG, mIdString + "replaceSessionClose");
Igor Murashkine1442202014-06-09 17:51:24 -0700329
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700330 // Set up fast shutdown. Possible alternative paths:
331 // - This session is active, so close() below starts the shutdown drain
332 // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
333 // - This session is already closed and has executed the idle drain listener, and
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700334 // configureOutputsChecked(null) has already been called.
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700335 //
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700336 // Do not call configureOutputsChecked(null) going forward, since it would race with the
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700337 // configuration for the new session. If it was already called, then we don't care, since it
338 // won't get called again.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700339 mSkipUnconfigure = true;
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700340
Igor Murashkin0b27d342014-05-30 09:45:05 -0700341 close();
342 }
343
344 @Override
345 public synchronized void close() {
Igor Murashkine1442202014-06-09 17:51:24 -0700346
Igor Murashkin0b27d342014-05-30 09:45:05 -0700347 if (mClosed) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700348 if (VERBOSE) Log.v(TAG, mIdString + "close - reentering");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700349 return;
350 }
351
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700352 if (VERBOSE) Log.v(TAG, mIdString + "close - first time");
Igor Murashkine1442202014-06-09 17:51:24 -0700353
Igor Murashkin0b27d342014-05-30 09:45:05 -0700354 mClosed = true;
355
356 /*
357 * Flush out any repeating request. Since camera is closed, no new requests
358 * can be queued, and eventually the entire request queue will be drained.
359 *
Igor Murashkine1442202014-06-09 17:51:24 -0700360 * If the camera device was already closed, short circuit and do nothing; since
361 * no more internal device callbacks will fire anyway.
362 *
363 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
364 * camera. Once that's done, fire #onClosed.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700365 */
366 try {
367 mDeviceImpl.stopRepeating();
Igor Murashkine1442202014-06-09 17:51:24 -0700368 } catch (IllegalStateException e) {
369 // OK: Camera device may already be closed, nothing else to do
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700370 Log.w(TAG, mIdString + "The camera device was already closed: ", e);
Igor Murashkine1442202014-06-09 17:51:24 -0700371
372 // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
373 // or just suppress the ISE only and rely onClosed.
374 // Also skip any of the draining work if this is already closed.
375
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700376 // Short-circuit; queue callback immediately and return
377 mStateCallback.onClosed(this);
Igor Murashkine1442202014-06-09 17:51:24 -0700378 return;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700379 } catch (CameraAccessException e) {
380 // OK: close does not throw checked exceptions.
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700381 Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700382
383 // TODO: call onError instead of onClosed if this happens
384 }
385
386 // If no sequences are pending, fire #onClosed immediately
387 mSequenceDrainer.beginDrain();
388 }
389
390 /**
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700391 * Whether currently in mid-abort.
392 *
393 * <p>This is used by the implementation to set the capture failure
394 * reason, in lieu of more accurate error codes from the camera service.
395 * Unsynchronized to avoid deadlocks between simultaneous session->device,
396 * device->session calls.</p>
397 *
398 * <p>Package-private.</p>
399 */
400 boolean isAborting() {
401 return mAborting;
402 }
403
404 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700405 * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700406 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700407 private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
408 InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
409 HandlerDispatcher<StateCallback> handlerPassthrough =
410 new HandlerDispatcher<>(userCallbackSink, handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700411
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700412 return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700413 }
414
415 /**
416 * Forward callbacks from
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700417 * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700418 *
419 * <p>In particular, all calls are automatically split to go both to our own
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700420 * internal callback, and to the user-specified callback (by transparently posting
Igor Murashkin0b27d342014-05-30 09:45:05 -0700421 * to the user-specified handler).</p>
422 *
423 * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
424 */
425 @SuppressWarnings("deprecation")
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700426 private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
427 Handler handler, CaptureCallback callback) {
428 CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700429 @Override
430 public void onCaptureSequenceCompleted(CameraDevice camera,
431 int sequenceId, long frameNumber) {
432 finishPendingSequence(sequenceId);
433 }
434
435 @Override
436 public void onCaptureSequenceAborted(CameraDevice camera,
437 int sequenceId) {
438 finishPendingSequence(sequenceId);
439 }
440 };
441
442 /*
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700443 * Split the calls from the device callback into local callback and the following chain:
Igor Murashkin21547d62014-06-04 15:21:42 -0700444 * - replace the first CameraDevice arg with a CameraCaptureSession
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700445 * - duck type from device callback to session callback
Igor Murashkin0b27d342014-05-30 09:45:05 -0700446 * - then forward the call to a handler
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700447 * - then finally invoke the destination method on the session callback object
Igor Murashkin0b27d342014-05-30 09:45:05 -0700448 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700449 if (callback == null) {
450 // OK: API allows the user to not specify a callback, and the handler may
Eino-Ville Talvala7875a882014-07-31 12:47:07 -0700451 // also be null in that case. Collapse whole dispatch chain to only call the local
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700452 // callback
453 return localCallback;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700454 }
455
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700456 InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
457 new InvokeDispatcher<>(localCallback);
Eino-Ville Talvala7875a882014-07-31 12:47:07 -0700458
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700459 InvokeDispatcher<CaptureCallback> userCallbackSink =
460 new InvokeDispatcher<>(callback);
461 HandlerDispatcher<CaptureCallback> handlerPassthrough =
462 new HandlerDispatcher<>(userCallbackSink, handler);
463 DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
464 = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
465 ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700466 replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
467 /*argumentIndex*/0, this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700468
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700469 BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
470 new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700471 replaceDeviceWithSession,
472 localSink);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700473
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700474 return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700475 }
476
477 /**
478 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700479 * Create an internal state callback, to be invoked on the mDeviceHandler
Igor Murashkin0b27d342014-05-30 09:45:05 -0700480 *
481 * <p>It has a few behaviors:
482 * <ul>
483 * <li>Convert device state changes into session state changes.
484 * <li>Keep track of async tasks that the session began (idle, abort).
485 * </ul>
486 * </p>
487 * */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700488 CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700489 final CameraCaptureSession session = this;
490
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700491 return new CameraDeviceImpl.StateCallbackKK() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700492 private boolean mBusy = false;
493 private boolean mActive = false;
494
495 @Override
496 public void onOpened(CameraDevice camera) {
497 throw new AssertionError("Camera must already be open before creating a session");
498 }
499
500 @Override
501 public void onDisconnected(CameraDevice camera) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700502 if (VERBOSE) Log.v(TAG, mIdString + "onDisconnected");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700503 close();
504 }
505
506 @Override
507 public void onError(CameraDevice camera, int error) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700508 // Should not be reached, handled by device code
509 Log.wtf(TAG, mIdString + "Got device error " + error);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700510 }
511
512 @Override
513 public void onActive(CameraDevice camera) {
514 mIdleDrainer.taskStarted();
515 mActive = true;
516
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700517 if (VERBOSE) Log.v(TAG, mIdString + "onActive");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700518 mStateCallback.onActive(session);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700519 }
520
521 @Override
522 public void onIdle(CameraDevice camera) {
523 boolean isAborting;
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700524 if (VERBOSE) Log.v(TAG, mIdString + "onIdle");
525
Igor Murashkin0b27d342014-05-30 09:45:05 -0700526 synchronized (session) {
527 isAborting = mAborting;
528 }
529
530 /*
531 * Check which states we transitioned through:
532 *
533 * (ACTIVE -> IDLE)
534 * (BUSY -> IDLE)
535 *
536 * Note that this is also legal:
537 * (ACTIVE -> BUSY -> IDLE)
538 *
539 * and mark those tasks as finished
540 */
541 if (mBusy && isAborting) {
542 mAbortDrainer.taskFinished();
543
544 synchronized (session) {
545 mAborting = false;
546 }
547 }
548
549 if (mActive) {
550 mIdleDrainer.taskFinished();
551 }
552
553 mBusy = false;
554 mActive = false;
555
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700556 mStateCallback.onReady(session);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700557 }
558
559 @Override
560 public void onBusy(CameraDevice camera) {
561 mBusy = true;
562
563 // TODO: Queue captures during abort instead of failing them
564 // since the app won't be able to distinguish the two actives
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700565 // Don't signal the application since there's no clean mapping here
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700566 if (VERBOSE) Log.v(TAG, mIdString + "onBusy");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700567 }
568
569 @Override
570 public void onUnconfigured(CameraDevice camera) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700571 if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigured");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700572 }
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700573
574 @Override
575 public void onSurfacePrepared(Surface surface) {
576 if (VERBOSE) Log.v(TAG, mIdString + "onPrepared");
577 mStateCallback.onSurfacePrepared(session, surface);
578 }
579
Igor Murashkin0b27d342014-05-30 09:45:05 -0700580 };
581
582 }
583
584 @Override
585 protected void finalize() throws Throwable {
586 try {
587 close();
588 } finally {
589 super.finalize();
590 }
591 }
592
Igor Murashkin0b27d342014-05-30 09:45:05 -0700593 private void checkNotClosed() {
594 if (mClosed) {
595 throw new IllegalStateException(
596 "Session has been closed; further changes are illegal.");
597 }
598 }
599
600 /**
601 * Notify the session that a pending capture sequence has just been queued.
602 *
603 * <p>During a shutdown/close, the session waits until all pending sessions are finished
604 * before taking any further steps to shut down itself.</p>
605 *
606 * @see #finishPendingSequence
607 */
608 private int addPendingSequence(int sequenceId) {
609 mSequenceDrainer.taskStarted(sequenceId);
610 return sequenceId;
611 }
612
613 /**
614 * Notify the session that a pending capture sequence is now finished.
615 *
616 * <p>During a shutdown/close, once all pending sequences finish, it is safe to
617 * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
618 */
619 private void finishPendingSequence(int sequenceId) {
620 mSequenceDrainer.taskFinished(sequenceId);
621 }
622
623 private class SequenceDrainListener implements TaskDrainer.DrainListener {
624 @Override
625 public void onDrained() {
626 /*
627 * No repeating request is set; and the capture queue has fully drained.
628 *
629 * If no captures were queued to begin with, and an abort was queued,
630 * it's still possible to get another BUSY before the last IDLE.
631 *
632 * If the camera is already "IDLE" and no aborts are pending,
633 * then the drain immediately finishes.
634 */
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700635 if (VERBOSE) Log.v(TAG, mIdString + "onSequenceDrained");
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700636
637
638 // Fire session close as soon as all sequences are complete.
639 // We may still need to unconfigure the device, but a new session might be created
640 // past this point, and notifications would then stop to this instance.
641 mStateCallback.onClosed(CameraCaptureSessionImpl.this);
642
643 // Fast path: A new capture session has replaced this one; don't wait for abort/idle
644 // as we won't get state updates any more anyway.
645 if (mSkipUnconfigure) {
646 return;
647 }
648
Igor Murashkin0b27d342014-05-30 09:45:05 -0700649 mAbortDrainer.beginDrain();
650 }
651 }
652
653 private class AbortDrainListener implements TaskDrainer.DrainListener {
654 @Override
655 public void onDrained() {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700656 if (VERBOSE) Log.v(TAG, mIdString + "onAbortDrained");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700657 synchronized (CameraCaptureSessionImpl.this) {
658 /*
659 * Any queued aborts have now completed.
660 *
661 * It's now safe to wait to receive the final "IDLE" event, as the camera device
662 * will no longer again transition to "ACTIVE" by itself.
663 *
664 * If the camera is already "IDLE", then the drain immediately finishes.
665 */
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700666
667 // Fast path: A new capture session has replaced this one; don't wait for idle
668 // as we won't get state updates any more anyway.
669 if (mSkipUnconfigure) {
670 return;
671 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700672 mIdleDrainer.beginDrain();
673 }
674 }
675 }
676
677 private class IdleDrainListener implements TaskDrainer.DrainListener {
678 @Override
679 public void onDrained() {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700680 if (VERBOSE) Log.v(TAG, mIdString + "onIdleDrained");
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700681
682 // Take device lock before session lock so that we can call back into device
683 // without causing a deadlock
684 synchronized (mDeviceImpl.mInterfaceLock) {
685 synchronized (CameraCaptureSessionImpl.this) {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700686 /*
687 * The device is now IDLE, and has settled. It will not transition to
688 * ACTIVE or BUSY again by itself.
689 *
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700690 * It's now safe to unconfigure the outputs.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700691 *
692 * This operation is idempotent; a session will not be closed twice.
693 */
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700694 if (VERBOSE)
695 Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
696 mSkipUnconfigure);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700697
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700698 // Fast path: A new capture session has replaced this one; don't wait for idle
699 // as we won't get state updates any more anyway.
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700700 if (mSkipUnconfigure) {
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700701 return;
702 }
703
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700704 // Final slow path: unconfigure the camera, no session has replaced us and
705 // everything is idle.
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700706 try {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700707 // begin transition to unconfigured
708 mDeviceImpl.configureStreamsChecked(null, null);
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700709 } catch (CameraAccessException e) {
710 // OK: do not throw checked exceptions.
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700711 Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700712
713 // TODO: call onError instead of onClosed if this happens
Eino-Ville Talvala56fbbea2014-09-23 18:18:07 -0700714 } catch (IllegalStateException e) {
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700715 // Camera is already closed, so nothing left to do
Eino-Ville Talvala56fbbea2014-09-23 18:18:07 -0700716 if (VERBOSE) Log.v(TAG, mIdString +
717 "Camera was already closed or busy, skipping unconfigure");
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700718 }
719
Igor Murashkin0b27d342014-05-30 09:45:05 -0700720 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700721 }
722 }
723 }
724
Igor Murashkin0b27d342014-05-30 09:45:05 -0700725}