blob: c7654c9e74e18aac250bc487fada7f7929949471 [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;
Eino-Ville Talvala0e04e912017-02-28 17:49:41 -080022import android.hardware.camera2.ICameraDeviceUser;
Igor Murashkin21547d62014-06-04 15:21:42 -070023import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
Igor Murashkin0b27d342014-05-30 09:45:05 -070024import android.hardware.camera2.dispatch.BroadcastDispatcher;
Igor Murashkin0b27d342014-05-30 09:45:05 -070025import android.hardware.camera2.dispatch.DuckTypingDispatcher;
26import android.hardware.camera2.dispatch.HandlerDispatcher;
27import android.hardware.camera2.dispatch.InvokeDispatcher;
Zhijun Hec8b181e2016-05-30 14:54:39 -070028import android.hardware.camera2.params.OutputConfiguration;
Igor Murashkin0b27d342014-05-30 09:45:05 -070029import 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
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -070041public class CameraCaptureSessionImpl extends CameraCaptureSession
42 implements CameraCaptureSessionCore {
Igor Murashkin0b27d342014-05-30 09:45:05 -070043 private static final String TAG = "CameraCaptureSession";
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070044 private static final boolean DEBUG = false;
Igor Murashkin0b27d342014-05-30 09:45:05 -070045
Eino-Ville Talvala95037852014-09-14 14:07:05 -070046 /** Simple integer ID for session for debugging */
47 private final int mId;
48 private final String mIdString;
49
Chien-Yu Chen5398a672015-03-19 14:48:43 -070050 /** Input surface configured by native camera framework based on user-specified configuration */
51 private final Surface mInput;
Igor Murashkin0b27d342014-05-30 09:45:05 -070052 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070053 * User-specified state callback, used for outgoing events; calls to this object will be
Igor Murashkin0b27d342014-05-30 09:45:05 -070054 * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
55 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070056 private final CameraCaptureSession.StateCallback mStateCallback;
57 /** User-specified state handler used for outgoing state callback events */
Igor Murashkin0b27d342014-05-30 09:45:05 -070058 private final Handler mStateHandler;
59
60 /** Internal camera device; used to translate calls into existing deprecated API */
Igor Murashkin21547d62014-06-04 15:21:42 -070061 private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
Igor Murashkin0b27d342014-05-30 09:45:05 -070062 /** Internal handler; used for all incoming events to preserve total order */
63 private final Handler mDeviceHandler;
64
65 /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
66 private final TaskDrainer<Integer> mSequenceDrainer;
67 /** Drain state transitions from ACTIVE -> IDLE */
68 private final TaskSingleDrainer mIdleDrainer;
69 /** Drain state transitions from BUSY -> IDLE */
70 private final TaskSingleDrainer mAbortDrainer;
Igor Murashkin0b27d342014-05-30 09:45:05 -070071
72 /** This session is closed; all further calls will throw ISE */
73 private boolean mClosed = false;
Igor Murashkin10fbbbb2014-08-19 16:19:30 -070074 /** This session failed to be configured successfully */
75 private final boolean mConfigureSuccess;
Igor Murashkin0b27d342014-05-30 09:45:05 -070076 /** Do not unconfigure if this is set; another session will overwrite configuration */
77 private boolean mSkipUnconfigure = false;
78
79 /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -070080 private volatile boolean mAborting;
Igor Murashkin0b27d342014-05-30 09:45:05 -070081
82 /**
83 * Create a new CameraCaptureSession.
84 *
85 * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
86 * There must be no pending actions
87 * (e.g. no pending captures, no repeating requests, no flush).</p>
88 */
Shuzhen Wang9c663d42016-10-28 23:25:02 -070089 CameraCaptureSessionImpl(int id, Surface input,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070090 CameraCaptureSession.StateCallback callback, Handler stateHandler,
Igor Murashkin21547d62014-06-04 15:21:42 -070091 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -070092 Handler deviceStateHandler, boolean configureSuccess) {
Shuzhen Wang9c663d42016-10-28 23:25:02 -070093 if (callback == null) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -070094 throw new IllegalArgumentException("callback must not be null");
Igor Murashkin0b27d342014-05-30 09:45:05 -070095 }
96
Eino-Ville Talvala95037852014-09-14 14:07:05 -070097 mId = id;
98 mIdString = String.format("Session %d: ", mId);
99
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700100 mInput = input;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700101 mStateHandler = checkHandler(stateHandler);
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700102 mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700103
104 mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
105 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
106
107 /*
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700108 * Use the same handler as the device's StateCallback for all the internal coming events
Igor Murashkin0b27d342014-05-30 09:45:05 -0700109 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700110 * This ensures total ordering between CameraDevice.StateCallback and
111 * CameraDeviceImpl.CaptureCallback events.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700112 */
113 mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
114 /*name*/"seq");
115 mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
116 /*name*/"idle");
117 mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
118 /*name*/"abort");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700119
120 // CameraDevice should call configureOutputs and have it finish before constructing us
121
122 if (configureSuccess) {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700123 mStateCallback.onConfigured(this);
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700124 if (DEBUG) Log.v(TAG, mIdString + "Created session successfully");
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700125 mConfigureSuccess = true;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700126 } else {
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700127 mStateCallback.onConfigureFailed(this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700128 mClosed = true; // do not fire any other callbacks, do not allow any other work
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700129 Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
Igor Murashkin10fbbbb2014-08-19 16:19:30 -0700130 mConfigureSuccess = false;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700131 }
132 }
133
134 @Override
135 public CameraDevice getDevice() {
136 return mDeviceImpl;
137 }
138
139 @Override
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700140 public void prepare(Surface surface) throws CameraAccessException {
141 mDeviceImpl.prepare(surface);
142 }
143
144 @Override
Ruben Brunk7ed1aaa2015-08-13 17:57:14 -0700145 public void prepare(int maxCount, Surface surface) throws CameraAccessException {
146 mDeviceImpl.prepare(maxCount, surface);
147 }
148
149 @Override
Eino-Ville Talvala14c09fa2015-07-15 16:04:04 -0700150 public void tearDown(Surface surface) throws CameraAccessException {
151 mDeviceImpl.tearDown(surface);
152 }
153
154 @Override
Shuzhen Wang4bd7abe2017-01-11 09:58:58 -0800155 public void finalizeOutputConfigurations(
156 List<OutputConfiguration> outputConfigs) throws CameraAccessException {
157 mDeviceImpl.finalizeOutputConfigs(outputConfigs);
Zhijun Hec8b181e2016-05-30 14:54:39 -0700158 }
159
160 @Override
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700161 public int capture(CaptureRequest request, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700162 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700163 if (request == null) {
164 throw new IllegalArgumentException("request must not be null");
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700165 } else if (request.isReprocess() && !isReprocessable()) {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700166 throw new IllegalArgumentException("this capture session cannot handle reprocess " +
167 "requests");
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700168 } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
Chien-Yu Chen7a316f6b2015-04-13 14:31:45 -0700169 throw new IllegalArgumentException("capture request was created for another session");
Igor Murashkine1442202014-06-09 17:51:24 -0700170 }
171
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700172 synchronized (mDeviceImpl.mInterfaceLock) {
173 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700174
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700175 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700176
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700177 if (DEBUG) {
178 Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
179 " handler " + handler);
180 }
181
182 return addPendingSequence(mDeviceImpl.capture(request,
183 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700184 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700185 }
186
187 @Override
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700188 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700189 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700190 if (requests == null) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700191 throw new IllegalArgumentException("Requests must not be null");
Igor Murashkine1442202014-06-09 17:51:24 -0700192 } else if (requests.isEmpty()) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700193 throw new IllegalArgumentException("Requests must have at least one element");
Igor Murashkine1442202014-06-09 17:51:24 -0700194 }
195
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700196 for (CaptureRequest request : requests) {
197 if (request.isReprocess()) {
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700198 if (!isReprocessable()) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700199 throw new IllegalArgumentException("This capture session cannot handle " +
200 "reprocess requests");
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700201 } else if (request.getReprocessableSessionId() != mId) {
Chien-Yu Chen4560df72015-04-27 17:21:31 -0700202 throw new IllegalArgumentException("Capture request was created for another " +
203 "session");
204 }
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700205 }
206 }
207
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700208 synchronized (mDeviceImpl.mInterfaceLock) {
209 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700210
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700211 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700212
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700213 if (DEBUG) {
214 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
215 Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
216 ", callback " + callback + " handler " + handler);
217 }
218
219 return addPendingSequence(mDeviceImpl.captureBurst(requests,
220 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700221 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700222 }
223
224 @Override
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700225 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
Igor Murashkin0b27d342014-05-30 09:45:05 -0700226 Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700227 if (request == null) {
228 throw new IllegalArgumentException("request must not be null");
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700229 } else if (request.isReprocess()) {
230 throw new IllegalArgumentException("repeating reprocess requests are not supported");
Igor Murashkine1442202014-06-09 17:51:24 -0700231 }
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700232
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700233 synchronized (mDeviceImpl.mInterfaceLock) {
234 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700235
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700236 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700237
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700238 if (DEBUG) {
239 Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
240 callback + " handler" + " " + handler);
241 }
242
243 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
244 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkine1442202014-06-09 17:51:24 -0700245 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700246 }
247
248 @Override
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700249 public int setRepeatingBurst(List<CaptureRequest> requests,
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700250 CaptureCallback callback, Handler handler) throws CameraAccessException {
Igor Murashkine1442202014-06-09 17:51:24 -0700251 if (requests == null) {
252 throw new IllegalArgumentException("requests must not be null");
253 } else if (requests.isEmpty()) {
254 throw new IllegalArgumentException("requests must have at least one element");
255 }
256
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700257 for (CaptureRequest r : requests) {
258 if (r.isReprocess()) {
259 throw new IllegalArgumentException("repeating reprocess burst requests are not " +
260 "supported");
261 }
262 }
263
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700264 synchronized (mDeviceImpl.mInterfaceLock) {
265 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700266
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700267 handler = checkHandler(handler, callback);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700268
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700269 if (DEBUG) {
270 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
271 Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
272 Arrays.toString(requestArray) + ", callback " + callback +
273 " handler" + "" + handler);
274 }
275
276 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
277 createCaptureCallbackProxy(handler, callback), mDeviceHandler));
Igor Murashkin0b27d342014-05-30 09:45:05 -0700278 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700279 }
280
281 @Override
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700282 public void stopRepeating() throws CameraAccessException {
283 synchronized (mDeviceImpl.mInterfaceLock) {
284 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700285
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700286 if (DEBUG) {
287 Log.v(TAG, mIdString + "stopRepeating");
288 }
289
290 mDeviceImpl.stopRepeating();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700291 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700292 }
293
294 @Override
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700295 public void abortCaptures() throws CameraAccessException {
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700296 synchronized (mDeviceImpl.mInterfaceLock) {
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700297 checkNotClosed();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700298
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700299 if (DEBUG) {
300 Log.v(TAG, mIdString + "abortCaptures");
301 }
302
303 if (mAborting) {
304 Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
305 return;
306 }
307
308 mAborting = true;
309 mAbortDrainer.taskStarted();
Igor Murashkin0b27d342014-05-30 09:45:05 -0700310
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700311 mDeviceImpl.flush();
312 // The next BUSY -> IDLE set of transitions will mark the end of the abort.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700313 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700314 }
315
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700316 @Override
Chien-Yu Chen8062d312015-05-12 14:24:10 -0700317 public boolean isReprocessable() {
Chien-Yu Chen5398a672015-03-19 14:48:43 -0700318 return mInput != null;
319 }
320
321 @Override
322 public Surface getInputSurface() {
323 return mInput;
324 }
325
Igor Murashkin0b27d342014-05-30 09:45:05 -0700326 /**
327 * Replace this session with another session.
328 *
329 * <p>This is an optimization to avoid unconfiguring and then immediately having to
330 * reconfigure again.</p>
331 *
332 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
333 * <p>
334 *
Eino-Ville Talvalad3b85f62014-08-05 15:02:31 -0700335 * <p>After this call completes, the session will not call any further methods on the camera
336 * device.</p>
337 *
Igor Murashkin0b27d342014-05-30 09:45:05 -0700338 * @see CameraCaptureSession#close
339 */
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700340 @Override
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700341 public void replaceSessionClose() {
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700342 synchronized (mDeviceImpl.mInterfaceLock) {
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700343 /*
344 * In order for creating new sessions to be fast, the new session should be created
345 * before the old session is closed.
346 *
347 * Otherwise the old session will always unconfigure if there is no new session to
348 * replace it.
349 *
350 * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
351 * to skip unconfigure if a new session is created before the captures are all drained,
352 * but this would introduce nondeterministic behavior.
353 */
Igor Murashkin0b27d342014-05-30 09:45:05 -0700354
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700355 if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose");
Igor Murashkine1442202014-06-09 17:51:24 -0700356
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700357 // Set up fast shutdown. Possible alternative paths:
358 // - This session is active, so close() below starts the shutdown drain
359 // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
360 // - This session is already closed and has executed the idle drain listener, and
361 // configureOutputsChecked(null) has already been called.
362 //
363 // Do not call configureOutputsChecked(null) going forward, since it would race with the
364 // configuration for the new session. If it was already called, then we don't care,
365 // since it won't get called again.
366 mSkipUnconfigure = true;
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700367 close();
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700368 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700369 }
370
371 @Override
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700372 public void close() {
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700373 synchronized (mDeviceImpl.mInterfaceLock) {
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700374 if (mClosed) {
375 if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
376 return;
377 }
Igor Murashkine1442202014-06-09 17:51:24 -0700378
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700379 if (DEBUG) Log.v(TAG, mIdString + "close - first time");
380
381 mClosed = true;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700382
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700383 /*
384 * Flush out any repeating request. Since camera is closed, no new requests
385 * can be queued, and eventually the entire request queue will be drained.
386 *
387 * If the camera device was already closed, short circuit and do nothing; since
388 * no more internal device callbacks will fire anyway.
389 *
390 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure
391 * the camera. Once that's done, fire #onClosed.
392 */
393 try {
394 mDeviceImpl.stopRepeating();
395 } catch (IllegalStateException e) {
396 // OK: Camera device may already be closed, nothing else to do
Igor Murashkine1442202014-06-09 17:51:24 -0700397
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700398 // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
399 // or just suppress the ISE only and rely onClosed.
400 // Also skip any of the draining work if this is already closed.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700401
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700402 // Short-circuit; queue callback immediately and return
403 mStateCallback.onClosed(this);
404 return;
405 } catch (CameraAccessException e) {
406 // OK: close does not throw checked exceptions.
407 Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
Igor Murashkine1442202014-06-09 17:51:24 -0700408
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700409 // TODO: call onError instead of onClosed if this happens
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700410 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700411
Yin-Chia Yehb0025e12016-08-08 17:50:53 -0700412 // If no sequences are pending, fire #onClosed immediately
413 mSequenceDrainer.beginDrain();
414 }
Eino-Ville Talvala3b594bf2017-07-31 09:57:15 -0700415 if (mInput != null) {
416 mInput.release();
417 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700418 }
419
420 /**
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700421 * Whether currently in mid-abort.
422 *
423 * <p>This is used by the implementation to set the capture failure
424 * reason, in lieu of more accurate error codes from the camera service.
425 * Unsynchronized to avoid deadlocks between simultaneous session->device,
426 * device->session calls.</p>
427 *
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700428 */
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700429 @Override
430 public boolean isAborting() {
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700431 return mAborting;
432 }
433
434 /**
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700435 * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700436 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700437 private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
438 InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
439 HandlerDispatcher<StateCallback> handlerPassthrough =
440 new HandlerDispatcher<>(userCallbackSink, handler);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700441
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700442 return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700443 }
444
445 /**
446 * Forward callbacks from
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700447 * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700448 *
449 * <p>In particular, all calls are automatically split to go both to our own
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700450 * internal callback, and to the user-specified callback (by transparently posting
Igor Murashkin0b27d342014-05-30 09:45:05 -0700451 * to the user-specified handler).</p>
452 *
453 * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
454 */
455 @SuppressWarnings("deprecation")
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700456 private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
457 Handler handler, CaptureCallback callback) {
458 CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -0700459
460 @Override
461 public void onCaptureStarted(CameraDevice camera,
462 CaptureRequest request, long timestamp, long frameNumber) {
463 // Do nothing
464 }
465
466 @Override
467 public void onCapturePartial(CameraDevice camera,
468 CaptureRequest request, android.hardware.camera2.CaptureResult result) {
469 // Do nothing
470 }
471
472 @Override
473 public void onCaptureProgressed(CameraDevice camera,
474 CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
475 // Do nothing
476 }
477
478 @Override
479 public void onCaptureCompleted(CameraDevice camera,
480 CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
481 // Do nothing
482 }
483
484 @Override
485 public void onCaptureFailed(CameraDevice camera,
486 CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
487 // Do nothing
488 }
489
Igor Murashkin0b27d342014-05-30 09:45:05 -0700490 @Override
491 public void onCaptureSequenceCompleted(CameraDevice camera,
492 int sequenceId, long frameNumber) {
493 finishPendingSequence(sequenceId);
494 }
495
496 @Override
497 public void onCaptureSequenceAborted(CameraDevice camera,
498 int sequenceId) {
499 finishPendingSequence(sequenceId);
500 }
Eino-Ville Talvalacad1bfe2017-05-23 14:10:49 -0700501
502 @Override
503 public void onCaptureBufferLost(CameraDevice camera,
504 CaptureRequest request, Surface target, long frameNumber) {
505 // Do nothing
506 }
507
Igor Murashkin0b27d342014-05-30 09:45:05 -0700508 };
509
510 /*
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700511 * Split the calls from the device callback into local callback and the following chain:
Igor Murashkin21547d62014-06-04 15:21:42 -0700512 * - replace the first CameraDevice arg with a CameraCaptureSession
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700513 * - duck type from device callback to session callback
Igor Murashkin0b27d342014-05-30 09:45:05 -0700514 * - then forward the call to a handler
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700515 * - then finally invoke the destination method on the session callback object
Igor Murashkin0b27d342014-05-30 09:45:05 -0700516 */
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700517 if (callback == null) {
518 // OK: API allows the user to not specify a callback, and the handler may
Eino-Ville Talvala7875a882014-07-31 12:47:07 -0700519 // also be null in that case. Collapse whole dispatch chain to only call the local
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700520 // callback
521 return localCallback;
Igor Murashkin0b27d342014-05-30 09:45:05 -0700522 }
523
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700524 InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
525 new InvokeDispatcher<>(localCallback);
Eino-Ville Talvala7875a882014-07-31 12:47:07 -0700526
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700527 InvokeDispatcher<CaptureCallback> userCallbackSink =
528 new InvokeDispatcher<>(callback);
529 HandlerDispatcher<CaptureCallback> handlerPassthrough =
530 new HandlerDispatcher<>(userCallbackSink, handler);
531 DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
532 = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
533 ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700534 replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
535 /*argumentIndex*/0, this);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700536
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700537 BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
538 new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
Eino-Ville Talvalaa6b5ba52014-07-02 16:30:53 -0700539 replaceDeviceWithSession,
540 localSink);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700541
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700542 return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700543 }
544
545 /**
546 *
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700547 * Create an internal state callback, to be invoked on the mDeviceHandler
Igor Murashkin0b27d342014-05-30 09:45:05 -0700548 *
549 * <p>It has a few behaviors:
550 * <ul>
551 * <li>Convert device state changes into session state changes.
552 * <li>Keep track of async tasks that the session began (idle, abort).
553 * </ul>
554 * </p>
555 * */
Eino-Ville Talvala639fffe2015-06-30 10:34:48 -0700556 @Override
557 public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700558 final CameraCaptureSession session = this;
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700559 final Object interfaceLock = mDeviceImpl.mInterfaceLock;
560
Igor Murashkin0b27d342014-05-30 09:45:05 -0700561
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700562 return new CameraDeviceImpl.StateCallbackKK() {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700563 private boolean mBusy = false;
564 private boolean mActive = false;
565
566 @Override
567 public void onOpened(CameraDevice camera) {
568 throw new AssertionError("Camera must already be open before creating a session");
569 }
570
571 @Override
572 public void onDisconnected(CameraDevice camera) {
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700573 if (DEBUG) Log.v(TAG, mIdString + "onDisconnected");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700574 close();
575 }
576
577 @Override
578 public void onError(CameraDevice camera, int error) {
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700579 // Should not be reached, handled by device code
580 Log.wtf(TAG, mIdString + "Got device error " + error);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700581 }
582
583 @Override
584 public void onActive(CameraDevice camera) {
585 mIdleDrainer.taskStarted();
586 mActive = true;
587
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700588 if (DEBUG) Log.v(TAG, mIdString + "onActive");
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700589 mStateCallback.onActive(session);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700590 }
591
592 @Override
593 public void onIdle(CameraDevice camera) {
594 boolean isAborting;
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700595 if (DEBUG) Log.v(TAG, mIdString + "onIdle");
Eino-Ville Talvala95037852014-09-14 14:07:05 -0700596
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700597 synchronized (interfaceLock) {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700598 isAborting = mAborting;
599 }
600
601 /*
602 * Check which states we transitioned through:
603 *
604 * (ACTIVE -> IDLE)
605 * (BUSY -> IDLE)
606 *
607 * Note that this is also legal:
608 * (ACTIVE -> BUSY -> IDLE)
609 *
610 * and mark those tasks as finished
611 */
612 if (mBusy && isAborting) {
613 mAbortDrainer.taskFinished();
614
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700615 synchronized (interfaceLock) {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700616 mAborting = false;
617 }
618 }
619
620 if (mActive) {
621 mIdleDrainer.taskFinished();
622 }
623
624 mBusy = false;
625 mActive = false;
626
Eino-Ville Talvalafd887432014-09-04 13:07:40 -0700627 mStateCallback.onReady(session);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700628 }
629
630 @Override
631 public void onBusy(CameraDevice camera) {
632 mBusy = true;
633
634 // TODO: Queue captures during abort instead of failing them
635 // since the app won't be able to distinguish the two actives
Eino-Ville Talvalaacc00952014-08-06 14:31:08 -0700636 // Don't signal the application since there's no clean mapping here
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700637 if (DEBUG) Log.v(TAG, mIdString + "onBusy");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700638 }
639
640 @Override
641 public void onUnconfigured(CameraDevice camera) {
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700642 if (DEBUG) Log.v(TAG, mIdString + "onUnconfigured");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700643 }
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700644
645 @Override
Shuzhen Wang88f1af22016-09-30 10:29:28 -0700646 public void onRequestQueueEmpty() {
647 if (DEBUG) Log.v(TAG, mIdString + "onRequestQueueEmpty");
648 mStateCallback.onCaptureQueueEmpty(session);
Eino-Ville Talvalaad916f72015-04-11 12:09:11 -0700649 }
650
Shuzhen Wang88f1af22016-09-30 10:29:28 -0700651 @Override
652 public void onSurfacePrepared(Surface surface) {
653 if (DEBUG) Log.v(TAG, mIdString + "onSurfacePrepared");
654 mStateCallback.onSurfacePrepared(session, surface);
655 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700656 };
657
658 }
659
660 @Override
661 protected void finalize() throws Throwable {
662 try {
663 close();
664 } finally {
665 super.finalize();
666 }
667 }
668
Igor Murashkin0b27d342014-05-30 09:45:05 -0700669 private void checkNotClosed() {
670 if (mClosed) {
671 throw new IllegalStateException(
672 "Session has been closed; further changes are illegal.");
673 }
674 }
675
676 /**
677 * Notify the session that a pending capture sequence has just been queued.
678 *
679 * <p>During a shutdown/close, the session waits until all pending sessions are finished
680 * before taking any further steps to shut down itself.</p>
681 *
682 * @see #finishPendingSequence
683 */
684 private int addPendingSequence(int sequenceId) {
685 mSequenceDrainer.taskStarted(sequenceId);
686 return sequenceId;
687 }
688
689 /**
690 * Notify the session that a pending capture sequence is now finished.
691 *
692 * <p>During a shutdown/close, once all pending sequences finish, it is safe to
693 * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
694 */
695 private void finishPendingSequence(int sequenceId) {
Eino-Ville Talvala7e718962016-04-20 15:05:40 -0700696 try {
697 mSequenceDrainer.taskFinished(sequenceId);
698 } catch (IllegalStateException e) {
699 // Workaround for b/27870771
700 Log.w(TAG, e.getMessage());
701 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700702 }
703
704 private class SequenceDrainListener implements TaskDrainer.DrainListener {
705 @Override
706 public void onDrained() {
707 /*
708 * No repeating request is set; and the capture queue has fully drained.
709 *
710 * If no captures were queued to begin with, and an abort was queued,
711 * it's still possible to get another BUSY before the last IDLE.
712 *
713 * If the camera is already "IDLE" and no aborts are pending,
714 * then the drain immediately finishes.
715 */
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700716 if (DEBUG) Log.v(TAG, mIdString + "onSequenceDrained");
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700717
718
719 // Fire session close as soon as all sequences are complete.
720 // We may still need to unconfigure the device, but a new session might be created
721 // past this point, and notifications would then stop to this instance.
722 mStateCallback.onClosed(CameraCaptureSessionImpl.this);
723
724 // Fast path: A new capture session has replaced this one; don't wait for abort/idle
725 // as we won't get state updates any more anyway.
726 if (mSkipUnconfigure) {
727 return;
728 }
729
Igor Murashkin0b27d342014-05-30 09:45:05 -0700730 mAbortDrainer.beginDrain();
731 }
732 }
733
734 private class AbortDrainListener implements TaskDrainer.DrainListener {
735 @Override
736 public void onDrained() {
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700737 if (DEBUG) Log.v(TAG, mIdString + "onAbortDrained");
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700738 synchronized (mDeviceImpl.mInterfaceLock) {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700739 /*
740 * Any queued aborts have now completed.
741 *
742 * It's now safe to wait to receive the final "IDLE" event, as the camera device
743 * will no longer again transition to "ACTIVE" by itself.
744 *
745 * If the camera is already "IDLE", then the drain immediately finishes.
746 */
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700747
748 // Fast path: A new capture session has replaced this one; don't wait for idle
749 // as we won't get state updates any more anyway.
750 if (mSkipUnconfigure) {
751 return;
752 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700753 mIdleDrainer.beginDrain();
754 }
755 }
756 }
757
758 private class IdleDrainListener implements TaskDrainer.DrainListener {
759 @Override
760 public void onDrained() {
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700761 if (DEBUG) Log.v(TAG, mIdString + "onIdleDrained");
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700762
763 // Take device lock before session lock so that we can call back into device
764 // without causing a deadlock
765 synchronized (mDeviceImpl.mInterfaceLock) {
Igor Murashkin0b27d342014-05-30 09:45:05 -0700766 /*
767 * The device is now IDLE, and has settled. It will not transition to
768 * ACTIVE or BUSY again by itself.
769 *
Eino-Ville Talvala937c93c2015-05-18 16:16:44 -0700770 * It's now safe to unconfigure the outputs.
Igor Murashkin0b27d342014-05-30 09:45:05 -0700771 *
772 * This operation is idempotent; a session will not be closed twice.
773 */
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700774 if (DEBUG)
775 Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
776 mSkipUnconfigure);
Igor Murashkin0b27d342014-05-30 09:45:05 -0700777
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700778 // Fast path: A new capture session has replaced this one; don't wait for idle
779 // as we won't get state updates any more anyway.
780 if (mSkipUnconfigure) {
781 return;
782 }
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700783
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700784 // Final slow path: unconfigure the camera, no session has replaced us and
785 // everything is idle.
786 try {
787 // begin transition to unconfigured
788 mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
789 /*operatingMode*/ ICameraDeviceUser.NORMAL_MODE);
790 } catch (CameraAccessException e) {
791 // OK: do not throw checked exceptions.
792 Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700793
Yin-Chia Yehec7f4c62017-06-19 14:17:29 -0700794 // TODO: call onError instead of onClosed if this happens
795 } catch (IllegalStateException e) {
796 // Camera is already closed, so nothing left to do
797 if (DEBUG) Log.v(TAG, mIdString +
798 "Camera was already closed or busy, skipping unconfigure");
Igor Murashkin0b27d342014-05-30 09:45:05 -0700799 }
Igor Murashkin0b27d342014-05-30 09:45:05 -0700800 }
801 }
802 }
803
Igor Murashkin0b27d342014-05-30 09:45:05 -0700804}