blob: b9eba9c1d5415f1451d751057e485c052dd1a02d [file] [log] [blame]
Aurimas Liutikas88c7ff12023-08-10 12:42:26 -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.CameraOfflineSession;
22import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
23import android.hardware.camera2.CaptureRequest;
24import android.hardware.camera2.ICameraDeviceUser;
25import android.hardware.camera2.params.OutputConfiguration;
26import android.hardware.camera2.utils.TaskDrainer;
27import android.hardware.camera2.utils.TaskSingleDrainer;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.SystemClock;
31import android.util.Log;
32import android.view.Surface;
33
34import java.util.Arrays;
35import java.util.Collection;
36import java.util.List;
37import java.util.concurrent.Executor;
38
39import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
40import static com.android.internal.util.Preconditions.*;
41
42public class CameraCaptureSessionImpl extends CameraCaptureSession
43 implements CameraCaptureSessionCore {
44 private static final String TAG = "CameraCaptureSession";
45 private static final boolean DEBUG = false;
46
47 /** Simple integer ID for session for debugging */
48 private final int mId;
49 private final String mIdString;
50
51 /** Input surface configured by native camera framework based on user-specified configuration */
52 private final Surface mInput;
53 /**
54 * User-specified state callback, used for outgoing events; calls to this object will be
55 * automatically invoked via {@code mStateExecutor}.
56 */
57 private final CameraCaptureSession.StateCallback mStateCallback;
58 /** User-specified state executor used for outgoing state callback events */
59 private final Executor mStateExecutor;
60
61 /** Internal camera device; used to translate calls into existing deprecated API */
62 private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
63 /** Internal executor; used for all incoming events to preserve total order */
64 private final Executor mDeviceExecutor;
65
66 /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
67 private final TaskDrainer<Integer> mSequenceDrainer;
68 /** Drain state transitions from ACTIVE -> IDLE */
69 private final TaskSingleDrainer mIdleDrainer;
70 /** Drain state transitions from BUSY -> IDLE */
71 private final TaskSingleDrainer mAbortDrainer;
72
73 /** This session is closed; all further calls will throw ISE */
74 private boolean mClosed = false;
75 /** This session failed to be configured successfully */
76 private final boolean mConfigureSuccess;
77 /** 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. */
81 private volatile boolean mAborting;
82
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 */
90 CameraCaptureSessionImpl(int id, Surface input,
91 CameraCaptureSession.StateCallback callback, Executor stateExecutor,
92 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
93 Executor deviceStateExecutor, boolean configureSuccess) {
94 if (callback == null) {
95 throw new IllegalArgumentException("callback must not be null");
96 }
97
98 mId = id;
99 mIdString = String.format("Session %d: ", mId);
100
101 mInput = input;
102 mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
103 mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
104
105 mDeviceExecutor = checkNotNull(deviceStateExecutor,
106 "deviceStateExecutor must not be null");
107 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
108
109 /*
110 * Use the same handler as the device's StateCallback for all the internal coming events
111 *
112 * This ensures total ordering between CameraDevice.StateCallback and
113 * CaptureCallback events.
114 */
115 mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
116 /*name*/"seq");
117 mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
118 /*name*/"idle");
119 mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
120 /*name*/"abort");
121
122 // CameraDevice should call configureOutputs and have it finish before constructing us
123
124 if (configureSuccess) {
125 mStateCallback.onConfigured(this);
126 if (DEBUG) Log.v(TAG, mIdString + "Created session successfully");
127 mConfigureSuccess = true;
128 } else {
129 mStateCallback.onConfigureFailed(this);
130 mClosed = true; // do not fire any other callbacks, do not allow any other work
131 Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");
132 mConfigureSuccess = false;
133 }
134 }
135
136 @Override
137 public CameraDevice getDevice() {
138 return mDeviceImpl;
139 }
140
141 @Override
142 public void prepare(Surface surface) throws CameraAccessException {
143 synchronized (mDeviceImpl.mInterfaceLock) {
144 checkNotClosed();
145 mDeviceImpl.prepare(surface);
146 }
147 }
148
149 @Override
150 public void prepare(int maxCount, Surface surface) throws CameraAccessException {
151 synchronized (mDeviceImpl.mInterfaceLock) {
152 checkNotClosed();
153 mDeviceImpl.prepare(maxCount, surface);
154 }
155 }
156
157 @Override
158 public void tearDown(Surface surface) throws CameraAccessException {
159 synchronized (mDeviceImpl.mInterfaceLock) {
160 checkNotClosed();
161 mDeviceImpl.tearDown(surface);
162 }
163 }
164
165 @Override
166 public void finalizeOutputConfigurations(
167 List<OutputConfiguration> outputConfigs) throws CameraAccessException {
168 synchronized (mDeviceImpl.mInterfaceLock) {
169 checkNotClosed();
170 mDeviceImpl.finalizeOutputConfigs(outputConfigs);
171 }
172 }
173
174 @Override
175 public int capture(CaptureRequest request, CaptureCallback callback,
176 Handler handler) throws CameraAccessException {
177 checkCaptureRequest(request);
178
179 synchronized (mDeviceImpl.mInterfaceLock) {
180 checkNotClosed();
181
182 handler = checkHandler(handler, callback);
183
184 if (DEBUG) {
185 Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
186 " handler " + handler);
187 }
188
189 return addPendingSequence(mDeviceImpl.capture(request,
190 createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
191 }
192 }
193
194 @Override
195 public int captureSingleRequest(CaptureRequest request, Executor executor,
196 CaptureCallback callback) throws CameraAccessException {
197 if (executor == null) {
198 throw new IllegalArgumentException("executor must not be null");
199 } else if (callback == null) {
200 throw new IllegalArgumentException("callback must not be null");
201 }
202 checkCaptureRequest(request);
203
204 synchronized (mDeviceImpl.mInterfaceLock) {
205 checkNotClosed();
206
207 executor = CameraDeviceImpl.checkExecutor(executor, callback);
208
209 if (DEBUG) {
210 Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
211 " executor " + executor);
212 }
213
214 return addPendingSequence(mDeviceImpl.capture(request,
215 createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
216 }
217 }
218
219 private void checkCaptureRequest(CaptureRequest request) {
220 if (request == null) {
221 throw new IllegalArgumentException("request must not be null");
222 } else if (request.isReprocess() && !isReprocessable()) {
223 throw new IllegalArgumentException("this capture session cannot handle reprocess " +
224 "requests");
225 } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
226 throw new IllegalArgumentException("capture request was created for another session");
227 }
228 }
229
230 @Override
231 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
232 Handler handler) throws CameraAccessException {
233 checkCaptureRequests(requests);
234
235 synchronized (mDeviceImpl.mInterfaceLock) {
236 checkNotClosed();
237
238 handler = checkHandler(handler, callback);
239
240 if (DEBUG) {
241 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
242 Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
243 ", callback " + callback + " handler " + handler);
244 }
245
246 return addPendingSequence(mDeviceImpl.captureBurst(requests,
247 createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
248 }
249 }
250
251 @Override
252 public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
253 CaptureCallback callback) throws CameraAccessException {
254 if (executor == null) {
255 throw new IllegalArgumentException("executor must not be null");
256 } else if (callback == null) {
257 throw new IllegalArgumentException("callback must not be null");
258 }
259 checkCaptureRequests(requests);
260
261 synchronized (mDeviceImpl.mInterfaceLock) {
262 checkNotClosed();
263
264 executor = CameraDeviceImpl.checkExecutor(executor, callback);
265
266 if (DEBUG) {
267 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
268 Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
269 ", callback " + callback + " executor " + executor);
270 }
271
272 return addPendingSequence(mDeviceImpl.captureBurst(requests,
273 createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
274 }
275 }
276
277 private void checkCaptureRequests(List<CaptureRequest> requests) {
278 if (requests == null) {
279 throw new IllegalArgumentException("Requests must not be null");
280 } else if (requests.isEmpty()) {
281 throw new IllegalArgumentException("Requests must have at least one element");
282 }
283
284 for (CaptureRequest request : requests) {
285 if (request.isReprocess()) {
286 if (!isReprocessable()) {
287 throw new IllegalArgumentException("This capture session cannot handle " +
288 "reprocess requests");
289 } else if (request.getReprocessableSessionId() != mId) {
290 throw new IllegalArgumentException("Capture request was created for another " +
291 "session");
292 }
293 }
294 }
295
296 }
297
298 @Override
299 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
300 Handler handler) throws CameraAccessException {
301 checkRepeatingRequest(request);
302
303 synchronized (mDeviceImpl.mInterfaceLock) {
304 checkNotClosed();
305
306 handler = checkHandler(handler, callback);
307
308 if (DEBUG) {
309 Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
310 callback + " handler" + " " + handler);
311 }
312
313 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
314 createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
315 }
316 }
317
318 @Override
319 public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
320 CaptureCallback callback) throws CameraAccessException {
321 if (executor == null) {
322 throw new IllegalArgumentException("executor must not be null");
323 } else if (callback == null) {
324 throw new IllegalArgumentException("callback must not be null");
325 }
326 checkRepeatingRequest(request);
327
328 synchronized (mDeviceImpl.mInterfaceLock) {
329 checkNotClosed();
330
331 executor = CameraDeviceImpl.checkExecutor(executor, callback);
332
333 if (DEBUG) {
334 Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
335 callback + " executor" + " " + executor);
336 }
337
338 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
339 createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
340 }
341 }
342
343 private void checkRepeatingRequest(CaptureRequest request) {
344 if (request == null) {
345 throw new IllegalArgumentException("request must not be null");
346 } else if (request.isReprocess()) {
347 throw new IllegalArgumentException("repeating reprocess requests are not supported");
348 }
349 }
350
351 @Override
352 public int setRepeatingBurst(List<CaptureRequest> requests,
353 CaptureCallback callback, Handler handler) throws CameraAccessException {
354 checkRepeatingRequests(requests);
355
356 synchronized (mDeviceImpl.mInterfaceLock) {
357 checkNotClosed();
358
359 handler = checkHandler(handler, callback);
360
361 if (DEBUG) {
362 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
363 Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
364 Arrays.toString(requestArray) + ", callback " + callback +
365 " handler" + "" + handler);
366 }
367
368 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
369 createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
370 }
371 }
372
373 @Override
374 public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
375 CaptureCallback callback) throws CameraAccessException {
376 if (executor == null) {
377 throw new IllegalArgumentException("executor must not be null");
378 } else if (callback == null) {
379 throw new IllegalArgumentException("callback must not be null");
380 }
381 checkRepeatingRequests(requests);
382
383 synchronized (mDeviceImpl.mInterfaceLock) {
384 checkNotClosed();
385
386 executor = CameraDeviceImpl.checkExecutor(executor, callback);
387
388 if (DEBUG) {
389 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
390 Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
391 Arrays.toString(requestArray) + ", callback " + callback +
392 " executor" + "" + executor);
393 }
394
395 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
396 createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
397 }
398 }
399
400 private void checkRepeatingRequests(List<CaptureRequest> requests) {
401 if (requests == null) {
402 throw new IllegalArgumentException("requests must not be null");
403 } else if (requests.isEmpty()) {
404 throw new IllegalArgumentException("requests must have at least one element");
405 }
406
407 for (CaptureRequest r : requests) {
408 if (r.isReprocess()) {
409 throw new IllegalArgumentException("repeating reprocess burst requests are not " +
410 "supported");
411 }
412 }
413 }
414
415 @Override
416 public void stopRepeating() throws CameraAccessException {
417 synchronized (mDeviceImpl.mInterfaceLock) {
418 checkNotClosed();
419
420 if (DEBUG) {
421 Log.v(TAG, mIdString + "stopRepeating");
422 }
423
424 mDeviceImpl.stopRepeating();
425 }
426 }
427
428 @Override
429 public void abortCaptures() throws CameraAccessException {
430 synchronized (mDeviceImpl.mInterfaceLock) {
431 checkNotClosed();
432
433 if (DEBUG) {
434 Log.v(TAG, mIdString + "abortCaptures");
435 }
436
437 if (mAborting) {
438 Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
439 return;
440 }
441
442 mAborting = true;
443 mAbortDrainer.taskStarted();
444
445 mDeviceImpl.flush();
446 // The next BUSY -> IDLE set of transitions will mark the end of the abort.
447 }
448 }
449
450 @Override
451 public void updateOutputConfiguration(OutputConfiguration config)
452 throws CameraAccessException {
453 synchronized (mDeviceImpl.mInterfaceLock) {
454 checkNotClosed();
455
456 if (DEBUG) {
457 Log.v(TAG, mIdString + "updateOutputConfiguration");
458 }
459
460 mDeviceImpl.updateOutputConfiguration(config);
461 }
462 }
463
464 @Override
465 public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs,
466 Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException {
467 synchronized (mDeviceImpl.mInterfaceLock) {
468 checkNotClosed();
469 }
470 return mDeviceImpl.switchToOffline(offlineOutputs, executor, listener);
471 }
472
473
474 @Override
475 public boolean supportsOfflineProcessing(Surface surface) {
476 synchronized (mDeviceImpl.mInterfaceLock) {
477 checkNotClosed();
478 }
479 return mDeviceImpl.supportsOfflineProcessing(surface);
480 }
481
482 @Override
483 public boolean isReprocessable() {
484 return mInput != null;
485 }
486
487 @Override
488 public Surface getInputSurface() {
489 return mInput;
490 }
491
492 /**
493 * Replace this session with another session.
494 *
495 * <p>This is an optimization to avoid unconfiguring and then immediately having to
496 * reconfigure again.</p>
497 *
498 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
499 * <p>
500 *
501 * <p>After this call completes, the session will not call any further methods on the camera
502 * device.</p>
503 *
504 * @see CameraCaptureSession#close
505 */
506 @Override
507 public void replaceSessionClose() {
508 synchronized (mDeviceImpl.mInterfaceLock) {
509 /*
510 * In order for creating new sessions to be fast, the new session should be created
511 * before the old session is closed.
512 *
513 * Otherwise the old session will always unconfigure if there is no new session to
514 * replace it.
515 *
516 * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
517 * to skip unconfigure if a new session is created before the captures are all drained,
518 * but this would introduce nondeterministic behavior.
519 */
520
521 if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose");
522
523 // Set up fast shutdown. Possible alternative paths:
524 // - This session is active, so close() below starts the shutdown drain
525 // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
526 // - This session is already closed and has executed the idle drain listener, and
527 // configureOutputsChecked(null) has already been called.
528 //
529 // Do not call configureOutputsChecked(null) going forward, since it would race with the
530 // configuration for the new session. If it was already called, then we don't care,
531 // since it won't get called again.
532 mSkipUnconfigure = true;
533 close();
534 }
535 }
536
537 @Override
538 public void closeWithoutDraining() {
539 synchronized (mDeviceImpl.mInterfaceLock) {
540 if (mClosed) {
541 if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
542 return;
543 }
544
545 if (DEBUG) Log.v(TAG, mIdString + "close - first time");
546
547 mClosed = true;
548 mStateCallback.onClosed(this);
549 }
550
551 if (mInput != null) {
552 mInput.release();
553 }
554 }
555
556 @Override
557 public void close() {
558 synchronized (mDeviceImpl.mInterfaceLock) {
559 if (mClosed) {
560 if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
561 return;
562 }
563
564 if (DEBUG) Log.v(TAG, mIdString + "close - first time");
565
566 mClosed = true;
567
568 /*
569 * Flush out any repeating request. Since camera is closed, no new requests
570 * can be queued, and eventually the entire request queue will be drained.
571 *
572 * If the camera device was already closed, short circuit and do nothing; since
573 * no more internal device callbacks will fire anyway.
574 *
575 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure
576 * the camera. Once that's done, fire #onClosed.
577 */
578 try {
579 mDeviceImpl.stopRepeating();
580 } catch (IllegalStateException e) {
581 // OK: Camera device may already be closed, nothing else to do
582
583 // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
584 // or just suppress the ISE only and rely onClosed.
585 // Also skip any of the draining work if this is already closed.
586
587 // Short-circuit; queue callback immediately and return
588 mStateCallback.onClosed(this);
589 return;
590 } catch (CameraAccessException e) {
591 // OK: close does not throw checked exceptions.
592 Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
593
594 // TODO: call onError instead of onClosed if this happens
595 }
596
597 // If no sequences are pending, fire #onClosed immediately
598 mSequenceDrainer.beginDrain();
599 }
600 if (mInput != null) {
601 mInput.release();
602 }
603 }
604
605 /**
606 * Whether currently in mid-abort.
607 *
608 * <p>This is used by the implementation to set the capture failure
609 * reason, in lieu of more accurate error codes from the camera service.
610 * Unsynchronized to avoid deadlocks between simultaneous session->device,
611 * device->session calls.</p>
612 *
613 */
614 @Override
615 public boolean isAborting() {
616 return mAborting;
617 }
618
619 /**
620 * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
621 */
622 private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
623 return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
624 }
625
626 /**
627 * Forward callbacks that usually originate from
628 * CameraDeviceImpl.CameraDeviceCallbacks to the CameraCaptureSession.CaptureCallback.
629 *
630 * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
631 */
632 @SuppressWarnings("deprecation")
633 private android.hardware.camera2.impl.CaptureCallback createCaptureCallbackProxy(
634 Handler handler, CaptureCallback callback) {
635 final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
636 handler) : null;
637
638 return createCaptureCallbackProxyWithExecutor(executor, callback);
639 }
640
641 private android.hardware.camera2.impl.CaptureCallback createCaptureCallbackProxyWithExecutor(
642 Executor executor, CaptureCallback callback) {
643 return new android.hardware.camera2.impl.CaptureCallback(executor, callback) {
644 @Override
645 public void onCaptureStarted(CameraDevice camera,
646 CaptureRequest request, long timestamp, long frameNumber) {
647 if ((callback != null) && (executor != null)) {
648 final long ident = Binder.clearCallingIdentity();
649 try {
650 executor.execute(() -> callback.onCaptureStarted(
651 CameraCaptureSessionImpl.this, request, timestamp,
652 frameNumber));
653 } finally {
654 Binder.restoreCallingIdentity(ident);
655 }
656 }
657 }
658
659 @Override
660 public void onReadoutStarted(CameraDevice camera,
661 CaptureRequest request, long timestamp, long frameNumber) {
662 if ((callback != null) && (executor != null)) {
663 final long ident = Binder.clearCallingIdentity();
664 try {
665 executor.execute(() -> callback.onReadoutStarted(
666 CameraCaptureSessionImpl.this, request, timestamp,
667 frameNumber));
668 } finally {
669 Binder.restoreCallingIdentity(ident);
670 }
671 }
672 }
673
674 @Override
675 public void onCapturePartial(CameraDevice camera,
676 CaptureRequest request, android.hardware.camera2.CaptureResult result) {
677 if ((callback != null) && (executor != null)) {
678 final long ident = Binder.clearCallingIdentity();
679 try {
680 executor.execute(() -> callback.onCapturePartial(
681 CameraCaptureSessionImpl.this, request, result));
682 } finally {
683 Binder.restoreCallingIdentity(ident);
684 }
685 }
686 }
687
688 @Override
689 public void onCaptureProgressed(CameraDevice camera,
690 CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
691 if ((callback != null) && (executor != null)) {
692 final long ident = Binder.clearCallingIdentity();
693 try {
694 executor.execute(() -> callback.onCaptureProgressed(
695 CameraCaptureSessionImpl.this, request, partialResult));
696 } finally {
697 Binder.restoreCallingIdentity(ident);
698 }
699 }
700 }
701
702 @Override
703 public void onCaptureCompleted(CameraDevice camera,
704 CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
705 if ((callback != null) && (executor != null)) {
706 final long ident = Binder.clearCallingIdentity();
707 try {
708 executor.execute(() -> callback.onCaptureCompleted(
709 CameraCaptureSessionImpl.this, request, result));
710 } finally {
711 Binder.restoreCallingIdentity(ident);
712 }
713 }
714 }
715
716 @Override
717 public void onCaptureFailed(CameraDevice camera,
718 CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
719 if ((callback != null) && (executor != null)) {
720 final long ident = Binder.clearCallingIdentity();
721 try {
722 executor.execute(() -> callback.onCaptureFailed(
723 CameraCaptureSessionImpl.this, request, failure));
724 } finally {
725 Binder.restoreCallingIdentity(ident);
726 }
727 }
728 }
729
730 @Override
731 public void onCaptureSequenceCompleted(CameraDevice camera,
732 int sequenceId, long frameNumber) {
733 if ((callback != null) && (executor != null)) {
734 final long ident = Binder.clearCallingIdentity();
735 try {
736 executor.execute(() -> callback.onCaptureSequenceCompleted(
737 CameraCaptureSessionImpl.this, sequenceId, frameNumber));
738 } finally {
739 Binder.restoreCallingIdentity(ident);
740 }
741 }
742 finishPendingSequence(sequenceId);
743 }
744
745 @Override
746 public void onCaptureSequenceAborted(CameraDevice camera,
747 int sequenceId) {
748 if ((callback != null) && (executor != null)) {
749 final long ident = Binder.clearCallingIdentity();
750 try {
751 executor.execute(() -> callback.onCaptureSequenceAborted(
752 CameraCaptureSessionImpl.this, sequenceId));
753 } finally {
754 Binder.restoreCallingIdentity(ident);
755 }
756 }
757 finishPendingSequence(sequenceId);
758 }
759
760 @Override
761 public void onCaptureBufferLost(CameraDevice camera,
762 CaptureRequest request, Surface target, long frameNumber) {
763 if ((callback != null) && (executor != null)) {
764 final long ident = Binder.clearCallingIdentity();
765 try {
766 executor.execute(() -> callback.onCaptureBufferLost(
767 CameraCaptureSessionImpl.this, request, target, frameNumber));
768 } finally {
769 Binder.restoreCallingIdentity(ident);
770 }
771 }
772 }
773 };
774 }
775
776 /**
777 *
778 * Create an internal state callback, to be invoked on the mDeviceExecutor
779 *
780 * <p>It has a few behaviors:
781 * <ul>
782 * <li>Convert device state changes into session state changes.
783 * <li>Keep track of async tasks that the session began (idle, abort).
784 * </ul>
785 * </p>
786 * */
787 @Override
788 public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
789 final CameraCaptureSession session = this;
790 final Object interfaceLock = mDeviceImpl.mInterfaceLock;
791
792
793 return new CameraDeviceImpl.StateCallbackKK() {
794 private boolean mBusy = false;
795 private boolean mActive = false;
796
797 @Override
798 public void onOpened(CameraDevice camera) {
799 throw new AssertionError("Camera must already be open before creating a session");
800 }
801
802 @Override
803 public void onDisconnected(CameraDevice camera) {
804 if (DEBUG) Log.v(TAG, mIdString + "onDisconnected");
805 close();
806 }
807
808 @Override
809 public void onError(CameraDevice camera, int error) {
810 // Should not be reached, handled by device code
811 Log.wtf(TAG, mIdString + "Got device error " + error);
812 }
813
814 @Override
815 public void onActive(CameraDevice camera) {
816 mIdleDrainer.taskStarted();
817 mActive = true;
818
819 if (DEBUG) Log.v(TAG, mIdString + "onActive");
820 mStateCallback.onActive(session);
821 }
822
823 @Override
824 public void onIdle(CameraDevice camera) {
825 boolean isAborting;
826 if (DEBUG) Log.v(TAG, mIdString + "onIdle");
827
828 synchronized (interfaceLock) {
829 isAborting = mAborting;
830 }
831
832 /*
833 * Check which states we transitioned through:
834 *
835 * (ACTIVE -> IDLE)
836 * (BUSY -> IDLE)
837 *
838 * Note that this is also legal:
839 * (ACTIVE -> BUSY -> IDLE)
840 *
841 * and mark those tasks as finished
842 */
843 if (mBusy && isAborting) {
844 mAbortDrainer.taskFinished();
845
846 synchronized (interfaceLock) {
847 mAborting = false;
848 }
849 }
850
851 if (mActive) {
852 mIdleDrainer.taskFinished();
853 }
854
855 mBusy = false;
856 mActive = false;
857
858 mStateCallback.onReady(session);
859 }
860
861 @Override
862 public void onBusy(CameraDevice camera) {
863 mBusy = true;
864
865 // TODO: Queue captures during abort instead of failing them
866 // since the app won't be able to distinguish the two actives
867 // Don't signal the application since there's no clean mapping here
868 if (DEBUG) Log.v(TAG, mIdString + "onBusy");
869 }
870
871 @Override
872 public void onUnconfigured(CameraDevice camera) {
873 if (DEBUG) Log.v(TAG, mIdString + "onUnconfigured");
874 }
875
876 @Override
877 public void onRequestQueueEmpty() {
878 if (DEBUG) Log.v(TAG, mIdString + "onRequestQueueEmpty");
879 mStateCallback.onCaptureQueueEmpty(session);
880 }
881
882 @Override
883 public void onSurfacePrepared(Surface surface) {
884 if (DEBUG) Log.v(TAG, mIdString + "onSurfacePrepared");
885 mStateCallback.onSurfacePrepared(session, surface);
886 }
887 };
888
889 }
890
891 @Override
892 protected void finalize() throws Throwable {
893 try {
894 close();
895 } finally {
896 super.finalize();
897 }
898 }
899
900 private void checkNotClosed() {
901 if (mClosed) {
902 throw new IllegalStateException(
903 "Session has been closed; further changes are illegal.");
904 }
905 }
906
907 /**
908 * Notify the session that a pending capture sequence has just been queued.
909 *
910 * <p>During a shutdown/close, the session waits until all pending sessions are finished
911 * before taking any further steps to shut down itself.</p>
912 *
913 * @see #finishPendingSequence
914 */
915 private int addPendingSequence(int sequenceId) {
916 mSequenceDrainer.taskStarted(sequenceId);
917 return sequenceId;
918 }
919
920 /**
921 * Notify the session that a pending capture sequence is now finished.
922 *
923 * <p>During a shutdown/close, once all pending sequences finish, it is safe to
924 * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
925 */
926 private void finishPendingSequence(int sequenceId) {
927 try {
928 mSequenceDrainer.taskFinished(sequenceId);
929 } catch (IllegalStateException e) {
930 // Workaround for b/27870771
931 Log.w(TAG, e.getMessage());
932 }
933 }
934
935 private class SequenceDrainListener implements TaskDrainer.DrainListener {
936 @Override
937 public void onDrained() {
938 /*
939 * No repeating request is set; and the capture queue has fully drained.
940 *
941 * If no captures were queued to begin with, and an abort was queued,
942 * it's still possible to get another BUSY before the last IDLE.
943 *
944 * If the camera is already "IDLE" and no aborts are pending,
945 * then the drain immediately finishes.
946 */
947 if (DEBUG) Log.v(TAG, mIdString + "onSequenceDrained");
948
949
950 // Fire session close as soon as all sequences are complete.
951 // We may still need to unconfigure the device, but a new session might be created
952 // past this point, and notifications would then stop to this instance.
953 mStateCallback.onClosed(CameraCaptureSessionImpl.this);
954
955 // Fast path: A new capture session has replaced this one; don't wait for abort/idle
956 // as we won't get state updates any more anyway.
957 if (mSkipUnconfigure) {
958 return;
959 }
960
961 mAbortDrainer.beginDrain();
962 }
963 }
964
965 private class AbortDrainListener implements TaskDrainer.DrainListener {
966 @Override
967 public void onDrained() {
968 if (DEBUG) Log.v(TAG, mIdString + "onAbortDrained");
969 synchronized (mDeviceImpl.mInterfaceLock) {
970 /*
971 * Any queued aborts have now completed.
972 *
973 * It's now safe to wait to receive the final "IDLE" event, as the camera device
974 * will no longer again transition to "ACTIVE" by itself.
975 *
976 * If the camera is already "IDLE", then the drain immediately finishes.
977 */
978
979 // Fast path: A new capture session has replaced this one; don't wait for idle
980 // as we won't get state updates any more anyway.
981 if (mSkipUnconfigure) {
982 return;
983 }
984 mIdleDrainer.beginDrain();
985 }
986 }
987 }
988
989 private class IdleDrainListener implements TaskDrainer.DrainListener {
990 @Override
991 public void onDrained() {
992 if (DEBUG) Log.v(TAG, mIdString + "onIdleDrained");
993
994 // Take device lock before session lock so that we can call back into device
995 // without causing a deadlock
996 synchronized (mDeviceImpl.mInterfaceLock) {
997 /*
998 * The device is now IDLE, and has settled. It will not transition to
999 * ACTIVE or BUSY again by itself.
1000 *
1001 * It's now safe to unconfigure the outputs.
1002 *
1003 * This operation is idempotent; a session will not be closed twice.
1004 */
1005 if (DEBUG)
1006 Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
1007 mSkipUnconfigure);
1008
1009 // Fast path: A new capture session has replaced this one; don't wait for idle
1010 // as we won't get state updates any more anyway.
1011 if (mSkipUnconfigure) {
1012 return;
1013 }
1014
1015 // Final slow path: unconfigure the camera, no session has replaced us and
1016 // everything is idle.
1017 try {
1018 // begin transition to unconfigured
1019 mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
1020 /*operatingMode*/ ICameraDeviceUser.NORMAL_MODE,
1021 /*sessionParams*/ null, SystemClock.uptimeMillis());
1022 } catch (CameraAccessException e) {
1023 // OK: do not throw checked exceptions.
1024 Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
1025
1026 // TODO: call onError instead of onClosed if this happens
1027 } catch (IllegalStateException e) {
1028 // Camera is already closed, so nothing left to do
1029 if (DEBUG) Log.v(TAG, mIdString +
1030 "Camera was already closed or busy, skipping unconfigure");
1031 }
1032 }
1033 }
1034 }
1035
1036}