1 /*
2  * Copyright 2020 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 
17 package androidx.camera.camera2.internal;
18 
19 import static java.util.Collections.emptyList;
20 
21 import android.hardware.camera2.CameraAccessException;
22 import android.hardware.camera2.CameraCaptureSession;
23 import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
24 import android.hardware.camera2.CameraDevice;
25 import android.hardware.camera2.CaptureRequest;
26 import android.os.Build;
27 import android.os.Handler;
28 import android.view.Surface;
29 
30 import androidx.annotation.GuardedBy;
31 import androidx.annotation.RequiresApi;
32 import androidx.camera.camera2.internal.annotation.CameraExecutor;
33 import androidx.camera.camera2.internal.compat.CameraCaptureSessionCompat;
34 import androidx.camera.camera2.internal.compat.CameraDeviceCompat;
35 import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
36 import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
37 import androidx.camera.core.Logger;
38 import androidx.camera.core.impl.DeferrableSurface;
39 import androidx.camera.core.impl.DeferrableSurfaces;
40 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
41 import androidx.camera.core.impl.utils.futures.FutureCallback;
42 import androidx.camera.core.impl.utils.futures.FutureChain;
43 import androidx.camera.core.impl.utils.futures.Futures;
44 import androidx.concurrent.futures.CallbackToFutureAdapter;
45 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
46 import androidx.core.util.Preconditions;
47 
48 import com.google.common.util.concurrent.ListenableFuture;
49 
50 import org.jspecify.annotations.NonNull;
51 import org.jspecify.annotations.Nullable;
52 
53 import java.util.List;
54 import java.util.Objects;
55 import java.util.concurrent.CancellationException;
56 import java.util.concurrent.Executor;
57 import java.util.concurrent.ScheduledExecutorService;
58 
59 /**
60  * The implementation of {@link SynchronizedCaptureSession} to forward the feature calls
61  * into the {@link CameraCaptureSession}.
62  *
63  * The implementation of {@link SynchronizedCaptureSession.StateCallback} and
64  * {@link SynchronizedCaptureSession.Opener} will be able to track the creation and close of the
65  * SynchronizedCaptureSession in {@link CaptureSessionRepository}.
66  * Some Quirks may be required to take some action before opening/closing other sessions, with the
67  * SynchronizedCaptureSessionBaseImpl, it would be useful when implementing the workaround of
68  * Quirks.
69  */
70 class SynchronizedCaptureSessionBaseImpl extends SynchronizedCaptureSession.StateCallback implements
71         SynchronizedCaptureSession, SynchronizedCaptureSession.Opener {
72 
73     private static final String TAG = "SyncCaptureSessionBase";
74 
75     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
76     final Object mLock = new Object();
77 
78     final @NonNull CaptureSessionRepository mCaptureSessionRepository;
79     final @NonNull Handler mCompatHandler;
80     @CameraExecutor
81     final @NonNull Executor mExecutor;
82     private final @NonNull ScheduledExecutorService mScheduledExecutorService;
83 
84     @Nullable StateCallback mCaptureSessionStateCallback;
85     @Nullable CameraCaptureSessionCompat mCameraCaptureSessionCompat;
86 
87     @GuardedBy("mLock")
88     @Nullable ListenableFuture<Void> mOpenCaptureSessionFuture;
89     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
90     @GuardedBy("mLock")
91     @Nullable Completer<Void> mOpenCaptureSessionCompleter;
92 
93     @GuardedBy("mLock")
94     private @Nullable ListenableFuture<List<Surface>> mStartingSurface;
95 
96     @GuardedBy("mLock")
97     private @Nullable List<DeferrableSurface> mHeldDeferrableSurfaces = null;
98 
99     @GuardedBy("mLock")
100     private boolean mClosed = false;
101     @GuardedBy("mLock")
102     private boolean mOpenerDisabled = false;
103     @GuardedBy("mLock")
104     private boolean mSessionFinished = false;
105 
SynchronizedCaptureSessionBaseImpl(@onNull CaptureSessionRepository repository, @CameraExecutor @NonNull Executor executor, @NonNull ScheduledExecutorService scheduledExecutorService, @NonNull Handler compatHandler)106     SynchronizedCaptureSessionBaseImpl(@NonNull CaptureSessionRepository repository,
107             @CameraExecutor @NonNull Executor executor,
108             @NonNull ScheduledExecutorService scheduledExecutorService,
109             @NonNull Handler compatHandler) {
110         mCaptureSessionRepository = repository;
111         mCompatHandler = compatHandler;
112         mExecutor = executor;
113         mScheduledExecutorService = scheduledExecutorService;
114     }
115 
116     @Override
getStateCallback()117     public @NonNull StateCallback getStateCallback() {
118         return this;
119     }
120 
121     @Override
getOpeningBlocker()122     public @NonNull ListenableFuture<Void> getOpeningBlocker() {
123         return Futures.immediateFuture(null);
124     }
125 
126     @Override
openCaptureSession(@onNull CameraDevice cameraDevice, @NonNull SessionConfigurationCompat sessionConfigurationCompat, @NonNull List<DeferrableSurface> deferrableSurfaces)127     public @NonNull ListenableFuture<Void> openCaptureSession(@NonNull CameraDevice cameraDevice,
128             @NonNull SessionConfigurationCompat sessionConfigurationCompat,
129             @NonNull List<DeferrableSurface> deferrableSurfaces) {
130         synchronized (mLock) {
131             if (mOpenerDisabled) {
132                 return Futures.immediateFailedFuture(
133                         new CancellationException("Opener is disabled"));
134             }
135             mCaptureSessionRepository.onCreateCaptureSession(this);
136             CameraDeviceCompat cameraDeviceCompat =
137                     CameraDeviceCompat.toCameraDeviceCompat(cameraDevice, mCompatHandler);
138             mOpenCaptureSessionFuture = CallbackToFutureAdapter.getFuture(completer -> {
139                 synchronized (mLock) {
140                     // Attempt to set all the configured deferrable surfaces is in used
141                     // before adding them to the session.
142                     holdDeferrableSurfaces(deferrableSurfaces);
143 
144                     Preconditions.checkState(mOpenCaptureSessionCompleter == null,
145                             "The openCaptureSessionCompleter can only set once!");
146 
147                     mOpenCaptureSessionCompleter = completer;
148                     cameraDeviceCompat.createCaptureSession(sessionConfigurationCompat);
149                     return "openCaptureSession[session="
150                             + SynchronizedCaptureSessionBaseImpl.this + "]";
151                 }
152             });
153 
154             Futures.addCallback(mOpenCaptureSessionFuture, new FutureCallback<Void>() {
155                 @Override
156                 public void onSuccess(@Nullable Void result) {
157                     // Nothing to do.
158                 }
159 
160                 @Override
161                 public void onFailure(@NonNull Throwable t) {
162                     finishClose();
163                     mCaptureSessionRepository.onCaptureSessionConfigureFail(
164                             SynchronizedCaptureSessionBaseImpl.this);
165                 }
166             }, CameraXExecutors.directExecutor());
167 
168             return Futures.nonCancellationPropagating(mOpenCaptureSessionFuture);
169         }
170     }
171 
isCameraCaptureSessionOpen()172     boolean isCameraCaptureSessionOpen() {
173         synchronized (mLock) {
174             return mOpenCaptureSessionFuture != null;
175         }
176     }
177 
178     @Override
createSessionConfigurationCompat( int sessionType, @NonNull List<OutputConfigurationCompat> outputsCompat, @NonNull StateCallback stateCallback)179     public @NonNull SessionConfigurationCompat createSessionConfigurationCompat(
180             int sessionType,
181             @NonNull List<OutputConfigurationCompat> outputsCompat,
182             @NonNull StateCallback stateCallback) {
183         mCaptureSessionStateCallback = stateCallback;
184         return new SessionConfigurationCompat(sessionType, outputsCompat, getExecutor(),
185                 new CameraCaptureSession.StateCallback() {
186 
187                     @Override
188                     public void onReady(@NonNull CameraCaptureSession session) {
189                         createCaptureSessionCompat(session);
190                         SynchronizedCaptureSessionBaseImpl.this.onReady(
191                                 SynchronizedCaptureSessionBaseImpl.this);
192                     }
193 
194                     @Override
195                     public void onActive(@NonNull CameraCaptureSession session) {
196                         createCaptureSessionCompat(session);
197                         SynchronizedCaptureSessionBaseImpl.this.onActive(
198                                 SynchronizedCaptureSessionBaseImpl.this);
199                     }
200 
201                     @RequiresApi(api = Build.VERSION_CODES.O)
202                     @Override
203                     public void onCaptureQueueEmpty(@NonNull CameraCaptureSession session) {
204                         createCaptureSessionCompat(session);
205                         SynchronizedCaptureSessionBaseImpl.this.onCaptureQueueEmpty(
206                                 SynchronizedCaptureSessionBaseImpl.this);
207                     }
208 
209                     @RequiresApi(api = Build.VERSION_CODES.M)
210                     @Override
211                     public void onSurfacePrepared(
212                             @NonNull CameraCaptureSession session,
213                             @NonNull Surface surface) {
214                         createCaptureSessionCompat(session);
215                         SynchronizedCaptureSessionBaseImpl.this.onSurfacePrepared(
216                                 SynchronizedCaptureSessionBaseImpl.this, surface);
217                     }
218 
219                     @Override
220                     public void onConfigured(@NonNull CameraCaptureSession session) {
221                         try {
222                             createCaptureSessionCompat(session);
223                             SynchronizedCaptureSessionBaseImpl.this.onConfigured(
224                                     SynchronizedCaptureSessionBaseImpl.this);
225                         } finally {
226                             // Finish the mOpenCaptureSessionCompleter after callback.
227                             Completer<Void> completer;
228                             synchronized (mLock) {
229                                 Preconditions.checkNotNull(mOpenCaptureSessionCompleter,
230                                         "OpenCaptureSession completer should not null");
231                                 completer = mOpenCaptureSessionCompleter;
232                                 mOpenCaptureSessionCompleter = null;
233                             }
234                             completer.set(null);
235                         }
236                     }
237 
238                     @Override
239                     public void onConfigureFailed(@NonNull CameraCaptureSession session) {
240                         try {
241                             createCaptureSessionCompat(session);
242                             SynchronizedCaptureSessionBaseImpl.this.onConfigureFailed(
243                                     SynchronizedCaptureSessionBaseImpl.this);
244                         } finally {
245                             // Finish the mOpenCaptureSessionCompleter after callback.
246                             Completer<Void> completer;
247                             synchronized (mLock) {
248                                 Preconditions.checkNotNull(mOpenCaptureSessionCompleter,
249                                         "OpenCaptureSession completer should not null");
250                                 completer = mOpenCaptureSessionCompleter;
251                                 mOpenCaptureSessionCompleter = null;
252                             }
253                             completer.setException(new IllegalStateException("onConfigureFailed"));
254                         }
255                     }
256 
257                     @Override
258                     public void onClosed(@NonNull CameraCaptureSession session) {
259                         createCaptureSessionCompat(session);
260                         SynchronizedCaptureSessionBaseImpl.this.onClosed(
261                                 SynchronizedCaptureSessionBaseImpl.this);
262                     }
263                 });
264     }
265 
266     @Override
267     @CameraExecutor
268     public @NonNull Executor getExecutor() {
269         return mExecutor;
270     }
271 
272     void createCaptureSessionCompat(@NonNull CameraCaptureSession session) {
273         if (mCameraCaptureSessionCompat == null) {
274             mCameraCaptureSessionCompat = CameraCaptureSessionCompat.toCameraCaptureSessionCompat(
275                     session, mCompatHandler);
276         }
277     }
278 
279     @SuppressWarnings("ConstantConditions") // Implied non-null type use for surfaces.
280     @Override
281     public @NonNull ListenableFuture<List<Surface>> startWithDeferrableSurface(
282             @NonNull List<DeferrableSurface> deferrableSurfaces, long timeout) {
283         synchronized (mLock) {
284             if (mOpenerDisabled) {
285                 return Futures.immediateFailedFuture(
286                         new CancellationException("Opener is disabled"));
287             }
288 
289             ListenableFuture<List<Surface>> future = DeferrableSurfaces.surfaceListWithTimeout(
290                     deferrableSurfaces, false, timeout, getExecutor(), mScheduledExecutorService);
291 
292             mStartingSurface = FutureChain.from(future).transformAsync(surfaces -> {
293                 Logger.d(TAG, "[" + SynchronizedCaptureSessionBaseImpl.this + "] getSurface done "
294                         + "with results: " + surfaces);
295                 // If a Surface in configuredSurfaces is null it means the
296                 // Surface was not retrieved from the ListenableFuture. Only
297                 // handle the first failed Surface since subsequent calls to
298                 // CaptureSession.open() will handle the other failed Surfaces if
299                 // there are any.
300                 if (surfaces.isEmpty()) {
301                     return Futures.immediateFailedFuture(new IllegalArgumentException(
302                             "Unable to open capture session without surfaces")
303                     );
304                 }
305                 if (surfaces.contains(null)) {
306                     return Futures.immediateFailedFuture(
307                             new DeferrableSurface.SurfaceClosedException(
308                                     "Surface closed", deferrableSurfaces.get(surfaces.indexOf(null))
309                             )
310                     );
311                 }
312                 return Futures.immediateFuture(surfaces);
313             }, getExecutor());
314 
315             return Futures.nonCancellationPropagating(mStartingSurface);
316         }
317     }
318 
319     @Override
320     public boolean stop() {
321         ListenableFuture<List<Surface>> startingSurface = null;
322         try {
323             synchronized (mLock) {
324                 if (!mOpenerDisabled) {
325                     if (mStartingSurface != null) {
326                         startingSurface = mStartingSurface;
327                     }
328                     mOpenerDisabled = true;
329                 }
330 
331                 // Return true if the CameraCaptureSession creation has not been started yet.
332                 return !isCameraCaptureSessionOpen();
333             }
334         } finally {
335             if (startingSurface != null) {
336                 startingSurface.cancel(true);
337             }
338         }
339     }
340 
341     @Override
342     public @NonNull CameraCaptureSessionCompat toCameraCaptureSessionCompat() {
343         Preconditions.checkNotNull(mCameraCaptureSessionCompat);
344         return mCameraCaptureSessionCompat;
345     }
346 
347     @Override
348     public @NonNull CameraDevice getDevice() {
349         Preconditions.checkNotNull(mCameraCaptureSessionCompat);
350         return mCameraCaptureSessionCompat.toCameraCaptureSession().getDevice();
351     }
352 
353     @Override
354     public @Nullable Surface getInputSurface() {
355         Preconditions.checkNotNull(mCameraCaptureSessionCompat);
356         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
357             return Api23Impl.getInputSurface(mCameraCaptureSessionCompat.toCameraCaptureSession());
358         } else {
359             return null;
360         }
361     }
362 
363     @Override
364     public int captureSingleRequest(@NonNull CaptureRequest request,
365             CameraCaptureSession.@NonNull CaptureCallback listener) throws CameraAccessException {
366         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
367                 + "before using this API.");
368         return mCameraCaptureSessionCompat.captureSingleRequest(request, getExecutor(), listener);
369     }
370 
371     @Override
372     public int captureBurstRequests(
373             @NonNull List<CaptureRequest> requests,
374             CameraCaptureSession.@NonNull CaptureCallback listener)
375             throws CameraAccessException {
376         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
377                 + "before using this API.");
378         return mCameraCaptureSessionCompat.captureBurstRequests(requests, getExecutor(), listener);
379     }
380 
381     @Override
382     public int setSingleRepeatingRequest(
383             @NonNull CaptureRequest request,
384             CameraCaptureSession.@NonNull CaptureCallback listener)
385             throws CameraAccessException {
386         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
387                 + "before using this API.");
388         return mCameraCaptureSessionCompat.setSingleRepeatingRequest(request, getExecutor(),
389                 listener);
390     }
391 
392     @Override
393     public int setRepeatingBurstRequests(
394             @NonNull List<CaptureRequest> requests,
395             CameraCaptureSession.@NonNull CaptureCallback listener)
396             throws CameraAccessException {
397         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
398                 + "before using this API.");
399         return mCameraCaptureSessionCompat.setRepeatingBurstRequests(requests, getExecutor(),
400                 listener);
401     }
402 
403     @Override
404     @NonNull
405     public List<CaptureRequest> createHighSpeedRequestList(@NonNull CaptureRequest request)
406             throws CameraAccessException {
407         CameraCaptureSession cameraCaptureSession =
408                 Preconditions.checkNotNull(mCameraCaptureSessionCompat).toCameraCaptureSession();
409         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
410                 && cameraCaptureSession instanceof CameraConstrainedHighSpeedCaptureSession) {
411             return Api23Impl.createHighSpeedRequestList(
412                     (CameraConstrainedHighSpeedCaptureSession) cameraCaptureSession, request);
413         } else {
414             return emptyList();
415         }
416     }
417 
418     @Override
419     public int captureSingleRequest(@NonNull CaptureRequest request, @NonNull Executor executor,
420             CameraCaptureSession.@NonNull CaptureCallback listener) throws CameraAccessException {
421         Preconditions.checkNotNull(mCameraCaptureSessionCompat,
422                 "Need to call openCaptureSession before using this API.");
423         return mCameraCaptureSessionCompat.captureSingleRequest(request, executor, listener);
424     }
425 
426     @Override
427     public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
428             @NonNull Executor executor, CameraCaptureSession.@NonNull CaptureCallback listener)
429             throws CameraAccessException {
430         Preconditions.checkNotNull(mCameraCaptureSessionCompat,
431                 "Need to call openCaptureSession before using this API.");
432         return mCameraCaptureSessionCompat.captureBurstRequests(requests, executor, listener);
433     }
434 
435     @Override
436     public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
437             @NonNull Executor executor, CameraCaptureSession.@NonNull CaptureCallback listener)
438             throws CameraAccessException {
439         Preconditions.checkNotNull(mCameraCaptureSessionCompat,
440                 "Need to call openCaptureSession before using this API.");
441         return mCameraCaptureSessionCompat.setSingleRepeatingRequest(request, executor, listener);
442     }
443 
444     @Override
445     public int setRepeatingBurstRequests(@NonNull List<CaptureRequest> requests,
446             @NonNull Executor executor, CameraCaptureSession.@NonNull CaptureCallback listener)
447             throws CameraAccessException {
448         Preconditions.checkNotNull(mCameraCaptureSessionCompat,
449                 "Need to call openCaptureSession before using this API.");
450         return mCameraCaptureSessionCompat.setRepeatingBurstRequests(requests, executor, listener);
451     }
452 
453     @Override
454     public void stopRepeating() throws CameraAccessException {
455         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
456                 + "before using this API.");
457         mCameraCaptureSessionCompat.toCameraCaptureSession().stopRepeating();
458     }
459 
460     @Override
461     public void abortCaptures() throws CameraAccessException {
462         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
463                 + "before using this API.");
464         mCameraCaptureSessionCompat.toCameraCaptureSession().abortCaptures();
465     }
466 
467     @Override
468     public void close() {
469         Preconditions.checkNotNull(mCameraCaptureSessionCompat, "Need to call openCaptureSession "
470                 + "before using this API.");
471         mCaptureSessionRepository.onCaptureSessionClosing(this);
472         mCameraCaptureSessionCompat.toCameraCaptureSession().close();
473         // Invoke the onSessionFinished callback directly to inform the closing
474         // step can be finished.
475         getExecutor().execute(() -> onSessionFinished(this));
476     }
477 
478     @Override
479     public void onReady(@NonNull SynchronizedCaptureSession session) {
480         Objects.requireNonNull(mCaptureSessionStateCallback);
481         mCaptureSessionStateCallback.onReady(session);
482     }
483 
484     @Override
485     public void onActive(@NonNull SynchronizedCaptureSession session) {
486         Objects.requireNonNull(mCaptureSessionStateCallback);
487         mCaptureSessionStateCallback.onActive(session);
488     }
489 
490     @RequiresApi(api = Build.VERSION_CODES.O)
491     @Override
492     public void onCaptureQueueEmpty(@NonNull SynchronizedCaptureSession session) {
493         Objects.requireNonNull(mCaptureSessionStateCallback);
494         mCaptureSessionStateCallback.onCaptureQueueEmpty(session);
495     }
496 
497     @RequiresApi(api = Build.VERSION_CODES.M)
498     @Override
499     public void onSurfacePrepared(@NonNull SynchronizedCaptureSession session,
500             @NonNull Surface surface) {
501         Objects.requireNonNull(mCaptureSessionStateCallback);
502         mCaptureSessionStateCallback.onSurfacePrepared(session, surface);
503     }
504 
505     @Override
506     public void onConfigured(@NonNull SynchronizedCaptureSession session) {
507         Objects.requireNonNull(mCaptureSessionStateCallback);
508         mCaptureSessionRepository.onCaptureSessionCreated(this);
509         mCaptureSessionStateCallback.onConfigured(session);
510     }
511 
512     @Override
513     public void onConfigureFailed(@NonNull SynchronizedCaptureSession session) {
514         Objects.requireNonNull(mCaptureSessionStateCallback);
515         finishClose();
516         mCaptureSessionRepository.onCaptureSessionConfigureFail(this);
517         mCaptureSessionStateCallback.onConfigureFailed(session);
518     }
519 
520     /**
521      * The onClosed will be invoked when the CameraCaptureSession is closed or when we apply the
522      * workaround the issues like b/140955560, b/144817309 to force close the session.
523      *
524      * <p>This callback will be invoked after the SynchronizedCaptureSession#openCaptureSession
525      * is completed, and at most be called once.
526      *
527      * @param session the SynchronizedCaptureSession that is created by
528      * {@link SynchronizedCaptureSessionImpl#openCaptureSession}
529      */
530     @Override
531     public void onClosed(@NonNull SynchronizedCaptureSession session) {
532         ListenableFuture<Void> openFuture = null;
533         synchronized (mLock) {
534             if (!mClosed) {
535                 mClosed = true;
536                 Preconditions.checkNotNull(mOpenCaptureSessionFuture,
537                         "Need to call openCaptureSession before using this API.");
538                 // Only callback onClosed after the capture session is configured.
539                 openFuture = mOpenCaptureSessionFuture;
540             }
541         }
542         finishClose();
543         if (openFuture != null) {
544             openFuture.addListener(() -> {
545                 // Set the CaptureSession closed before invoke the state callback.
546                 mCaptureSessionRepository.onCaptureSessionClosed(
547                         SynchronizedCaptureSessionBaseImpl.this);
548 
549                 // Invoke the onSessionFinished since the SynchronizedCaptureSession receives
550                 // the onClosed callback, we can treat this session is already in closed state.
551                 onSessionFinished(session);
552 
553                 if (mCameraCaptureSessionCompat != null) {
554                     // Only call onClosed() if we have the instance of CameraCaptureSession.
555                     Objects.requireNonNull(mCaptureSessionStateCallback);
556                     mCaptureSessionStateCallback.onClosed(session);
557                 } else {
558                     Logger.w(TAG, "[" + SynchronizedCaptureSessionBaseImpl.this + "] Cannot call "
559                             + "onClosed() when the CameraCaptureSession is not correctly "
560                             + "configured.");
561                 }
562             }, CameraXExecutors.directExecutor());
563         }
564     }
565 
566     @Override
567     void onSessionFinished(@NonNull SynchronizedCaptureSession session) {
568         ListenableFuture<Void> openFuture = null;
569         synchronized (mLock) {
570             if (!mSessionFinished) {
571                 mSessionFinished = true;
572                 Preconditions.checkNotNull(mOpenCaptureSessionFuture,
573                         "Need to call openCaptureSession before using this API.");
574                 // Only callback onClosed after the capture session is configured.
575                 openFuture = mOpenCaptureSessionFuture;
576             }
577         }
578         if (openFuture != null) {
579             openFuture.addListener(() -> {
580                 Objects.requireNonNull(mCaptureSessionStateCallback);
581                 mCaptureSessionStateCallback.onSessionFinished(session);
582             }, CameraXExecutors.directExecutor());
583         }
584     }
585 
586     /**
587      * Hold the DeferrableSurfaces to be used for this session to prevent the DeferrableSurfaces
588      * from being released.
589      *
590      * <p>Only one set of DeferrableSurfaces will be set to in used at the same time, it will unset
591      * the previous deferrableSurfaces if it has been set before.
592      *
593      * @param deferrableSurfaces will be set to in used.
594      * @throws DeferrableSurface.SurfaceClosedException if the deferrableSurfaces contains any
595      * closed surface.
596      */
597     void holdDeferrableSurfaces(@NonNull List<DeferrableSurface> deferrableSurfaces)
598             throws DeferrableSurface.SurfaceClosedException {
599         synchronized (mLock) {
600             releaseDeferrableSurfaces();
601             DeferrableSurfaces.incrementAll(deferrableSurfaces);
602             mHeldDeferrableSurfaces = deferrableSurfaces;
603         }
604     }
605 
606     /**
607      * Release the DeferrableSurfaces that is held by the holdDeferrableSurfaces()
608      */
609     void releaseDeferrableSurfaces() {
610         synchronized (mLock) {
611             if (mHeldDeferrableSurfaces != null) {
612                 DeferrableSurfaces.decrementAll(mHeldDeferrableSurfaces);
613 
614                 // Clears the mRegisteredDeferrableSurfaces to prevent from duplicate
615                 // decrement calls.
616                 mHeldDeferrableSurfaces = null;
617             }
618         }
619     }
620 
621     @Override
622     public void finishClose() {
623         releaseDeferrableSurfaces();
624     }
625 
626     @Override
627     public void onCameraDeviceError(int error) {
628         // Nothing to do for the default implementation.
629     }
630 
631     /**
632      * Nested class to avoid verification errors for methods introduced in Android 6.0 (API 23).
633      */
634     @RequiresApi(23)
635     private static class Api23Impl {
636 
637         private Api23Impl() {
638         }
639 
640         static Surface getInputSurface(CameraCaptureSession cameraCaptureSession) {
641             return cameraCaptureSession.getInputSurface();
642         }
643 
644         @NonNull
645         static List<CaptureRequest> createHighSpeedRequestList(
646                 @NonNull CameraConstrainedHighSpeedCaptureSession constrainedHighSpeedSession,
647                 @NonNull CaptureRequest captureRequest)
648                 throws CameraAccessException {
649             return constrainedHighSpeedSession.createHighSpeedRequestList(captureRequest);
650         }
651     }
652 }
653