1 /* 2 * Copyright 2019 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 android.graphics.ImageFormat; 20 import android.hardware.camera2.CameraAccessException; 21 import android.hardware.camera2.CameraCaptureSession; 22 import android.hardware.camera2.CameraCaptureSession.CaptureCallback; 23 import android.hardware.camera2.CameraDevice; 24 import android.hardware.camera2.CaptureRequest; 25 import android.hardware.camera2.TotalCaptureResult; 26 import android.hardware.camera2.params.DynamicRangeProfiles; 27 import android.hardware.camera2.params.MultiResolutionStreamInfo; 28 import android.hardware.camera2.params.OutputConfiguration; 29 import android.hardware.camera2.params.SessionConfiguration; 30 import android.os.Build; 31 import android.view.Surface; 32 33 import androidx.annotation.GuardedBy; 34 import androidx.annotation.OptIn; 35 import androidx.annotation.RequiresApi; 36 import androidx.camera.camera2.impl.Camera2ImplConfig; 37 import androidx.camera.camera2.internal.compat.params.DynamicRangeConversions; 38 import androidx.camera.camera2.internal.compat.params.DynamicRangesCompat; 39 import androidx.camera.camera2.internal.compat.params.InputConfigurationCompat; 40 import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat; 41 import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat; 42 import androidx.camera.camera2.internal.compat.quirk.CaptureNoResponseQuirk; 43 import androidx.camera.camera2.internal.compat.workaround.RequestMonitor; 44 import androidx.camera.camera2.internal.compat.workaround.StillCaptureFlow; 45 import androidx.camera.camera2.internal.compat.workaround.TemplateParamsOverride; 46 import androidx.camera.camera2.internal.compat.workaround.TorchStateReset; 47 import androidx.camera.camera2.interop.ExperimentalCamera2Interop; 48 import androidx.camera.core.DynamicRange; 49 import androidx.camera.core.Logger; 50 import androidx.camera.core.MirrorMode; 51 import androidx.camera.core.impl.CameraCaptureCallback; 52 import androidx.camera.core.impl.CaptureConfig; 53 import androidx.camera.core.impl.DeferrableSurface; 54 import androidx.camera.core.impl.Quirks; 55 import androidx.camera.core.impl.SessionConfig; 56 import androidx.camera.core.impl.utils.SurfaceUtil; 57 import androidx.camera.core.impl.utils.executor.CameraXExecutors; 58 import androidx.camera.core.impl.utils.futures.FutureCallback; 59 import androidx.camera.core.impl.utils.futures.FutureChain; 60 import androidx.camera.core.impl.utils.futures.Futures; 61 import androidx.concurrent.futures.CallbackToFutureAdapter; 62 import androidx.core.util.Preconditions; 63 import androidx.tracing.Trace; 64 65 import com.google.common.util.concurrent.ListenableFuture; 66 67 import org.jspecify.annotations.NonNull; 68 import org.jspecify.annotations.Nullable; 69 70 import java.util.ArrayList; 71 import java.util.Collection; 72 import java.util.Collections; 73 import java.util.HashMap; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Objects; 77 import java.util.concurrent.CancellationException; 78 79 /** 80 * A basic implementation of {@link CaptureSessionInterface} for capturing images from the camera 81 * which is tied to a specific {@link CameraDevice}. 82 */ 83 final class CaptureSession implements CaptureSessionInterface { 84 private static final String TAG = "CaptureSession"; 85 86 // TODO: Find a proper timeout threshold. 87 private static final long TIMEOUT_GET_SURFACE_IN_MS = 5000L; 88 /** Lock to ensure session operations run atomically. */ 89 final Object mSessionLock = new Object(); 90 /** The configuration for the currently issued single capture requests. */ 91 @GuardedBy("mSessionLock") 92 private final List<CaptureConfig> mCaptureConfigs = new ArrayList<>(); 93 @GuardedBy("mSessionLock") 94 private final StateCallback mCaptureSessionStateCallback; 95 /** The Opener to help on creating the SynchronizedCaptureSession. */ 96 @GuardedBy("mSessionLock") 97 SynchronizedCaptureSession.@Nullable Opener mSessionOpener; 98 /** The framework camera capture session held by this session. */ 99 @GuardedBy("mSessionLock") 100 @Nullable SynchronizedCaptureSession mSynchronizedCaptureSession; 101 /** The configuration for the currently issued capture requests. */ 102 @GuardedBy("mSessionLock") 103 @Nullable SessionConfig mSessionConfig; 104 /** 105 * The map of DeferrableSurface to Surface. It is both for restoring the surfaces used to 106 * configure the current capture session and for getting the configured surface from a 107 * DeferrableSurface. 108 */ 109 @GuardedBy("mSessionLock") 110 private final Map<DeferrableSurface, Surface> mConfiguredSurfaceMap = new HashMap<>(); 111 112 /** The list of DeferrableSurface used to notify surface detach events */ 113 @GuardedBy("mSessionLock") 114 List<DeferrableSurface> mConfiguredDeferrableSurfaces = Collections.emptyList(); 115 /** Maximum state this session achieved (for debugging) */ 116 @GuardedBy("mSessionLock") 117 State mHighestState = State.UNINITIALIZED; 118 /** Tracks the current state of the session. */ 119 @GuardedBy("mSessionLock") 120 State mState = State.UNINITIALIZED; 121 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 122 @GuardedBy("mSessionLock") 123 ListenableFuture<Void> mReleaseFuture; 124 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 125 @GuardedBy("mSessionLock") 126 CallbackToFutureAdapter.Completer<Void> mReleaseCompleter; 127 @GuardedBy("mSessionLock") 128 private @NonNull Map<DeferrableSurface, Long> mStreamUseCaseMap = new HashMap<>(); 129 private final StillCaptureFlow mStillCaptureFlow = new StillCaptureFlow(); 130 private final TorchStateReset mTorchStateReset = new TorchStateReset(); 131 private final RequestMonitor mRequestMonitor; 132 private final DynamicRangesCompat mDynamicRangesCompat; 133 private final TemplateParamsOverride mTemplateParamsOverride; 134 private final boolean mCanUseMultiResolutionImageReader; 135 136 /** 137 * Constructor for CaptureSession without CameraQuirk. 138 */ CaptureSession(@onNull DynamicRangesCompat dynamicRangesCompat)139 CaptureSession(@NonNull DynamicRangesCompat dynamicRangesCompat) { 140 this(dynamicRangesCompat, false); 141 } 142 143 /** 144 * Constructor for CaptureSession without CameraQuirk. 145 */ CaptureSession(@onNull DynamicRangesCompat dynamicRangesCompat, boolean canUseMultiResolutionImageReader)146 CaptureSession(@NonNull DynamicRangesCompat dynamicRangesCompat, 147 boolean canUseMultiResolutionImageReader) { 148 this(dynamicRangesCompat, new Quirks(Collections.emptyList()), 149 canUseMultiResolutionImageReader); 150 } 151 152 /** 153 * Constructor for CaptureSession with CameraQuirk. 154 */ CaptureSession(@onNull DynamicRangesCompat dynamicRangesCompat, @NonNull Quirks cameraQuirks)155 CaptureSession(@NonNull DynamicRangesCompat dynamicRangesCompat, 156 @NonNull Quirks cameraQuirks) { 157 this(dynamicRangesCompat, cameraQuirks, false); 158 } 159 160 /** 161 * Constructor for CaptureSession. 162 */ CaptureSession(@onNull DynamicRangesCompat dynamicRangesCompat, @NonNull Quirks cameraQuirks, boolean canUseMultiResolutionImageReader)163 CaptureSession(@NonNull DynamicRangesCompat dynamicRangesCompat, 164 @NonNull Quirks cameraQuirks, boolean canUseMultiResolutionImageReader) { 165 setState(State.INITIALIZED); 166 mDynamicRangesCompat = dynamicRangesCompat; 167 mCaptureSessionStateCallback = new StateCallback(); 168 mRequestMonitor = new RequestMonitor(cameraQuirks.contains(CaptureNoResponseQuirk.class)); 169 mTemplateParamsOverride = new TemplateParamsOverride(cameraQuirks); 170 mCanUseMultiResolutionImageReader = canUseMultiResolutionImageReader; 171 } 172 173 @Override setStreamUseCaseMap(@onNull Map<DeferrableSurface, Long> streamUseCaseMap)174 public void setStreamUseCaseMap(@NonNull Map<DeferrableSurface, Long> streamUseCaseMap) { 175 synchronized (mSessionLock) { 176 mStreamUseCaseMap = streamUseCaseMap; 177 } 178 } 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override getSessionConfig()184 public @Nullable SessionConfig getSessionConfig() { 185 synchronized (mSessionLock) { 186 return mSessionConfig; 187 } 188 } 189 190 /** 191 * {@inheritDoc} 192 */ 193 @Override setSessionConfig(@ullable SessionConfig sessionConfig)194 public void setSessionConfig(@Nullable SessionConfig sessionConfig) { 195 synchronized (mSessionLock) { 196 switch (mState) { 197 case UNINITIALIZED: 198 throw new IllegalStateException( 199 "setSessionConfig() should not be possible in state: " + mState); 200 case INITIALIZED: 201 case GET_SURFACE: 202 case OPENING: 203 mSessionConfig = sessionConfig; 204 break; 205 case OPENED: 206 mSessionConfig = sessionConfig; 207 if (sessionConfig == null) { 208 return; 209 } 210 211 if (!mConfiguredSurfaceMap.keySet().containsAll(sessionConfig.getSurfaces())) { 212 Logger.e(TAG, "Does not have the proper configured lists"); 213 return; 214 } 215 216 Logger.d(TAG, "Attempting to submit CaptureRequest after setting"); 217 issueRepeatingCaptureRequests(mSessionConfig); 218 break; 219 case CLOSED: 220 case RELEASING: 221 case RELEASED: 222 throw new IllegalStateException( 223 "Session configuration cannot be set on a closed/released session."); 224 } 225 } 226 } 227 228 /** 229 * {@inheritDoc} 230 */ 231 @Override open(@onNull SessionConfig sessionConfig, @NonNull CameraDevice cameraDevice, SynchronizedCaptureSession.@NonNull Opener opener)232 public @NonNull ListenableFuture<Void> open(@NonNull SessionConfig sessionConfig, 233 @NonNull CameraDevice cameraDevice, 234 SynchronizedCaptureSession.@NonNull Opener opener) { 235 synchronized (mSessionLock) { 236 switch (mState) { 237 case INITIALIZED: 238 setState(State.GET_SURFACE); 239 mConfiguredDeferrableSurfaces = new ArrayList<>(sessionConfig.getSurfaces()); 240 mSessionOpener = opener; 241 ListenableFuture<Void> openFuture = FutureChain.from( 242 mSessionOpener.startWithDeferrableSurface( 243 mConfiguredDeferrableSurfaces, TIMEOUT_GET_SURFACE_IN_MS) 244 ).transformAsync( 245 surfaces -> openCaptureSession(surfaces, sessionConfig, cameraDevice), 246 mSessionOpener.getExecutor()); 247 248 Futures.addCallback(openFuture, new FutureCallback<Void>() { 249 @Override 250 public void onSuccess(@Nullable Void result) { 251 // Nothing to do. 252 } 253 254 @Override 255 public void onFailure(@NonNull Throwable t) { 256 synchronized (mSessionLock) { 257 // Stop the Opener if we get any failure during opening. 258 mSessionOpener.stop(); 259 switch (mState) { 260 case OPENING: 261 case CLOSED: 262 case RELEASING: 263 if (!(t instanceof CancellationException)) { 264 Logger.w(TAG, "Opening session with fail " + mState, t); 265 finishClose(); 266 } 267 break; 268 default: 269 } 270 } 271 } 272 }, mSessionOpener.getExecutor()); 273 274 // The cancellation of the external ListenableFuture cannot actually stop 275 // the open session since we can't cancel the camera2 flow. The underlying 276 // Future is used to track the session is configured, we don't want to 277 // propagate the cancellation event to it. Wrap the Future in a 278 // NonCancellationPropagatingFuture, so that if the external caller cancels 279 // the Future it won't affect the underlying Future. 280 return Futures.nonCancellationPropagating(openFuture); 281 default: 282 Logger.e(TAG, "Open not allowed in state: " + mState); 283 } 284 285 return Futures.immediateFailedFuture( 286 new IllegalStateException("open() should not allow the state: " + mState)); 287 } 288 } 289 290 @OptIn(markerClass = ExperimentalCamera2Interop.class) openCaptureSession( @onNull List<Surface> configuredSurfaces, @NonNull SessionConfig sessionConfig, @NonNull CameraDevice cameraDevice)291 private @NonNull ListenableFuture<Void> openCaptureSession( 292 @NonNull List<Surface> configuredSurfaces, @NonNull SessionConfig sessionConfig, 293 @NonNull CameraDevice cameraDevice) { 294 synchronized (mSessionLock) { 295 switch (mState) { 296 case UNINITIALIZED: 297 case INITIALIZED: 298 case OPENED: 299 return Futures.immediateFailedFuture(new IllegalStateException( 300 "openCaptureSession() should not be possible in state: " + mState)); 301 case GET_SURFACE: 302 // Establishes the mapping of DeferrableSurface to Surface. Capture request 303 // will use this mapping to get the Surface from DeferrableSurface. 304 mConfiguredSurfaceMap.clear(); 305 for (int i = 0; i < configuredSurfaces.size(); i++) { 306 mConfiguredSurfaceMap.put(mConfiguredDeferrableSurfaces.get(i), 307 configuredSurfaces.get(i)); 308 } 309 310 setState(State.OPENING); 311 Logger.d(TAG, "Opening capture session."); 312 SynchronizedCaptureSession.StateCallback callbacks = 313 SynchronizedCaptureSessionStateCallbacks.createComboCallback( 314 mCaptureSessionStateCallback, 315 new SynchronizedCaptureSessionStateCallbacks.Adapter( 316 sessionConfig.getSessionStateCallbacks()) 317 ); 318 319 Camera2ImplConfig camera2Config = 320 new Camera2ImplConfig(sessionConfig.getImplementationOptions()); 321 // Generate the CaptureRequest builder from repeating request since Android 322 // recommend use the same template type as the initial capture request. The 323 // tag and output targets would be ignored by default. 324 CaptureConfig.Builder sessionParameterConfigBuilder = 325 CaptureConfig.Builder.from(sessionConfig.getRepeatingCaptureConfig()); 326 327 Map<SessionConfig.OutputConfig, OutputConfigurationCompat> 328 mrirOutputConfigurationCompatMap = new HashMap<>(); 329 if (mCanUseMultiResolutionImageReader && Build.VERSION.SDK_INT >= 35) { 330 Map<Integer, List<SessionConfig.OutputConfig>> groupIdToOutputConfigsMap = 331 groupMrirOutputConfigs(sessionConfig.getOutputConfigs()); 332 mrirOutputConfigurationCompatMap = 333 createMultiResolutionOutputConfigurationCompats( 334 groupIdToOutputConfigsMap, mConfiguredSurfaceMap); 335 } 336 337 List<OutputConfigurationCompat> outputConfigList = new ArrayList<>(); 338 String physicalCameraIdForAllStreams = 339 camera2Config.getPhysicalCameraId(null); 340 for (SessionConfig.OutputConfig outputConfig : 341 sessionConfig.getOutputConfigs()) { 342 OutputConfigurationCompat outputConfiguration = null; 343 344 // If an OutputConfiguration has been created via the MRIR approach, 345 // retrieves it from the map 346 if (mCanUseMultiResolutionImageReader && Build.VERSION.SDK_INT >= 35) { 347 outputConfiguration = mrirOutputConfigurationCompatMap.get( 348 outputConfig); 349 } 350 351 // Otherwise, uses the original approach to create the 352 // OutputConfigurationCompat. 353 if (outputConfiguration == null) { 354 outputConfiguration = getOutputConfigurationCompat( 355 outputConfig, 356 mConfiguredSurfaceMap, 357 physicalCameraIdForAllStreams); 358 if (mStreamUseCaseMap.containsKey(outputConfig.getSurface())) { 359 outputConfiguration.setStreamUseCase( 360 mStreamUseCaseMap.get(outputConfig.getSurface())); 361 } 362 } 363 outputConfigList.add(outputConfiguration); 364 } 365 366 // Some DeferrableSurfaces might actually point to the same Surface. For 367 // example, a Surface(ImageReader) could be shared between use cases. 368 // Therefore, there might be duplicate surfaces that need to be removed. 369 // We might consider removing this logic if this is no longer necessary. 370 outputConfigList = getUniqueOutputConfigurations(outputConfigList); 371 372 SessionConfigurationCompat sessionConfigCompat = 373 mSessionOpener.createSessionConfigurationCompat( 374 sessionConfig.getSessionType(), outputConfigList, 375 callbacks); 376 377 if (sessionConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG 378 && sessionConfig.getInputConfiguration() != null) { 379 sessionConfigCompat.setInputConfiguration( 380 InputConfigurationCompat.wrap( 381 sessionConfig.getInputConfiguration())); 382 } 383 384 try { 385 CaptureRequest captureRequest = 386 Camera2CaptureRequestBuilder.buildWithoutTarget( 387 sessionParameterConfigBuilder.build(), cameraDevice, 388 mTemplateParamsOverride); 389 if (captureRequest != null) { 390 sessionConfigCompat.setSessionParameters(captureRequest); 391 } 392 } catch (CameraAccessException e) { 393 return Futures.immediateFailedFuture(e); 394 } 395 396 return mSessionOpener.openCaptureSession(cameraDevice, 397 sessionConfigCompat, mConfiguredDeferrableSurfaces); 398 default: 399 return Futures.immediateFailedFuture(new CancellationException( 400 "openCaptureSession() not execute in state: " + mState)); 401 } 402 } 403 } 404 getUniqueOutputConfigurations( @onNull List<OutputConfigurationCompat> outputConfigurations)405 private @NonNull List<OutputConfigurationCompat> getUniqueOutputConfigurations( 406 @NonNull List<OutputConfigurationCompat> outputConfigurations) { 407 List<Surface> addedSurfaces = new ArrayList<>(); 408 List<OutputConfigurationCompat> results = new ArrayList<>(); 409 for (OutputConfigurationCompat outputConfiguration : outputConfigurations) { 410 if (addedSurfaces.contains(outputConfiguration.getSurface())) { 411 // Surface already added, ignore this outputConfiguration. 412 continue; 413 } 414 addedSurfaces.add(outputConfiguration.getSurface()); 415 results.add(outputConfiguration); 416 } 417 return results; 418 } 419 getOutputConfigurationCompat( SessionConfig.@onNull OutputConfig outputConfig, @NonNull Map<DeferrableSurface, Surface> configuredSurfaceMap, @Nullable String physicalCameraIdForAllStreams)420 private @NonNull OutputConfigurationCompat getOutputConfigurationCompat( 421 SessionConfig.@NonNull OutputConfig outputConfig, 422 @NonNull Map<DeferrableSurface, Surface> configuredSurfaceMap, 423 @Nullable String physicalCameraIdForAllStreams) { 424 Surface surface = configuredSurfaceMap.get(outputConfig.getSurface()); 425 Preconditions.checkNotNull(surface, 426 "Surface in OutputConfig not found in configuredSurfaceMap."); 427 428 OutputConfigurationCompat outputConfiguration = 429 new OutputConfigurationCompat(outputConfig.getSurfaceGroupId(), 430 surface); 431 // Set the desired physical camera ID, or null to use the logical stream. 432 // TODO(b/219414502): Configure different streams with different physical 433 // camera IDs. 434 if (physicalCameraIdForAllStreams != null) { 435 outputConfiguration.setPhysicalCameraId(physicalCameraIdForAllStreams); 436 } else { 437 outputConfiguration.setPhysicalCameraId( 438 outputConfig.getPhysicalCameraId()); 439 } 440 441 // No need to map MIRROR_MODE_ON_FRONT_ONLY to MIRROR_MODE_AUTO 442 // since its default value in framework 443 if (outputConfig.getMirrorMode() == MirrorMode.MIRROR_MODE_OFF) { 444 outputConfiguration.setMirrorMode(OutputConfiguration.MIRROR_MODE_NONE); 445 } else if (outputConfig.getMirrorMode() == MirrorMode.MIRROR_MODE_ON) { 446 outputConfiguration.setMirrorMode(OutputConfiguration.MIRROR_MODE_H); 447 } 448 449 if (!outputConfig.getSharedSurfaces().isEmpty()) { 450 outputConfiguration.enableSurfaceSharing(); 451 for (DeferrableSurface sharedDeferSurface : outputConfig.getSharedSurfaces()) { 452 Surface sharedSurface = configuredSurfaceMap.get(sharedDeferSurface); 453 Preconditions.checkNotNull(sharedSurface, 454 "Surface in OutputConfig not found in configuredSurfaceMap."); 455 outputConfiguration.addSurface(sharedSurface); 456 } 457 } 458 459 long dynamicRangeProfile = DynamicRangeProfiles.STANDARD; 460 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 461 DynamicRangeProfiles dynamicRangeProfiles = 462 mDynamicRangesCompat.toDynamicRangeProfiles(); 463 if (dynamicRangeProfiles != null) { 464 DynamicRange requestedDynamicRange = outputConfig.getDynamicRange(); 465 Long dynamicRangeProfileOrNull = 466 DynamicRangeConversions.dynamicRangeToFirstSupportedProfile( 467 requestedDynamicRange, dynamicRangeProfiles); 468 if (dynamicRangeProfileOrNull == null) { 469 Logger.e(TAG, 470 "Requested dynamic range is not supported. Defaulting to STANDARD " 471 + "dynamic range profile.\nRequested dynamic range:\n " 472 + requestedDynamicRange); 473 } else { 474 dynamicRangeProfile = dynamicRangeProfileOrNull; 475 } 476 } 477 } 478 outputConfiguration.setDynamicRangeProfile(dynamicRangeProfile); 479 return outputConfiguration; 480 } 481 482 /** 483 * {@inheritDoc} 484 */ 485 @Override close()486 public void close() { 487 synchronized (mSessionLock) { 488 switch (mState) { 489 case UNINITIALIZED: 490 throw new IllegalStateException( 491 "close() should not be possible in state: " + mState); 492 case GET_SURFACE: 493 Preconditions.checkNotNull(mSessionOpener, 494 "The Opener shouldn't null in state:" + mState); 495 mSessionOpener.stop(); 496 // Fall through 497 case INITIALIZED: 498 setState(State.RELEASED); 499 break; 500 case OPENED: 501 // Not break close flow. Fall through 502 case OPENING: 503 Preconditions.checkNotNull(mSessionOpener, 504 "The Opener shouldn't null in state:" + mState); 505 mSessionOpener.stop(); 506 setState(State.CLOSED); 507 mRequestMonitor.stop(); 508 mSessionConfig = null; 509 510 break; 511 case CLOSED: 512 case RELEASING: 513 case RELEASED: 514 break; 515 } 516 } 517 } 518 519 /** 520 * {@inheritDoc} 521 */ 522 @SuppressWarnings("ObjectToString") 523 @Override release(boolean abortInFlightCaptures)524 public @NonNull ListenableFuture<Void> release(boolean abortInFlightCaptures) { 525 synchronized (mSessionLock) { 526 switch (mState) { 527 case UNINITIALIZED: 528 throw new IllegalStateException( 529 "release() should not be possible in state: " + mState); 530 case OPENED: 531 case CLOSED: 532 if (mSynchronizedCaptureSession != null) { 533 if (abortInFlightCaptures) { 534 try { 535 mSynchronizedCaptureSession.abortCaptures(); 536 } catch (CameraAccessException e) { 537 // We couldn't abort the captures, but we should continue on to 538 // release the session. 539 Logger.e(TAG, "Unable to abort captures.", e); 540 } 541 } 542 mSynchronizedCaptureSession.close(); 543 } 544 // Fall through 545 case OPENING: 546 setState(State.RELEASING); 547 mRequestMonitor.stop(); 548 Preconditions.checkNotNull(mSessionOpener, 549 "The Opener shouldn't null in state:" + mState); 550 if (mSessionOpener.stop()) { 551 // The CameraCaptureSession doesn't created finish the release flow 552 // directly. 553 finishClose(); 554 break; 555 } 556 // Fall through 557 case RELEASING: 558 if (mReleaseFuture == null) { 559 mReleaseFuture = CallbackToFutureAdapter.getFuture( 560 completer -> { 561 synchronized (mSessionLock) { 562 Preconditions.checkState(mReleaseCompleter == null, 563 "Release completer expected to be null"); 564 mReleaseCompleter = completer; 565 return "Release[session=" + CaptureSession.this + "]"; 566 } 567 }); 568 } 569 570 return mReleaseFuture; 571 case GET_SURFACE: 572 Preconditions.checkNotNull(mSessionOpener, 573 "The Opener shouldn't null in state:" + mState); 574 mSessionOpener.stop(); 575 // Fall through 576 case INITIALIZED: 577 setState(State.RELEASED); 578 // Fall through 579 case RELEASED: 580 break; 581 } 582 } 583 584 // Already released. Return success immediately. 585 return Futures.immediateFuture(null); 586 } 587 588 /** 589 * {@inheritDoc} 590 */ 591 @Override issueCaptureRequests(@onNull List<CaptureConfig> captureConfigs)592 public void issueCaptureRequests(@NonNull List<CaptureConfig> captureConfigs) { 593 synchronized (mSessionLock) { 594 switch (mState) { 595 case UNINITIALIZED: 596 throw new IllegalStateException( 597 "issueCaptureRequests() should not be possible in state: " 598 + mState); 599 case INITIALIZED: 600 case GET_SURFACE: 601 case OPENING: 602 mCaptureConfigs.addAll(captureConfigs); 603 break; 604 case OPENED: 605 mCaptureConfigs.addAll(captureConfigs); 606 issuePendingCaptureRequest(); 607 break; 608 case CLOSED: 609 case RELEASING: 610 case RELEASED: 611 throw new IllegalStateException( 612 "Cannot issue capture request on a closed/released session."); 613 } 614 } 615 } 616 617 /** 618 * {@inheritDoc} 619 */ 620 @Override getCaptureConfigs()621 public @NonNull List<CaptureConfig> getCaptureConfigs() { 622 synchronized (mSessionLock) { 623 return Collections.unmodifiableList(mCaptureConfigs); 624 } 625 } 626 627 /** Returns the current state of the session. */ getState()628 State getState() { 629 synchronized (mSessionLock) { 630 return mState; 631 } 632 } 633 634 @Override isInOpenState()635 public boolean isInOpenState() { 636 synchronized (mSessionLock) { 637 return mState == State.OPENED || mState == State.OPENING; 638 } 639 } 640 641 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 642 @GuardedBy("mSessionLock") finishClose()643 void finishClose() { 644 if (mState == State.RELEASED) { 645 Logger.d(TAG, "Skipping finishClose due to being state RELEASED."); 646 return; 647 } 648 649 setState(State.RELEASED); 650 mSynchronizedCaptureSession = null; 651 652 if (mReleaseCompleter != null) { 653 mReleaseCompleter.set(null); 654 mReleaseCompleter = null; 655 } 656 } 657 658 /** 659 * Sets the {@link CaptureRequest} so that the camera will start producing data. 660 * 661 * <p>It will stop running repeating if there are no surfaces. 662 */ 663 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ issueRepeatingCaptureRequests(@ullable SessionConfig sessionConfig)664 int issueRepeatingCaptureRequests(@Nullable SessionConfig sessionConfig) { 665 synchronized (mSessionLock) { 666 if (sessionConfig == null) { 667 Logger.d(TAG, "Skipping issueRepeatingCaptureRequests for no configuration case."); 668 return -1; 669 } 670 671 if (mState != State.OPENED) { 672 Logger.d(TAG, "Skipping issueRepeatingCaptureRequests due to session closed"); 673 return -1; 674 } 675 676 CaptureConfig captureConfig = sessionConfig.getRepeatingCaptureConfig(); 677 if (captureConfig.getSurfaces().isEmpty()) { 678 Logger.d(TAG, "Skipping issueRepeatingCaptureRequests for no surface."); 679 try { 680 // At least from Android L, framework will ignore the stopRepeating() if 681 // there is no ongoing repeating request, so it should be safe to always call 682 // stopRepeating() without checking if there is a repeating request. 683 mSynchronizedCaptureSession.stopRepeating(); 684 } catch (CameraAccessException e) { 685 Logger.e(TAG, "Unable to access camera: " + e.getMessage()); 686 Thread.dumpStack(); 687 } 688 return -1; 689 } 690 691 try { 692 Logger.d(TAG, "Issuing request for session."); 693 CaptureRequest captureRequest = Camera2CaptureRequestBuilder.build( 694 captureConfig, mSynchronizedCaptureSession.getDevice(), 695 mConfiguredSurfaceMap, true, mTemplateParamsOverride); 696 if (captureRequest == null) { 697 Logger.d(TAG, "Skipping issuing empty request for session."); 698 return -1; 699 } 700 701 CameraCaptureSession.CaptureCallback comboCaptureCallback = 702 mRequestMonitor.createMonitorListener(createCamera2CaptureCallback( 703 captureConfig.getCameraCaptureCallbacks())); 704 705 if (sessionConfig.getSessionType() == SessionConfiguration.SESSION_HIGH_SPEED) { 706 List<CaptureRequest> requests = 707 mSynchronizedCaptureSession.createHighSpeedRequestList(captureRequest); 708 return mSynchronizedCaptureSession.setRepeatingBurstRequests(requests, 709 comboCaptureCallback); 710 } else { // SessionConfiguration.SESSION_REGULAR 711 return mSynchronizedCaptureSession.setSingleRepeatingRequest(captureRequest, 712 comboCaptureCallback); 713 } 714 } catch (CameraAccessException e) { 715 Logger.e(TAG, "Unable to access camera: " + e.getMessage()); 716 Thread.dumpStack(); 717 } 718 719 return -1; 720 } 721 } 722 723 /** Issues mCaptureConfigs to {@link CameraCaptureSession}. */ 724 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 725 @GuardedBy("mSessionLock") issuePendingCaptureRequest()726 void issuePendingCaptureRequest() { 727 mRequestMonitor.getRequestsProcessedFuture().addListener(() -> { 728 synchronized (mSessionLock) { 729 if (mCaptureConfigs.isEmpty()) { 730 return; 731 } 732 try { 733 issueBurstCaptureRequest(mCaptureConfigs); 734 } finally { 735 mCaptureConfigs.clear(); 736 } 737 } 738 }, CameraXExecutors.directExecutor()); 739 } 740 741 /** 742 * Issues input CaptureConfig list to {@link CameraCaptureSession}. 743 * 744 * @return A unique capture sequence ID or -1 if request is not submitted. 745 */ 746 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ issueBurstCaptureRequest(List<CaptureConfig> captureConfigs)747 int issueBurstCaptureRequest(List<CaptureConfig> captureConfigs) { 748 synchronized (mSessionLock) { 749 if (mState != State.OPENED) { 750 Logger.d(TAG, "Skipping issueBurstCaptureRequest due to session closed"); 751 return -1; 752 } 753 if (captureConfigs.isEmpty()) { 754 return -1; 755 } 756 try { 757 CameraBurstCaptureCallback callbackAggregator = new CameraBurstCaptureCallback(); 758 List<CaptureRequest> captureRequests = new ArrayList<>(); 759 boolean isStillCapture = false; 760 Logger.d(TAG, "Issuing capture request."); 761 for (CaptureConfig captureConfig : captureConfigs) { 762 if (captureConfig.getSurfaces().isEmpty()) { 763 Logger.d(TAG, "Skipping issuing empty capture request."); 764 continue; 765 } 766 767 // Validate all surfaces belong to configured surfaces map 768 boolean surfacesValid = true; 769 for (DeferrableSurface surface : captureConfig.getSurfaces()) { 770 if (!mConfiguredSurfaceMap.containsKey(surface)) { 771 Logger.d(TAG, 772 "Skipping capture request with invalid surface: " + surface); 773 surfacesValid = false; 774 break; 775 } 776 } 777 778 if (!surfacesValid) { 779 // An invalid surface was detected in this request. 780 // Skip it and go on to the next request. 781 // TODO (b/133710422): Report this request as an error. 782 continue; 783 } 784 785 if (captureConfig.getTemplateType() == CameraDevice.TEMPLATE_STILL_CAPTURE) { 786 isStillCapture = true; 787 } 788 CaptureConfig.Builder captureConfigBuilder = CaptureConfig.Builder.from( 789 captureConfig); 790 791 if (captureConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG 792 && captureConfig.getCameraCaptureResult() != null) { 793 captureConfigBuilder.setCameraCaptureResult( 794 captureConfig.getCameraCaptureResult()); 795 } 796 797 // The override priority for implementation options 798 // P1 Single capture options 799 // P2 SessionConfig options 800 if (mSessionConfig != null) { 801 captureConfigBuilder.addImplementationOptions( 802 mSessionConfig.getRepeatingCaptureConfig() 803 .getImplementationOptions()); 804 } 805 806 // Need to override again since single capture options has highest priority. 807 captureConfigBuilder.addImplementationOptions( 808 captureConfig.getImplementationOptions()); 809 810 CaptureRequest captureRequest = Camera2CaptureRequestBuilder.build( 811 captureConfigBuilder.build(), mSynchronizedCaptureSession.getDevice(), 812 mConfiguredSurfaceMap, false, mTemplateParamsOverride); 813 if (captureRequest == null) { 814 Logger.d(TAG, "Skipping issuing request without surface."); 815 return -1; 816 } 817 818 List<CameraCaptureSession.CaptureCallback> cameraCallbacks = new ArrayList<>(); 819 for (CameraCaptureCallback callback : 820 captureConfig.getCameraCaptureCallbacks()) { 821 CaptureCallbackConverter.toCaptureCallback(callback, cameraCallbacks); 822 } 823 callbackAggregator.addCamera2Callbacks(captureRequest, cameraCallbacks); 824 captureRequests.add(captureRequest); 825 } 826 827 if (!captureRequests.isEmpty()) { 828 if (mStillCaptureFlow 829 .shouldStopRepeatingBeforeCapture(captureRequests, isStillCapture)) { 830 mSynchronizedCaptureSession.stopRepeating(); 831 callbackAggregator.setCaptureSequenceCallback( 832 (session, sequenceId, isAborted) -> { 833 synchronized (mSessionLock) { 834 if (mState == State.OPENED) { 835 issueRepeatingCaptureRequests(mSessionConfig); 836 } 837 } 838 }); 839 } 840 if (mTorchStateReset.isTorchResetRequired(captureRequests, isStillCapture)) { 841 callbackAggregator.addCamera2Callbacks( 842 captureRequests.get(captureRequests.size() - 1), 843 Collections.singletonList(new CaptureCallback() { 844 845 @Override 846 public void onCaptureCompleted( 847 @NonNull CameraCaptureSession session, 848 @NonNull CaptureRequest request, 849 @NonNull TotalCaptureResult result) { 850 synchronized (mSessionLock) { 851 if (mSessionConfig == null) { 852 return; 853 } 854 CaptureConfig repeatingConfig = 855 mSessionConfig.getRepeatingCaptureConfig(); 856 Logger.d(TAG, "Submit FLASH_MODE_OFF request"); 857 issueCaptureRequests(Collections.singletonList( 858 mTorchStateReset.createTorchResetRequest( 859 repeatingConfig))); 860 } 861 } 862 })); 863 } 864 if (mSessionConfig != null && mSessionConfig.getSessionType() 865 == SessionConfiguration.SESSION_HIGH_SPEED) { 866 return captureHighSpeedBurst(captureRequests, callbackAggregator); 867 } else { // SessionConfiguration.SESSION_REGULAR 868 return mSynchronizedCaptureSession.captureBurstRequests(captureRequests, 869 callbackAggregator); 870 } 871 } else { 872 Logger.d(TAG, 873 "Skipping issuing burst request due to no valid request elements"); 874 } 875 } catch (CameraAccessException e) { 876 Logger.e(TAG, "Unable to access camera: " + e.getMessage()); 877 Thread.dumpStack(); 878 } 879 880 return -1; 881 } 882 } 883 884 @GuardedBy("mSessionLock") captureHighSpeedBurst(@onNull List<CaptureRequest> captureRequests, @NonNull CameraBurstCaptureCallback callbackAggregator)885 private int captureHighSpeedBurst(@NonNull List<CaptureRequest> captureRequests, 886 @NonNull CameraBurstCaptureCallback callbackAggregator) 887 throws CameraAccessException { 888 // Create a new CameraBurstCaptureCallback to handle callbacks from high-speed requests. 889 // This is necessary because high-speed capture sessions generate multiple requests for 890 // each original request, and we need to map the callbacks back to the original requests. 891 CameraBurstCaptureCallback highSpeedCallbackAggregator = new CameraBurstCaptureCallback(); 892 893 int sequenceId = -1; 894 895 for (CaptureRequest captureRequest : captureRequests) { 896 List<CaptureRequest> highSpeedRequests = 897 Objects.requireNonNull(mSynchronizedCaptureSession) 898 .createHighSpeedRequestList(captureRequest); 899 900 // For each high-speed request, create a forwarding callback that maps the high-speed 901 // request back to the original request and forwards the callback to the original 902 // callback aggregator. 903 for (CaptureRequest highSpeedRequest : highSpeedRequests) { 904 CaptureCallback forwardingCallback = new RequestForwardingCaptureCallback( 905 captureRequest, callbackAggregator); 906 highSpeedCallbackAggregator.addCamera2Callbacks(highSpeedRequest, 907 Collections.singletonList(forwardingCallback)); 908 } 909 910 sequenceId = mSynchronizedCaptureSession.captureBurstRequests( 911 highSpeedRequests, highSpeedCallbackAggregator); 912 } 913 914 // Return the sequence ID of the last burst capture as a representative ID. 915 return sequenceId; 916 } 917 918 /** 919 * Discards all captures currently pending and in-progress as fast as possible. 920 */ abortCaptures()921 void abortCaptures() { 922 synchronized (mSessionLock) { 923 if (mState != State.OPENED) { 924 Logger.e(TAG, "Unable to abort captures. Incorrect state:" + mState); 925 return; 926 } 927 928 try { 929 mSynchronizedCaptureSession.abortCaptures(); 930 } catch (CameraAccessException e) { 931 Logger.e(TAG, "Unable to abort captures.", e); 932 } 933 } 934 } 935 936 /** 937 * Cancels any ongoing repeating capture. 938 */ stopRepeating()939 void stopRepeating() { 940 synchronized (mSessionLock) { 941 if (mState != State.OPENED) { 942 Logger.e(TAG, "Unable to stop repeating. Incorrect state:" + mState); 943 return; 944 } 945 946 try { 947 mSynchronizedCaptureSession.stopRepeating(); 948 } catch (CameraAccessException e) { 949 Logger.e(TAG, "Unable to stop repeating.", e); 950 } 951 } 952 } 953 954 /** 955 * {@inheritDoc} 956 */ 957 @Override cancelIssuedCaptureRequests()958 public void cancelIssuedCaptureRequests() { 959 List<CaptureConfig> captureConfigs = null; 960 synchronized (mSessionLock) { 961 if (!mCaptureConfigs.isEmpty()) { 962 captureConfigs = new ArrayList<>(mCaptureConfigs); 963 mCaptureConfigs.clear(); 964 } 965 } 966 967 if (captureConfigs != null) { 968 for (CaptureConfig captureConfig : captureConfigs) { 969 for (CameraCaptureCallback cameraCaptureCallback : 970 captureConfig.getCameraCaptureCallbacks()) { 971 cameraCaptureCallback.onCaptureCancelled(captureConfig.getId()); 972 } 973 } 974 } 975 } 976 977 @GuardedBy("mSessionLock") setState(@onNull State state)978 private void setState(@NonNull State state) { 979 if (state.ordinal() > mHighestState.ordinal()) { 980 mHighestState = state; 981 } 982 mState = state; 983 // Some sessions are created and immediately destroyed, so only trace those sessions 984 // that are actually used, which we distinguish by capture sessions that have gone to 985 // at least a GET_SURFACE state. 986 if (Trace.isEnabled() && mHighestState.ordinal() >= State.GET_SURFACE.ordinal()) { 987 String counterName = "CX:C2State[" + String.format("CaptureSession@%x", hashCode()) 988 + "]"; 989 Trace.setCounter(counterName, state.ordinal()); 990 } 991 } 992 993 @GuardedBy("mSessionLock") createCamera2CaptureCallback( List<CameraCaptureCallback> cameraCaptureCallbacks, CameraCaptureSession.CaptureCallback... additionalCallbacks)994 private CameraCaptureSession.CaptureCallback createCamera2CaptureCallback( 995 List<CameraCaptureCallback> cameraCaptureCallbacks, 996 CameraCaptureSession.CaptureCallback... additionalCallbacks) { 997 List<CameraCaptureSession.CaptureCallback> camera2Callbacks = 998 new ArrayList<>(cameraCaptureCallbacks.size() + additionalCallbacks.length); 999 for (CameraCaptureCallback callback : cameraCaptureCallbacks) { 1000 camera2Callbacks.add(CaptureCallbackConverter.toCaptureCallback(callback)); 1001 } 1002 Collections.addAll(camera2Callbacks, additionalCallbacks); 1003 return Camera2CaptureCallbacks.createComboCallback(camera2Callbacks); 1004 } 1005 1006 /** 1007 * Returns the map which contains the data by mapping surface group id to OutputConfig list. 1008 */ groupMrirOutputConfigs( @onNull Collection<SessionConfig.OutputConfig> outputConfigs)1009 private static @NonNull Map<Integer, List<SessionConfig.OutputConfig>> groupMrirOutputConfigs( 1010 @NonNull Collection<SessionConfig.OutputConfig> outputConfigs) { 1011 Map<Integer, List<SessionConfig.OutputConfig>> groupResult = new HashMap<>(); 1012 1013 for (SessionConfig.OutputConfig outputConfig : outputConfigs) { 1014 // When shared surfaces is not empty, surface sharing will be enabled on the 1015 // OutputConfiguration. In that case, MultiResolutionImageReader shouldn't be used. 1016 if (outputConfig.getSurfaceGroupId() <= 0 1017 || !outputConfig.getSharedSurfaces().isEmpty()) { 1018 continue; 1019 } 1020 List<SessionConfig.OutputConfig> groupedOutputConfigs = groupResult.get( 1021 outputConfig.getSurfaceGroupId()); 1022 if (groupedOutputConfigs == null) { 1023 groupedOutputConfigs = new ArrayList<>(); 1024 groupResult.put(outputConfig.getSurfaceGroupId(), groupedOutputConfigs); 1025 } 1026 groupedOutputConfigs.add(outputConfig); 1027 } 1028 1029 // Double-check that the list size of each group is at least 2. It is the necessary 1030 // condition to create a MRIR. 1031 Map<Integer, List<SessionConfig.OutputConfig>> mrirGroupResult = new HashMap<>(); 1032 for (int groupId : groupResult.keySet()) { 1033 if (groupResult.get(groupId).size() >= 2) { 1034 mrirGroupResult.put(groupId, groupResult.get(groupId)); 1035 } 1036 } 1037 1038 return mrirGroupResult; 1039 } 1040 1041 @RequiresApi(35) 1042 private static @NonNull Map<SessionConfig.OutputConfig, OutputConfigurationCompat> createMultiResolutionOutputConfigurationCompats( @onNull Map<Integer, List<SessionConfig.OutputConfig>> groupIdToOutputConfigsMap, @NonNull Map<DeferrableSurface, Surface> configuredSurfaceMap)1043 createMultiResolutionOutputConfigurationCompats( 1044 @NonNull Map<Integer, List<SessionConfig.OutputConfig>> groupIdToOutputConfigsMap, 1045 @NonNull Map<DeferrableSurface, Surface> configuredSurfaceMap) { 1046 Map<SessionConfig.OutputConfig, OutputConfigurationCompat> 1047 outputConfigToOutputConfigurationCompatMap = new HashMap<>(); 1048 1049 for (int groupId : groupIdToOutputConfigsMap.keySet()) { 1050 List<MultiResolutionStreamInfo> streamInfos = new ArrayList<>(); 1051 int imageFormat = ImageFormat.UNKNOWN; 1052 for (SessionConfig.OutputConfig outputConfig : groupIdToOutputConfigsMap.get(groupId)) { 1053 Surface surface = configuredSurfaceMap.get(outputConfig.getSurface()); 1054 SurfaceUtil.SurfaceInfo surfaceInfo = SurfaceUtil.getSurfaceInfo(surface); 1055 if (imageFormat == ImageFormat.UNKNOWN) { 1056 imageFormat = surfaceInfo.format; 1057 } 1058 streamInfos.add(new MultiResolutionStreamInfo(surfaceInfo.width, surfaceInfo.height, 1059 Objects.requireNonNull(outputConfig.getPhysicalCameraId()))); 1060 } 1061 if (imageFormat == ImageFormat.UNKNOWN || streamInfos.isEmpty()) { 1062 Logger.e(TAG, "Skips to create instances for multi-resolution output. imageFormat: " 1063 + imageFormat + ", streamInfos size: " + streamInfos.size()); 1064 continue; 1065 } 1066 List<OutputConfiguration> outputConfigurations = 1067 OutputConfiguration.createInstancesForMultiResolutionOutput(streamInfos, 1068 imageFormat); 1069 if (outputConfigurations != null) { 1070 for (SessionConfig.OutputConfig outputConfig : groupIdToOutputConfigsMap.get( 1071 groupId)) { 1072 OutputConfiguration outputConfiguration = outputConfigurations.remove(0); 1073 Surface surface = configuredSurfaceMap.get(outputConfig.getSurface()); 1074 outputConfiguration.addSurface(surface); 1075 outputConfigToOutputConfigurationCompatMap.put(outputConfig, 1076 new OutputConfigurationCompat(outputConfiguration)); 1077 } 1078 } 1079 } 1080 return outputConfigToOutputConfigurationCompatMap; 1081 } 1082 1083 // Debugging note: these states are kept in ordinal order. Any additions or changes should try 1084 // to maintain the same order such that the highest ordinal is the state of largest resource 1085 // utilization. 1086 enum State { 1087 /** The default state of the session before construction. */ 1088 UNINITIALIZED, 1089 /** 1090 * Terminal state where the session has been cleaned up. At this point the session should 1091 * not be used as nothing will happen in this state. 1092 */ 1093 RELEASED, 1094 /** 1095 * Stable state once the session has been constructed, but prior to the {@link 1096 * CameraCaptureSession} being opened. 1097 */ 1098 INITIALIZED, 1099 /** 1100 * Transitional state to get the configured surface from the configuration. Once the 1101 * surfaces is ready, we can create the {@link CameraCaptureSession}. 1102 */ 1103 GET_SURFACE, 1104 /** Transitional state where the resources are being cleaned up. */ 1105 RELEASING, 1106 /** 1107 * Stable state where the session has been closed. However the {@link CameraCaptureSession} 1108 * is still valid. It will remain valid until a new instance is opened at which point {@link 1109 * CameraCaptureSession.StateCallback#onClosed(CameraCaptureSession)} will be called to do 1110 * final cleanup. 1111 */ 1112 CLOSED, 1113 /** 1114 * Transitional state when the {@link CameraCaptureSession} is in the process of being 1115 * opened. 1116 */ 1117 OPENING, 1118 /** 1119 * Stable state where the {@link CameraCaptureSession} has been successfully opened. During 1120 * this state if a valid {@link SessionConfig} has been set then the {@link 1121 * CaptureRequest} will be issued. 1122 */ 1123 OPENED 1124 } 1125 1126 /** 1127 * Callback for handling state changes to the {@link CameraCaptureSession}. 1128 * 1129 * <p>State changes are ignored once the CaptureSession has been closed. 1130 */ 1131 final class StateCallback extends SynchronizedCaptureSession.StateCallback { 1132 1133 /** 1134 * {@inheritDoc} 1135 * 1136 * <p>Once the {@link CameraCaptureSession} has been configured then the capture request 1137 * will be immediately issued. 1138 */ 1139 @Override onConfigured(@onNull SynchronizedCaptureSession session)1140 public void onConfigured(@NonNull SynchronizedCaptureSession session) { 1141 synchronized (mSessionLock) { 1142 switch (mState) { 1143 case UNINITIALIZED: 1144 case INITIALIZED: 1145 case GET_SURFACE: 1146 case OPENED: 1147 case RELEASED: 1148 throw new IllegalStateException( 1149 "onConfigured() should not be possible in state: " + mState); 1150 case OPENING: 1151 setState(State.OPENED); 1152 mSynchronizedCaptureSession = session; 1153 Logger.d(TAG, "Attempting to send capture request onConfigured"); 1154 issueRepeatingCaptureRequests(mSessionConfig); 1155 issuePendingCaptureRequest(); 1156 break; 1157 case CLOSED: 1158 mSynchronizedCaptureSession = session; 1159 break; 1160 case RELEASING: 1161 session.close(); 1162 break; 1163 } 1164 Logger.d(TAG, "CameraCaptureSession.onConfigured() mState=" + mState); 1165 } 1166 } 1167 1168 @Override onReady(@onNull SynchronizedCaptureSession session)1169 public void onReady(@NonNull SynchronizedCaptureSession session) { 1170 synchronized (mSessionLock) { 1171 switch (mState) { 1172 case UNINITIALIZED: 1173 throw new IllegalStateException( 1174 "onReady() should not be possible in state: " + mState); 1175 default: 1176 } 1177 Logger.d(TAG, "CameraCaptureSession.onReady() " + mState); 1178 } 1179 } 1180 1181 @Override onSessionFinished(@onNull SynchronizedCaptureSession session)1182 public void onSessionFinished(@NonNull SynchronizedCaptureSession session) { 1183 synchronized (mSessionLock) { 1184 if (mState == State.UNINITIALIZED) { 1185 throw new IllegalStateException( 1186 "onSessionFinished() should not be possible in state: " + mState); 1187 } 1188 Logger.d(TAG, "onSessionFinished()"); 1189 1190 finishClose(); 1191 } 1192 } 1193 1194 @Override onConfigureFailed(@onNull SynchronizedCaptureSession session)1195 public void onConfigureFailed(@NonNull SynchronizedCaptureSession session) { 1196 synchronized (mSessionLock) { 1197 switch (mState) { 1198 case UNINITIALIZED: 1199 case INITIALIZED: 1200 case GET_SURFACE: 1201 case OPENED: 1202 throw new IllegalStateException( 1203 "onConfigureFailed() should not be possible in state: " + mState); 1204 case OPENING: 1205 case CLOSED: 1206 case RELEASING: 1207 // For CaptureSession onConfigureFailed in framework, it will not allow 1208 // any close function or callback work. Calling session.close() will not 1209 // trigger StateCallback.onClosed(). It has to complete the close flow 1210 // internally. Check b/147402661 for detail. 1211 finishClose(); 1212 break; 1213 case RELEASED: 1214 Logger.d(TAG, "ConfigureFailed callback after change to RELEASED state"); 1215 break; 1216 } 1217 Logger.e(TAG, "CameraCaptureSession.onConfigureFailed() " + mState); 1218 } 1219 } 1220 } 1221 } 1222