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