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 static android.graphics.ImageFormat.JPEG; 20 import static android.graphics.ImageFormat.PRIVATE; 21 import static android.graphics.ImageFormat.YUV_420_888; 22 23 import static androidx.camera.camera2.internal.Camera2CameraImplTest.TestUseCase.SurfaceOption; 24 import static androidx.camera.camera2.internal.Camera2CameraImplTest.TestUseCase.SurfaceOption.NON_REPEATING; 25 import static androidx.camera.camera2.internal.Camera2CameraImplTest.TestUseCase.SurfaceOption.REPEATING; 26 import static androidx.camera.camera2.internal.compat.quirk.CaptureIntentPreviewQuirk.workaroundByCaptureIntentPreview; 27 import static androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA; 28 import static androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT; 29 import static androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE; 30 import static androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE; 31 32 import static com.google.common.base.Preconditions.checkNotNull; 33 import static com.google.common.base.Preconditions.checkState; 34 import static com.google.common.truth.Truth.assertThat; 35 36 import static junit.framework.TestCase.assertTrue; 37 38 import static org.junit.Assume.assumeTrue; 39 import static org.mockito.ArgumentMatchers.any; 40 import static org.mockito.ArgumentMatchers.anyInt; 41 import static org.mockito.Mockito.mock; 42 import static org.mockito.Mockito.never; 43 import static org.mockito.Mockito.spy; 44 import static org.mockito.Mockito.timeout; 45 import static org.mockito.Mockito.verify; 46 import static org.mockito.internal.verification.VerificationModeFactory.times; 47 48 import static java.util.Arrays.asList; 49 import static java.util.Collections.singletonList; 50 51 import android.content.Context; 52 import android.content.pm.PackageManager; 53 import android.graphics.SurfaceTexture; 54 import android.hardware.camera2.CameraDevice; 55 import android.hardware.camera2.CaptureRequest; 56 import android.hardware.camera2.CaptureResult; 57 import android.media.Image; 58 import android.media.ImageReader; 59 import android.os.Handler; 60 import android.os.HandlerThread; 61 import android.os.Looper; 62 import android.util.Range; 63 import android.util.Size; 64 import android.view.Surface; 65 66 import androidx.camera.camera2.Camera2Config; 67 import androidx.camera.camera2.internal.compat.CameraManagerCompat; 68 import androidx.camera.camera2.internal.util.SemaphoreReleasingCamera2Callbacks; 69 import androidx.camera.camera2.interop.Camera2Interop; 70 import androidx.camera.core.Camera; 71 import androidx.camera.core.CameraControl; 72 import androidx.camera.core.CameraSelector; 73 import androidx.camera.core.CompositionSettings; 74 import androidx.camera.core.DynamicRange; 75 import androidx.camera.core.ImageCapture; 76 import androidx.camera.core.MirrorMode; 77 import androidx.camera.core.Preview; 78 import androidx.camera.core.UseCase; 79 import androidx.camera.core.impl.CameraCaptureCallback; 80 import androidx.camera.core.impl.CameraCaptureResult; 81 import androidx.camera.core.impl.CameraInternal; 82 import androidx.camera.core.impl.CameraStateRegistry; 83 import androidx.camera.core.impl.CaptureConfig; 84 import androidx.camera.core.impl.DeferrableSurface; 85 import androidx.camera.core.impl.ImmediateSurface; 86 import androidx.camera.core.impl.Observable; 87 import androidx.camera.core.impl.SessionConfig; 88 import androidx.camera.core.impl.StreamSpec; 89 import androidx.camera.core.impl.UseCaseConfig; 90 import androidx.camera.core.impl.UseCaseConfigFactory; 91 import androidx.camera.core.impl.utils.executor.CameraXExecutors; 92 import androidx.camera.core.resolutionselector.ResolutionSelector; 93 import androidx.camera.core.streamsharing.StreamSharing; 94 import androidx.camera.testing.impl.CameraUtil; 95 import androidx.camera.testing.impl.HandlerUtil; 96 import androidx.camera.testing.impl.fakes.FakeCameraCoordinator; 97 import androidx.camera.testing.impl.fakes.FakeUseCase; 98 import androidx.camera.testing.impl.fakes.FakeUseCaseConfig; 99 import androidx.camera.testing.impl.mocks.MockObserver; 100 import androidx.camera.testing.impl.mocks.helpers.CallTimes; 101 import androidx.camera.testing.impl.mocks.helpers.CallTimesAtLeast; 102 import androidx.camera.video.Recorder; 103 import androidx.camera.video.VideoCapture; 104 import androidx.core.os.HandlerCompat; 105 import androidx.test.core.app.ApplicationProvider; 106 import androidx.test.ext.junit.runners.AndroidJUnit4; 107 import androidx.test.filters.LargeTest; 108 import androidx.test.filters.SdkSuppress; 109 import androidx.test.platform.app.InstrumentationRegistry; 110 111 import com.google.common.util.concurrent.ListenableFuture; 112 113 import org.jspecify.annotations.NonNull; 114 import org.jspecify.annotations.Nullable; 115 import org.junit.After; 116 import org.junit.AfterClass; 117 import org.junit.Before; 118 import org.junit.BeforeClass; 119 import org.junit.Rule; 120 import org.junit.Test; 121 import org.junit.rules.TestRule; 122 import org.junit.runner.RunWith; 123 import org.mockito.ArgumentCaptor; 124 import org.mockito.Mockito; 125 126 import java.util.ArrayList; 127 import java.util.HashMap; 128 import java.util.HashSet; 129 import java.util.List; 130 import java.util.Map; 131 import java.util.Set; 132 import java.util.concurrent.ExecutionException; 133 import java.util.concurrent.ExecutorService; 134 import java.util.concurrent.Semaphore; 135 import java.util.concurrent.TimeUnit; 136 import java.util.concurrent.TimeoutException; 137 import java.util.concurrent.atomic.AtomicReference; 138 139 /** 140 * Contains tests for {@link androidx.camera.camera2.internal.Camera2CameraImpl} internal 141 * implementation. 142 */ 143 @LargeTest 144 @RunWith(AndroidJUnit4.class) 145 @SdkSuppress(minSdkVersion = 21) 146 public final class Camera2CameraImplTest { 147 @CameraSelector.LensFacing 148 private static final int DEFAULT_LENS_FACING = CameraSelector.LENS_FACING_BACK; 149 @CameraSelector.LensFacing 150 private static final int DEFAULT_PAIRED_CAMERA_LENS_FACING = CameraSelector.LENS_FACING_FRONT; 151 // For the purpose of this test, always say we have 1 camera available. 152 private static final int DEFAULT_AVAILABLE_CAMERA_COUNT = 1; 153 private static final int DEFAULT_TEMPLATE_TYPE = CameraDevice.TEMPLATE_PREVIEW; 154 private static final Map<Integer, Boolean> DEFAULT_TEMPLATE_TO_ZSL_DISABLED = new HashMap<>(); 155 156 static { DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_PREVIEW, false)157 DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_PREVIEW, false); DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_STILL_CAPTURE, true)158 DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_STILL_CAPTURE, true); DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_RECORD, true)159 DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_RECORD, true); DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, false)160 DEFAULT_TEMPLATE_TO_ZSL_DISABLED.put(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, false); 161 } 162 163 private static final SurfaceOption DEFAULT_SURFACE_OPTION = REPEATING; 164 private static final int DEFAULT_IMAGE_FORMAT = PRIVATE; 165 166 private static final Set<CameraInternal.State> STABLE_STATES = new HashSet<>(asList( 167 CameraInternal.State.CLOSED, 168 CameraInternal.State.OPEN, 169 CameraInternal.State.RELEASED)); 170 171 static ExecutorService sCameraExecutor; 172 173 @Rule 174 public TestRule mCameraRule = CameraUtil.grantCameraPermissionAndPreTestAndPostTest( 175 new CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig()) 176 ); 177 178 private final ArrayList<UseCase> mFakeUseCases = new ArrayList<>(); 179 private Camera2CameraImpl mCamera2CameraImpl; 180 private static HandlerThread sCameraHandlerThread; 181 private static Handler sCameraHandler; 182 private FakeCameraCoordinator mCameraCoordinator; 183 private CameraStateRegistry mCameraStateRegistry; 184 Semaphore mSemaphore; 185 CameraCaptureCallback mMockRepeatingCaptureCallback; 186 String mCameraId; 187 String mPairedCameraId; 188 SemaphoreReleasingCamera2Callbacks.SessionStateCallback mSessionStateCallback; 189 190 @BeforeClass classSetup()191 public static void classSetup() { 192 sCameraHandlerThread = new HandlerThread("cameraThread"); 193 sCameraHandlerThread.start(); 194 sCameraHandler = HandlerCompat.createAsync(sCameraHandlerThread.getLooper()); 195 sCameraExecutor = CameraXExecutors.newHandlerExecutor(sCameraHandler); 196 } 197 198 @AfterClass classTeardown()199 public static void classTeardown() { 200 sCameraHandlerThread.quitSafely(); 201 } 202 203 @Before setup()204 public void setup() throws Exception { 205 mMockRepeatingCaptureCallback = Mockito.mock(CameraCaptureCallback.class); 206 mSessionStateCallback = new SemaphoreReleasingCamera2Callbacks.SessionStateCallback(); 207 mCameraId = CameraUtil.getCameraIdWithLensFacing(DEFAULT_LENS_FACING); 208 mPairedCameraId = CameraUtil.getCameraIdWithLensFacing(DEFAULT_PAIRED_CAMERA_LENS_FACING); 209 mSemaphore = new Semaphore(0); 210 mCameraCoordinator = new FakeCameraCoordinator(); 211 mCameraStateRegistry = new CameraStateRegistry(mCameraCoordinator, 212 DEFAULT_AVAILABLE_CAMERA_COUNT); 213 CameraManagerCompat cameraManagerCompat = 214 CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext()); 215 216 Camera2CameraInfoImpl camera2CameraInfo = new Camera2CameraInfoImpl( 217 mCameraId, cameraManagerCompat); 218 mCamera2CameraImpl = new Camera2CameraImpl( 219 (Context) ApplicationProvider.getApplicationContext(), 220 cameraManagerCompat, mCameraId, camera2CameraInfo, mCameraCoordinator, 221 mCameraStateRegistry, sCameraExecutor, sCameraHandler, 222 DisplayInfoManager.getInstance(ApplicationProvider.getApplicationContext()), 223 -1L 224 ); 225 } 226 227 @After teardown()228 public void teardown() throws InterruptedException, ExecutionException { 229 for (UseCase fakeUseCase : mFakeUseCases) { 230 InstrumentationRegistry.getInstrumentation().runOnMainSync( 231 () -> fakeUseCase.unbindFromCamera(mCamera2CameraImpl)); 232 } 233 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 234 235 // Need to release the camera no matter what is done, otherwise the CameraDevice is not 236 // closed. 237 // When the CameraDevice is not closed, then it can cause problems with interferes with 238 // other test cases. 239 if (mCamera2CameraImpl != null) { 240 ListenableFuture<Void> cameraReleaseFuture = mCamera2CameraImpl.release(); 241 242 // Wait for camera to be fully closed 243 cameraReleaseFuture.get(); 244 245 mCamera2CameraImpl = null; 246 } 247 } 248 249 @Test attachUseCase()250 public void attachUseCase() { 251 mCamera2CameraImpl.open(); 252 253 UseCase useCase = createUseCase(); 254 mCamera2CameraImpl.attachUseCases(singletonList(useCase)); 255 256 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 257 258 mCamera2CameraImpl.detachUseCases(singletonList(useCase)); 259 mCamera2CameraImpl.release(); 260 } 261 262 @Test activeUseCase()263 public void activeUseCase() { 264 mCamera2CameraImpl.open(); 265 mCamera2CameraImpl.onUseCaseActive(createUseCase()); 266 267 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 268 269 mCamera2CameraImpl.release(); 270 } 271 272 @Test attachAndActiveUseCase()273 public void attachAndActiveUseCase() { 274 UseCase useCase1 = createUseCase(); 275 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 276 mCamera2CameraImpl.onUseCaseActive(useCase1); 277 278 verify(mMockRepeatingCaptureCallback, timeout(4000).atLeastOnce()) 279 .onCaptureCompleted(anyInt(), any()); 280 281 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 282 } 283 284 @Test detachUseCase()285 public void detachUseCase() { 286 UseCase useCase1 = createUseCase(); 287 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 288 289 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 290 mCamera2CameraImpl.onUseCaseActive(useCase1); 291 292 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 293 294 assertThat(mCamera2CameraImpl.getCameraControlInternal() 295 .isZslDisabledByByUserCaseConfig()).isFalse(); 296 } 297 298 @Test unopenedCamera()299 public void unopenedCamera() { 300 UseCase useCase1 = createUseCase(); 301 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 302 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 303 304 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 305 } 306 307 @Test closedCamera()308 public void closedCamera() { 309 UseCase useCase1 = createUseCase(); 310 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 311 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 312 313 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 314 } 315 316 @Test releaseUnopenedCamera()317 public void releaseUnopenedCamera() { 318 UseCase useCase1 = createUseCase(); 319 // Checks that if a camera has been released then calling open() will no longer open it. 320 mCamera2CameraImpl.release(); 321 mCamera2CameraImpl.open(); 322 323 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 324 mCamera2CameraImpl.onUseCaseActive(useCase1); 325 326 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 327 328 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 329 } 330 331 @Test releasedOpenedCamera()332 public void releasedOpenedCamera() { 333 UseCase useCase1 = createUseCase(); 334 mCamera2CameraImpl.open(); 335 mCamera2CameraImpl.release(); 336 337 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 338 mCamera2CameraImpl.onUseCaseActive(useCase1); 339 340 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), any()); 341 342 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 343 } 344 345 @Test attach_oneUseCase_isAttached()346 public void attach_oneUseCase_isAttached() { 347 UseCase useCase1 = createUseCase(); 348 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 349 350 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isTrue(); 351 352 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 353 } 354 355 @Test attach_sameUseCases_staysAttached()356 public void attach_sameUseCases_staysAttached() { 357 UseCase useCase1 = createUseCase(); 358 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 359 boolean attachedAfterFirstAdd = mCamera2CameraImpl.isUseCaseAttached(useCase1); 360 361 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 362 363 assertThat(attachedAfterFirstAdd).isTrue(); 364 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isTrue(); 365 366 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 367 } 368 369 @Test attach_twoUseCases_bothBecomeAttached()370 public void attach_twoUseCases_bothBecomeAttached() { 371 UseCase useCase1 = createUseCase(); 372 UseCase useCase2 = createUseCase(); 373 mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2)); 374 375 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isTrue(); 376 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase2)).isTrue(); 377 378 mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2)); 379 } 380 381 @Test detach_detachedUseCase_staysDetached()382 public void detach_detachedUseCase_staysDetached() { 383 UseCase useCase1 = createUseCase(); 384 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 385 386 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isFalse(); 387 } 388 389 @Test detachOneAttachedUseCase_fromAttachedUseCases_onlyDetachedSingleUseCase()390 public void detachOneAttachedUseCase_fromAttachedUseCases_onlyDetachedSingleUseCase() { 391 UseCase useCase1 = createUseCase(); 392 UseCase useCase2 = createUseCase(); 393 mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2)); 394 395 boolean useCase1isAttachedAfterFirstAdd = mCamera2CameraImpl.isUseCaseAttached(useCase1); 396 boolean useCase2isAttachedAfterFirstAdd = mCamera2CameraImpl.isUseCaseAttached(useCase2); 397 398 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 399 400 assertThat(useCase1isAttachedAfterFirstAdd).isTrue(); 401 assertThat(useCase2isAttachedAfterFirstAdd).isTrue(); 402 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isFalse(); 403 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase2)).isTrue(); 404 405 mCamera2CameraImpl.detachUseCases(singletonList(useCase2)); 406 } 407 408 @Test detachSameAttachedUseCaseTwice_onlyDetachesSameUseCase()409 public void detachSameAttachedUseCaseTwice_onlyDetachesSameUseCase() { 410 UseCase useCase1 = createUseCase(); 411 UseCase useCase2 = createUseCase(); 412 mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2)); 413 414 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 415 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 416 417 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase1)).isFalse(); 418 assertThat(mCamera2CameraImpl.isUseCaseAttached(useCase2)).isTrue(); 419 420 mCamera2CameraImpl.detachUseCases(singletonList(useCase2)); 421 } 422 423 @Test attachUseCase_changeSurface_onUseCaseReset_correctAttachCount()424 public void attachUseCase_changeSurface_onUseCaseReset_correctAttachCount() 425 throws ExecutionException, InterruptedException { 426 blockHandler(); 427 428 UseCase useCase1 = createUseCase(); 429 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 430 DeferrableSurface surface1 = useCase1.getSessionConfig().getSurfaces().get(0); 431 432 unblockHandler(); 433 HandlerUtil.waitForLooperToIdle(sCameraHandler); 434 435 changeUseCaseSurface(useCase1); 436 mCamera2CameraImpl.onUseCaseReset(useCase1); 437 DeferrableSurface surface2 = useCase1.getSessionConfig().getSurfaces().get(0); 438 439 // Wait for camera to be released to ensure it has finished closing 440 ListenableFuture<Void> releaseFuture = mCamera2CameraImpl.release(); 441 releaseFuture.get(); 442 443 assertThat(surface1).isNotEqualTo(surface2); 444 445 // Old surface is decremented when CameraCaptureSession is closed by new 446 // CameraCaptureSession. 447 assertThat(surface1.getUseCount()).isEqualTo(0); 448 // New surface is decremented when CameraCaptureSession is closed by 449 // mCamera2CameraImpl.release() 450 assertThat(surface2.getUseCount()).isEqualTo(0); 451 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 452 } 453 454 @Test pendingSingleRequestRunSuccessfully_whenAnotherUseCaseAttached()455 public void pendingSingleRequestRunSuccessfully_whenAnotherUseCaseAttached() 456 throws InterruptedException { 457 458 // Block camera thread to queue all the camera operations. 459 blockHandler(); 460 461 UseCase useCase1 = createUseCase(); 462 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 463 464 CameraCaptureCallback captureCallback = mock(CameraCaptureCallback.class); 465 CaptureConfig.Builder captureConfigBuilder = new CaptureConfig.Builder(); 466 captureConfigBuilder.setTemplateType(CameraDevice.TEMPLATE_PREVIEW); 467 captureConfigBuilder.addSurface(useCase1.getSessionConfig().getSurfaces().get(0)); 468 captureConfigBuilder.addCameraCaptureCallback(captureCallback); 469 470 mCamera2CameraImpl.getCameraControlInternal().submitStillCaptureRequests( 471 singletonList(captureConfigBuilder.build()), 472 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY, 473 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH); 474 475 UseCase useCase2 = createUseCase(); 476 mCamera2CameraImpl.attachUseCases(singletonList(useCase2)); 477 478 // Unblock camera handler to make camera operation run quickly . 479 // To make the single request not able to run in 1st capture session. and verify if it can 480 // be carried over to the new capture session and run successfully. 481 unblockHandler(); 482 HandlerUtil.waitForLooperToIdle(sCameraHandler); 483 484 // CameraCaptureCallback.onCaptureCompleted() should be called to signal a capture attempt. 485 verify(captureCallback, timeout(3000).times(1)) 486 .onCaptureCompleted(anyInt(), any(CameraCaptureResult.class)); 487 488 mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2)); 489 } 490 491 @Test pendingSingleRequestSkipped_whenTheUseCaseIsRemoved()492 public void pendingSingleRequestSkipped_whenTheUseCaseIsRemoved() 493 throws InterruptedException { 494 495 // Block camera thread to queue all the camera operations. 496 blockHandler(); 497 498 UseCase useCase1 = createUseCase(); 499 UseCase useCase2 = createUseCase(); 500 501 mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2)); 502 503 CameraCaptureCallback captureCallback = mock(CameraCaptureCallback.class); 504 CaptureConfig.Builder captureConfigBuilder = new CaptureConfig.Builder(); 505 captureConfigBuilder.setTemplateType(DEFAULT_TEMPLATE_TYPE); 506 captureConfigBuilder.addSurface(useCase1.getSessionConfig().getSurfaces().get(0)); 507 captureConfigBuilder.addCameraCaptureCallback(captureCallback); 508 509 mCamera2CameraImpl.getCameraControlInternal().submitStillCaptureRequests( 510 singletonList(captureConfigBuilder.build()), 511 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY, 512 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH); 513 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 514 515 // Unblock camera handle to make camera operation run quickly . 516 // To make the single request not able to run in 1st capture session. and verify if it can 517 // be carried to the new capture session and run successfully. 518 unblockHandler(); 519 HandlerUtil.waitForLooperToIdle(sCameraHandler); 520 521 // TODO: b/133710422 should provide a way to detect if request is cancelled. 522 Thread.sleep(1000); 523 524 // CameraCaptureCallback.onCaptureCompleted() is not called and there is no crash. 525 verify(captureCallback, times(0)) 526 .onCaptureCompleted(anyInt(), any(CameraCaptureResult.class)); 527 528 mCamera2CameraImpl.detachUseCases(singletonList(useCase2)); 529 } 530 531 @Test attachRepeatingUseCase_meteringRepeatingIsNotAttached()532 public void attachRepeatingUseCase_meteringRepeatingIsNotAttached() { 533 UseCase repeating = createUseCase(REPEATING); 534 535 mCamera2CameraImpl.attachUseCases(singletonList(repeating)); 536 537 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 538 539 mCamera2CameraImpl.detachUseCases(singletonList(repeating)); 540 } 541 542 @Test attachNonRepeatingUseCase_meteringRepeatingIsAttached()543 public void attachNonRepeatingUseCase_meteringRepeatingIsAttached() { 544 UseCase nonRepeating = createUseCase(NON_REPEATING); 545 546 mCamera2CameraImpl.attachUseCases(singletonList(nonRepeating)); 547 548 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 549 550 mCamera2CameraImpl.detachUseCases(singletonList(nonRepeating)); 551 } 552 553 @Test attachNonRepeatingUseCase_whenCameraModeConcurrent_meteringRepeatingIsAttached()554 public void attachNonRepeatingUseCase_whenCameraModeConcurrent_meteringRepeatingIsAttached() { 555 PackageManager pm = ApplicationProvider.getApplicationContext().getPackageManager(); 556 assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT)); 557 558 mCameraCoordinator.setCameraOperatingMode(CAMERA_OPERATING_MODE_CONCURRENT); 559 UseCase nonRepeating = createUseCase(NON_REPEATING); 560 561 mCamera2CameraImpl.attachUseCases(singletonList(nonRepeating)); 562 563 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 564 565 mCamera2CameraImpl.detachUseCases(singletonList(nonRepeating)); 566 } 567 568 @Test attachImageCapture_meteringRepeatingIsAttached()569 public void attachImageCapture_meteringRepeatingIsAttached() { 570 // attaching ImageCapture will allow StreamUseCase to be enabled in supported devices 571 UseCase imageCapture = createImageCapture(); 572 573 mCamera2CameraImpl.attachUseCases(singletonList(imageCapture)); 574 575 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 576 577 InstrumentationRegistry.getInstrumentation().runOnMainSync( 578 () -> mCamera2CameraImpl.detachUseCases(singletonList(imageCapture))); 579 } 580 581 @Test attachStreamSharingWithNonRepeatingChildren_meteringRepeatingIsNotAttached()582 public void attachStreamSharingWithNonRepeatingChildren_meteringRepeatingIsNotAttached() { 583 // StreamSharing use case adds a repeating surface by default, no need for MeteringRepeating 584 Set<UseCase> useCases = new HashSet<>(); 585 586 Preview preview = new Preview.Builder().build(); 587 // No repeating surface is added for Preview when surface provider is not set 588 InstrumentationRegistry.getInstrumentation().runOnMainSync( 589 () -> preview.setSurfaceProvider(null)); 590 useCases.add(preview); 591 592 useCases.add(new VideoCapture.Builder<>(new Recorder.Builder().build()).build()); 593 594 StreamSharing streamSharing = createStreamSharingUseCase(useCases); 595 596 InstrumentationRegistry.getInstrumentation().runOnMainSync( 597 () -> mCamera2CameraImpl.attachUseCases(singletonList(streamSharing))); 598 599 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 600 601 InstrumentationRegistry.getInstrumentation().runOnMainSync( 602 () -> mCamera2CameraImpl.detachUseCases(singletonList(streamSharing))); 603 } 604 605 @Test exceedSupportedUseCaseCount_meteringRepeatingIsNotAttached()606 public void exceedSupportedUseCaseCount_meteringRepeatingIsNotAttached() { 607 // adding a lot of use cases will ensure surface combination is no longer supported 608 List<UseCase> useCases = new ArrayList<>(); 609 for (int i = 0; i < 5; i++) { 610 useCases.add(createUseCase(NON_REPEATING)); 611 } 612 mCamera2CameraImpl.attachUseCases(useCases); 613 614 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 615 616 mCamera2CameraImpl.detachUseCases(useCases); 617 } 618 619 @Test exceedSupportedUseCaseCountLater_meteringRepeatingIsRemoved()620 public void exceedSupportedUseCaseCountLater_meteringRepeatingIsRemoved() { 621 UseCase imageCapture = createImageCapture(); 622 mCamera2CameraImpl.attachUseCases(singletonList(imageCapture)); 623 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 624 625 // adding a lot of use cases will ensure surface combination is no longer supported 626 List<UseCase> useCases = new ArrayList<>(); 627 for (int i = 0; i < 5; i++) { 628 useCases.add(createUseCase(NON_REPEATING)); 629 } 630 mCamera2CameraImpl.attachUseCases(useCases); 631 632 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 633 634 InstrumentationRegistry.getInstrumentation().runOnMainSync( 635 () -> mCamera2CameraImpl.detachUseCases(singletonList(imageCapture))); 636 mCamera2CameraImpl.detachUseCases(useCases); 637 } 638 639 @Test attachRepeatingUseCaseLater_meteringRepeatingIsRemoved()640 public void attachRepeatingUseCaseLater_meteringRepeatingIsRemoved() { 641 UseCase nonRepeating = createUseCase(NON_REPEATING); 642 UseCase repeating = createUseCase(REPEATING); 643 644 mCamera2CameraImpl.attachUseCases(singletonList(nonRepeating)); 645 646 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 647 648 mCamera2CameraImpl.attachUseCases(singletonList(repeating)); 649 650 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 651 652 mCamera2CameraImpl.detachUseCases(asList(nonRepeating, repeating)); 653 } 654 655 @Test attachStreamSharingUseCaseLater_meteringRepeatingIsRemoved()656 public void attachStreamSharingUseCaseLater_meteringRepeatingIsRemoved() { 657 UseCase nonRepeating = createUseCase(NON_REPEATING); 658 659 mCamera2CameraImpl.attachUseCases(singletonList(nonRepeating)); 660 661 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 662 663 // StreamSharing use case adds a repeating surface by default, no need for MeteringRepeating 664 Set<UseCase> useCases = new HashSet<>(); 665 666 Preview preview = new Preview.Builder().build(); 667 // No repeating surface is added for Preview when surface provider is not set 668 InstrumentationRegistry.getInstrumentation().runOnMainSync( 669 () -> preview.setSurfaceProvider(null)); 670 useCases.add(preview); 671 672 useCases.add(new VideoCapture.Builder<>(new Recorder.Builder().build()).build()); 673 674 StreamSharing streamSharing = createStreamSharingUseCase(useCases); 675 676 InstrumentationRegistry.getInstrumentation().runOnMainSync( 677 () -> mCamera2CameraImpl.attachUseCases(singletonList(streamSharing))); 678 679 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 680 681 InstrumentationRegistry.getInstrumentation().runOnMainSync( 682 () -> mCamera2CameraImpl.detachUseCases(asList(nonRepeating, streamSharing))); 683 } 684 685 @Test detachRepeatingUseCaseLater_meteringRepeatingIsAttached()686 public void detachRepeatingUseCaseLater_meteringRepeatingIsAttached() { 687 UseCase repeating = createUseCase(REPEATING); 688 UseCase nonRepeating = createUseCase(NON_REPEATING); 689 690 mCamera2CameraImpl.attachUseCases(asList(repeating, nonRepeating)); 691 692 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 693 694 mCamera2CameraImpl.detachUseCases(singletonList(repeating)); 695 696 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 697 698 mCamera2CameraImpl.detachUseCases(singletonList(nonRepeating)); 699 } 700 701 @Test onUseCaseReset_toRepeating_meteringRepeatingIsAttached()702 public void onUseCaseReset_toRepeating_meteringRepeatingIsAttached() { 703 TestUseCase useCase = createUseCase(NON_REPEATING); 704 705 mCamera2CameraImpl.attachUseCases(singletonList(useCase)); 706 707 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 708 709 useCase.setSurfaceOption(REPEATING); 710 useCase.notifyResetForTesting(); 711 712 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 713 714 mCamera2CameraImpl.detachUseCases(singletonList(useCase)); 715 } 716 717 @Test onUseCaseReset_toNonRepeating_meteringRepeatingIsAttached()718 public void onUseCaseReset_toNonRepeating_meteringRepeatingIsAttached() { 719 TestUseCase useCase = createUseCase(REPEATING); 720 721 mCamera2CameraImpl.attachUseCases(singletonList(useCase)); 722 723 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isFalse(); 724 725 useCase.setSurfaceOption(NON_REPEATING); 726 useCase.notifyResetForTesting(); 727 728 assertThat(mCamera2CameraImpl.isMeteringRepeatingAttached()).isTrue(); 729 730 mCamera2CameraImpl.detachUseCases(singletonList(useCase)); 731 } 732 733 @Test cameraStateIsClosed_afterInitialization()734 public void cameraStateIsClosed_afterInitialization() 735 throws ExecutionException, InterruptedException { 736 Observable<CameraInternal.State> state = mCamera2CameraImpl.getCameraState(); 737 CameraInternal.State currentState = state.fetchData().get(); 738 assertThat(currentState).isEqualTo(CameraInternal.State.CLOSED); 739 } 740 741 @Test cameraStateTransitionTest()742 public void cameraStateTransitionTest() throws InterruptedException { 743 744 final AtomicReference<CameraInternal.State> lastStableState = new AtomicReference<>(null); 745 Observable.Observer<CameraInternal.State> observer = 746 new Observable.Observer<CameraInternal.State>() { 747 @Override 748 public void onNewData(CameraInternal.@Nullable State value) { 749 // Ignore any transient states. 750 if (STABLE_STATES.contains(value)) { 751 lastStableState.set(value); 752 mSemaphore.release(); 753 } 754 } 755 756 @Override 757 public void onError(@NonNull Throwable t) { /* Ignore any transient errors. */ } 758 }; 759 760 List<CameraInternal.State> observedStates = new ArrayList<>(); 761 mCamera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(), 762 observer); 763 764 // Wait for initial CLOSED state 765 mSemaphore.acquire(); 766 observedStates.add(lastStableState.get()); 767 768 // Wait for OPEN state 769 mCamera2CameraImpl.open(); 770 mSemaphore.acquire(); 771 observedStates.add(lastStableState.get()); 772 773 // Wait for CLOSED state again 774 mCamera2CameraImpl.close(); 775 mSemaphore.acquire(); 776 observedStates.add(lastStableState.get()); 777 778 // Wait for RELEASED state 779 mCamera2CameraImpl.release(); 780 mSemaphore.acquire(); 781 observedStates.add(lastStableState.get()); 782 783 mCamera2CameraImpl.getCameraState().removeObserver(observer); 784 785 assertThat(observedStates).containsExactly( 786 CameraInternal.State.CLOSED, 787 CameraInternal.State.OPEN, 788 CameraInternal.State.CLOSED, 789 CameraInternal.State.RELEASED); 790 } 791 792 @Test cameraTransitionsThroughPendingState_whenNoCamerasAvailable()793 public void cameraTransitionsThroughPendingState_whenNoCamerasAvailable() { 794 MockObserver<CameraInternal.State> mockObserver = new MockObserver<>(); 795 796 // Ensure real camera can't open due to max cameras being open 797 Camera mockCamera = mock(Camera.class); 798 mCameraStateRegistry.registerCamera( 799 mockCamera, 800 CameraXExecutors.directExecutor(), 801 () -> { 802 }, 803 () -> { 804 }); 805 mCameraStateRegistry.tryOpenCamera(mockCamera); 806 807 mCamera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(), 808 mockObserver); 809 810 mCamera2CameraImpl.open(); 811 812 // Ensure that the camera gets to a PENDING_OPEN state 813 mockObserver.verifyOnNewDataCall(CameraInternal.State.PENDING_OPEN, 3000, 814 new CallTimesAtLeast(1)); 815 816 // Allow camera to be opened 817 mCameraStateRegistry.markCameraState(mockCamera, CameraInternal.State.CLOSED); 818 819 mockObserver.verifyOnNewDataCall(CameraInternal.State.OPEN, 3000); 820 821 mCamera2CameraImpl.getCameraState().removeObserver(mockObserver); 822 } 823 824 @Test cameraStateIsReleased_afterRelease()825 public void cameraStateIsReleased_afterRelease() 826 throws ExecutionException, InterruptedException { 827 Observable<CameraInternal.State> state = mCamera2CameraImpl.getCameraState(); 828 829 // Wait for camera to release 830 mCamera2CameraImpl.release().get(); 831 CameraInternal.State currentState = state.fetchData().get(); 832 833 assertThat(currentState).isEqualTo(CameraInternal.State.RELEASED); 834 } 835 836 @Test openNewCaptureSessionImmediateBeforePreviousCaptureSessionClosed()837 public void openNewCaptureSessionImmediateBeforePreviousCaptureSessionClosed() 838 throws InterruptedException { 839 mCamera2CameraImpl.open(); 840 UseCase useCase1 = createUseCase(); 841 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 842 mCamera2CameraImpl.onUseCaseActive(useCase1); 843 844 // Wait a little bit for the camera to open. 845 assertTrue(mSessionStateCallback.waitForOnConfigured(1)); 846 847 // Remove the useCase1 and trigger the CaptureSession#close(). 848 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 849 850 // Create the secondary use case immediately and open it before the first use case closed. 851 UseCase useCase2 = createUseCase(); 852 mCamera2CameraImpl.attachUseCases(singletonList(useCase2)); 853 mCamera2CameraImpl.onUseCaseActive(useCase2); 854 // Wait for the secondary capture session is configured. 855 assertTrue(mSessionStateCallback.waitForOnConfigured(1)); 856 857 MockObserver<CameraInternal.State> mockObserver = new MockObserver<>(); 858 859 mCamera2CameraImpl.getCameraState().addObserver(CameraXExecutors.directExecutor(), 860 mockObserver); 861 mCamera2CameraImpl.detachUseCases(singletonList(useCase2)); 862 mCamera2CameraImpl.close(); 863 864 // Wait for the CLOSED state. If the test fail, the CameraX might in wrong internal state, 865 // and the Camera2CameraImpl#release() might stuck. 866 mockObserver.verifyOnNewDataCall(CameraInternal.State.CLOSED, 4000, 867 new CallTimes(1)); 868 } 869 870 @Test closeCaptureSessionImmediateAfterCreateCaptureSession()871 public void closeCaptureSessionImmediateAfterCreateCaptureSession() 872 throws InterruptedException { 873 mCamera2CameraImpl.open(); 874 // Create another use case to keep the camera open. 875 UseCase useCaseDummy = createUseCase(); 876 UseCase useCase = createUseCase(); 877 mCamera2CameraImpl.attachUseCases(asList(useCase, useCaseDummy)); 878 mCamera2CameraImpl.onUseCaseActive(useCase); 879 880 // Wait a little bit for the camera to open. 881 assertTrue(mSessionStateCallback.waitForOnConfigured(2)); 882 883 // Remove the useCase and trigger the CaptureSession#close(). 884 mCamera2CameraImpl.detachUseCases(singletonList(useCase)); 885 assertTrue(mSessionStateCallback.waitForOnClosed(2)); 886 } 887 888 // Blocks the camera thread handler. blockHandler()889 private void blockHandler() { 890 sCameraHandler.post(() -> { 891 try { 892 mSemaphore.acquire(); 893 } catch (InterruptedException e) { 894 // Do nothing. 895 } 896 }); 897 } 898 899 // unblock camera thread handler unblockHandler()900 private void unblockHandler() { 901 mSemaphore.release(); 902 } 903 createUseCase()904 private @NonNull TestUseCase createUseCase() { 905 return createUseCase(DEFAULT_TEMPLATE_TYPE); 906 } 907 createUseCase(int template)908 private @NonNull TestUseCase createUseCase(int template) { 909 return createUseCase(template, DEFAULT_IMAGE_FORMAT); 910 } 911 createUseCase(int template, int imageFormat)912 private @NonNull TestUseCase createUseCase(int template, int imageFormat) { 913 return createUseCase(template, DEFAULT_SURFACE_OPTION, imageFormat); 914 } 915 createUseCase(@onNull SurfaceOption surfaceOption)916 private @NonNull TestUseCase createUseCase(@NonNull SurfaceOption surfaceOption) { 917 return createUseCase(DEFAULT_TEMPLATE_TYPE, surfaceOption, DEFAULT_IMAGE_FORMAT); 918 } 919 createUseCase(int template, @NonNull SurfaceOption surfaceOption, int imageFormat)920 private @NonNull TestUseCase createUseCase(int template, @NonNull SurfaceOption surfaceOption, 921 int imageFormat) { 922 return createUseCase(template, surfaceOption, imageFormat, null, null); 923 } 924 createUseCase(int template, @NonNull SurfaceOption surfaceOption, int imageFormat, @Nullable Range<Integer> targetFpsRange, @Nullable DynamicRange dynamicRange)925 private @NonNull TestUseCase createUseCase(int template, @NonNull SurfaceOption surfaceOption, 926 int imageFormat, @Nullable Range<Integer> targetFpsRange, 927 @Nullable DynamicRange dynamicRange) { 928 boolean isZslDisabled = getDefaultZslDisabled(template); 929 FakeUseCaseConfig.Builder configBuilder = 930 new FakeUseCaseConfig.Builder().setSessionOptionUnpacker( 931 new Camera2SessionOptionUnpacker()).setTargetName("UseCase") 932 .setZslDisabled(isZslDisabled); 933 if (targetFpsRange != null) { 934 configBuilder.setTargetFrameRate(targetFpsRange); 935 } 936 if (dynamicRange != null) { 937 configBuilder.setDynamicRange(dynamicRange); 938 } 939 new Camera2Interop.Extender<>(configBuilder).setSessionStateCallback(mSessionStateCallback); 940 return createUseCase(configBuilder.getUseCaseConfig(), template, surfaceOption, 941 imageFormat); 942 } 943 createUseCase(@onNull FakeUseCaseConfig config, int template, @NonNull SurfaceOption surfaceOption, int imageFormat)944 private @NonNull TestUseCase createUseCase(@NonNull FakeUseCaseConfig config, int template, 945 @NonNull SurfaceOption surfaceOption, int imageFormat) { 946 TestUseCase testUseCase = new TestUseCase( 947 template, 948 config, 949 mCamera2CameraImpl, 950 mMockRepeatingCaptureCallback, 951 surfaceOption, 952 imageFormat 953 ); 954 StreamSpec.Builder builder = StreamSpec.builder( 955 new Size(640, 480)).setImplementationOptions( 956 StreamUseCaseUtil.getStreamSpecImplementationOptions(config)); 957 if (config.getTargetFrameRate(null) != null) { 958 builder.setExpectedFrameRateRange(config.getTargetFrameRate()); 959 } 960 if (config.hasDynamicRange()) { 961 builder.setDynamicRange(config.getDynamicRange()); 962 } 963 testUseCase.updateSuggestedStreamSpec(builder.build(), 964 null); 965 mFakeUseCases.add(testUseCase); 966 return testUseCase; 967 } 968 createImageCapture()969 private @NonNull ImageCapture createImageCapture() { 970 UseCaseConfigFactory useCaseConfigFactory = 971 new Camera2UseCaseConfigFactory(ApplicationProvider.getApplicationContext()); 972 973 ImageCapture imageCapture = new ImageCapture.Builder().build(); 974 975 FakeUseCaseConfig.Builder configBuilder = 976 new FakeUseCaseConfig.Builder().setSessionOptionUnpacker( 977 new Camera2SessionOptionUnpacker()).setTargetName("UseCase"); 978 new Camera2Interop.Extender<>(configBuilder).setSessionStateCallback(mSessionStateCallback); 979 UseCaseConfig<?> config = configBuilder.getUseCaseConfig(); 980 981 imageCapture.bindToCamera(mCamera2CameraImpl, null, null, 982 imageCapture.getDefaultConfig(true, 983 useCaseConfigFactory)); 984 985 InstrumentationRegistry.getInstrumentation().runOnMainSync( 986 () -> imageCapture.updateSuggestedStreamSpec(StreamSpec.builder( 987 new Size(640, 480)).setImplementationOptions( 988 StreamUseCaseUtil.getStreamSpecImplementationOptions(config)).build(), 989 null)); 990 991 mFakeUseCases.add(imageCapture); 992 return imageCapture; 993 } 994 createStreamSharingUseCase(@onNull Set<UseCase> children)995 private @NonNull StreamSharing createStreamSharingUseCase(@NonNull Set<UseCase> children) { 996 UseCaseConfigFactory useCaseConfigFactory = 997 new Camera2UseCaseConfigFactory(ApplicationProvider.getApplicationContext()); 998 999 StreamSharing streamSharing = 1000 new StreamSharing(mCamera2CameraImpl, null, 1001 CompositionSettings.DEFAULT, 1002 CompositionSettings.DEFAULT, 1003 children, useCaseConfigFactory); 1004 1005 FakeUseCaseConfig.Builder configBuilder = 1006 new FakeUseCaseConfig.Builder().setSessionOptionUnpacker( 1007 new Camera2SessionOptionUnpacker()).setTargetName("UseCase"); 1008 new Camera2Interop.Extender<>(configBuilder).setSessionStateCallback(mSessionStateCallback); 1009 UseCaseConfig<?> config = configBuilder.getUseCaseConfig(); 1010 1011 streamSharing.bindToCamera(mCamera2CameraImpl, null, null, 1012 streamSharing.getDefaultConfig(true, 1013 useCaseConfigFactory)); 1014 1015 InstrumentationRegistry.getInstrumentation().runOnMainSync( 1016 () -> streamSharing.updateSuggestedStreamSpec(StreamSpec.builder( 1017 new Size(640, 480)).setImplementationOptions( 1018 StreamUseCaseUtil.getStreamSpecImplementationOptions(config)).build(), 1019 null)); 1020 1021 mFakeUseCases.add(streamSharing); 1022 return streamSharing; 1023 } 1024 1025 @Test useCaseOnStateAttached_isCalled()1026 public void useCaseOnStateAttached_isCalled() throws InterruptedException { 1027 TestUseCase useCase1 = spy((TestUseCase) createUseCase()); 1028 TestUseCase useCase2 = spy((TestUseCase) createUseCase()); 1029 1030 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 1031 mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2)); 1032 1033 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1034 1035 Handler uiThreadHandler = new Handler(Looper.getMainLooper()); 1036 HandlerUtil.waitForLooperToIdle(uiThreadHandler); 1037 1038 verify(useCase1, times(1)).onStateAttached(); 1039 verify(useCase2, times(1)).onStateAttached(); 1040 1041 mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2)); 1042 } 1043 1044 @Test useCaseOnStateDetached_isCalled()1045 public void useCaseOnStateDetached_isCalled() throws InterruptedException { 1046 TestUseCase useCase1 = spy((TestUseCase) createUseCase()); 1047 TestUseCase useCase2 = spy((TestUseCase) createUseCase()); 1048 TestUseCase useCase3 = spy((TestUseCase) createUseCase()); 1049 1050 mCamera2CameraImpl.attachUseCases(asList(useCase1, useCase2)); 1051 1052 mCamera2CameraImpl.detachUseCases(asList(useCase1, useCase2, useCase3)); 1053 1054 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1055 1056 Handler uiThreadHandler = new Handler(Looper.getMainLooper()); 1057 HandlerUtil.waitForLooperToIdle(uiThreadHandler); 1058 1059 verify(useCase1, times(1)).onStateDetached(); 1060 verify(useCase2, times(1)).onStateDetached(); 1061 verify(useCase3, times(0)).onStateDetached(); 1062 } 1063 isCameraControlActive(Camera2CameraControlImpl camera2CameraControlImpl)1064 private boolean isCameraControlActive(Camera2CameraControlImpl camera2CameraControlImpl) { 1065 ListenableFuture<Void> listenableFuture = camera2CameraControlImpl.setZoomRatio(2.0f); 1066 try { 1067 // setZoom() will fail immediately when CameraControl is not active. 1068 listenableFuture.get(50, TimeUnit.MILLISECONDS); 1069 } catch (ExecutionException e) { 1070 if (e.getCause() instanceof CameraControl.OperationCanceledException) { 1071 return false; 1072 } 1073 } catch (InterruptedException | TimeoutException e) { 1074 // Do nothing. 1075 } 1076 return true; 1077 } 1078 1079 @Test activateCameraControl_whenExistsAttachedUseCases()1080 public void activateCameraControl_whenExistsAttachedUseCases() throws InterruptedException { 1081 Camera2CameraControlImpl camera2CameraControlImpl = 1082 (Camera2CameraControlImpl) mCamera2CameraImpl.getCameraControlInternal(); 1083 1084 assertThat(isCameraControlActive(camera2CameraControlImpl)).isFalse(); 1085 1086 UseCase useCase1 = createUseCase(); 1087 1088 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 1089 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1090 1091 assertThat(isCameraControlActive(camera2CameraControlImpl)).isTrue(); 1092 1093 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 1094 } 1095 1096 @Test deactivateCameraControl_whenNoAttachedUseCases()1097 public void deactivateCameraControl_whenNoAttachedUseCases() throws InterruptedException { 1098 Camera2CameraControlImpl camera2CameraControlImpl = 1099 (Camera2CameraControlImpl) mCamera2CameraImpl.getCameraControlInternal(); 1100 UseCase useCase1 = createUseCase(); 1101 1102 mCamera2CameraImpl.attachUseCases(singletonList(useCase1)); 1103 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1104 assertThat(isCameraControlActive(camera2CameraControlImpl)).isTrue(); 1105 1106 mCamera2CameraImpl.detachUseCases(singletonList(useCase1)); 1107 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1108 1109 assertThat(isCameraControlActive(camera2CameraControlImpl)).isFalse(); 1110 } 1111 1112 @Test attachUseCaseWithTemplatePreview()1113 public void attachUseCaseWithTemplatePreview() throws InterruptedException { 1114 UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW); 1115 1116 mCamera2CameraImpl.attachUseCases(singletonList(preview)); 1117 mCamera2CameraImpl.onUseCaseActive(preview); 1118 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1119 1120 ArgumentCaptor<CameraCaptureResult> captor = 1121 ArgumentCaptor.forClass(CameraCaptureResult.class); 1122 verify(mMockRepeatingCaptureCallback, timeout(4000).atLeastOnce()) 1123 .onCaptureCompleted(anyInt(), captor.capture()); 1124 1125 CaptureResult captureResult = 1126 ((Camera2CameraCaptureResult) captor.getValue()).getCaptureResult(); 1127 1128 assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT)) 1129 .isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); 1130 1131 mCamera2CameraImpl.detachUseCases(singletonList(preview)); 1132 } 1133 1134 @Test attachUseCaseWithTemplateRecord()1135 public void attachUseCaseWithTemplateRecord() throws InterruptedException { 1136 UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW); 1137 UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD); 1138 int expectedCaptureIntent = CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD; 1139 if (workaroundByCaptureIntentPreview( 1140 mCamera2CameraImpl.getCameraInfoInternal().getCameraQuirks())) { 1141 expectedCaptureIntent = CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW; 1142 } 1143 1144 mCamera2CameraImpl.attachUseCases(asList(preview, record)); 1145 mCamera2CameraImpl.onUseCaseActive(preview); 1146 mCamera2CameraImpl.onUseCaseActive(record); 1147 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1148 1149 ArgumentCaptor<CameraCaptureResult> captor = 1150 ArgumentCaptor.forClass(CameraCaptureResult.class); 1151 verify(mMockRepeatingCaptureCallback, timeout(4000).atLeastOnce()) 1152 .onCaptureCompleted(anyInt(), captor.capture()); 1153 1154 CaptureResult captureResult = 1155 ((Camera2CameraCaptureResult) captor.getValue()).getCaptureResult(); 1156 1157 assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT)) 1158 .isEqualTo(expectedCaptureIntent); 1159 1160 mCamera2CameraImpl.detachUseCases(asList(preview, record)); 1161 } 1162 1163 @SdkSuppress(minSdkVersion = 23) 1164 @Test attachUseCaseWithTemplateZSLNoRecord()1165 public void attachUseCaseWithTemplateZSLNoRecord() throws InterruptedException { 1166 if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) { 1167 return; 1168 } 1169 UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW); 1170 UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); 1171 1172 mCamera2CameraImpl.attachUseCases(asList(preview, zsl)); 1173 mCamera2CameraImpl.onUseCaseActive(preview); 1174 mCamera2CameraImpl.onUseCaseActive(zsl); 1175 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1176 1177 ArgumentCaptor<CameraCaptureResult> captor = 1178 ArgumentCaptor.forClass(CameraCaptureResult.class); 1179 verify(mMockRepeatingCaptureCallback, timeout(4000).atLeastOnce()) 1180 .onCaptureCompleted(anyInt(), captor.capture()); 1181 1182 CaptureResult captureResult = 1183 ((Camera2CameraCaptureResult) captor.getValue()).getCaptureResult(); 1184 1185 assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT)) 1186 .isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); 1187 assertThat( 1188 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1189 .isFalse(); 1190 1191 mCamera2CameraImpl.detachUseCases(asList(preview, zsl)); 1192 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1193 assertThat(mCamera2CameraImpl.getCameraControlInternal() 1194 .isZslDisabledByByUserCaseConfig()).isFalse(); 1195 } 1196 1197 @SdkSuppress(minSdkVersion = 23) 1198 @Test attachUseCaseWithTemplateZSLHasRecord()1199 public void attachUseCaseWithTemplateZSLHasRecord() throws InterruptedException { 1200 if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) { 1201 return; 1202 } 1203 // Legacy device can support surface combination PRIV + YUV + JPEG 1204 UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, PRIVATE); 1205 UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, YUV_420_888); 1206 UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, NON_REPEATING, JPEG); 1207 int expectedCaptureIntent = CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD; 1208 if (workaroundByCaptureIntentPreview( 1209 mCamera2CameraImpl.getCameraInfoInternal().getCameraQuirks())) { 1210 expectedCaptureIntent = CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW; 1211 } 1212 1213 mCamera2CameraImpl.attachUseCases(asList(preview, record, zsl)); 1214 mCamera2CameraImpl.onUseCaseActive(preview); 1215 mCamera2CameraImpl.onUseCaseActive(record); 1216 mCamera2CameraImpl.onUseCaseActive(zsl); 1217 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1218 1219 ArgumentCaptor<CameraCaptureResult> captor = 1220 ArgumentCaptor.forClass(CameraCaptureResult.class); 1221 verify(mMockRepeatingCaptureCallback, timeout(4000).atLeastOnce()) 1222 .onCaptureCompleted(anyInt(), captor.capture()); 1223 1224 CaptureResult captureResult = 1225 ((Camera2CameraCaptureResult) captor.getValue()).getCaptureResult(); 1226 1227 assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT)) 1228 .isEqualTo(expectedCaptureIntent); 1229 assertThat( 1230 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1231 .isTrue(); 1232 1233 mCamera2CameraImpl.detachUseCases(asList(preview, record, zsl)); 1234 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1235 assertThat( 1236 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1237 .isFalse(); 1238 } 1239 1240 @SdkSuppress(minSdkVersion = 23) 1241 @Test attachAndDetachUseCasesMultipleTimes()1242 public void attachAndDetachUseCasesMultipleTimes() throws InterruptedException { 1243 if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) { 1244 return; 1245 } 1246 // Legacy device can support surface combination PRIV + YUV + JPEG 1247 UseCase preview = createUseCase(CameraDevice.TEMPLATE_PREVIEW, PRIVATE); 1248 UseCase record = createUseCase(CameraDevice.TEMPLATE_RECORD, YUV_420_888); 1249 UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, NON_REPEATING, JPEG); 1250 1251 mCamera2CameraImpl.attachUseCases(asList(preview, zsl)); 1252 mCamera2CameraImpl.onUseCaseActive(preview); 1253 mCamera2CameraImpl.onUseCaseActive(zsl); 1254 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1255 1256 assertThat( 1257 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1258 .isFalse(); 1259 1260 mCamera2CameraImpl.attachUseCases(singletonList(record)); 1261 mCamera2CameraImpl.onUseCaseActive(record); 1262 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1263 1264 assertThat( 1265 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1266 .isTrue(); 1267 1268 mCamera2CameraImpl.detachUseCases(singletonList(record)); 1269 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1270 assertThat( 1271 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1272 .isFalse(); 1273 1274 mCamera2CameraImpl.attachUseCases(singletonList(record)); 1275 mCamera2CameraImpl.onUseCaseActive(record); 1276 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1277 assertThat( 1278 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1279 .isTrue(); 1280 1281 mCamera2CameraImpl.detachUseCases(singletonList(zsl)); 1282 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1283 assertThat( 1284 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1285 .isTrue(); 1286 1287 mCamera2CameraImpl.detachUseCases(singletonList(preview)); 1288 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1289 assertThat( 1290 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1291 .isTrue(); 1292 1293 mCamera2CameraImpl.detachUseCases(singletonList(record)); 1294 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1295 assertThat( 1296 mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1297 .isFalse(); 1298 } 1299 1300 @SdkSuppress(minSdkVersion = 23) 1301 @Test zslDisabled_whenHighResolutionIsEnabled()1302 public void zslDisabled_whenHighResolutionIsEnabled() throws InterruptedException { 1303 UseCase zsl = createUseCase(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); 1304 1305 // Creates a test use case with high resolution enabled. 1306 ResolutionSelector highResolutionSelector = 1307 new ResolutionSelector.Builder().setAllowedResolutionMode( 1308 PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE).build(); 1309 FakeUseCaseConfig.Builder configBuilder = 1310 new FakeUseCaseConfig.Builder().setSessionOptionUnpacker( 1311 new Camera2SessionOptionUnpacker()).setTargetName( 1312 "UseCase").setResolutionSelector(highResolutionSelector); 1313 new Camera2Interop.Extender<>(configBuilder).setSessionStateCallback(mSessionStateCallback); 1314 UseCase highResolutionUseCase = createUseCase(configBuilder.getUseCaseConfig(), 1315 CameraDevice.TEMPLATE_PREVIEW, DEFAULT_SURFACE_OPTION, DEFAULT_IMAGE_FORMAT); 1316 1317 // Checks zsl is disabled after UseCase#onAttach() is called to merge/update config. 1318 assertThat(highResolutionUseCase.getCurrentConfig().isZslDisabled(false)).isTrue(); 1319 1320 if (!mCamera2CameraImpl.getCameraInfo().isZslSupported()) { 1321 return; 1322 } 1323 1324 mCamera2CameraImpl.attachUseCases(asList(zsl, highResolutionUseCase)); 1325 mCamera2CameraImpl.onUseCaseActive(zsl); 1326 mCamera2CameraImpl.onUseCaseActive(highResolutionUseCase); 1327 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1328 assertThat(mCamera2CameraImpl.getCameraControlInternal().isZslDisabledByByUserCaseConfig()) 1329 .isTrue(); 1330 } 1331 1332 @Test attachUseCaseWithTemplatePreviewInConcurrentMode()1333 public void attachUseCaseWithTemplatePreviewInConcurrentMode() throws Exception { 1334 // Arrange. 1335 CameraManagerCompat cameraManagerCompat = 1336 CameraManagerCompat.from((Context) ApplicationProvider.getApplicationContext()); 1337 1338 PackageManager pm = ApplicationProvider.getApplicationContext().getPackageManager(); 1339 if (mPairedCameraId != null 1340 && pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT)) { 1341 Camera2CameraInfoImpl pairedCamera2CameraInfo = new Camera2CameraInfoImpl( 1342 mPairedCameraId, cameraManagerCompat); 1343 Camera2CameraImpl pairedCamera2CameraImpl = new Camera2CameraImpl( 1344 (Context) ApplicationProvider.getApplicationContext(), 1345 cameraManagerCompat, mPairedCameraId, pairedCamera2CameraInfo, 1346 mCameraCoordinator, 1347 mCameraStateRegistry, sCameraExecutor, sCameraHandler, 1348 DisplayInfoManager.getInstance(ApplicationProvider.getApplicationContext()), 1349 -1L); 1350 mCameraCoordinator.addConcurrentCameraIdsAndCameraSelectors( 1351 new HashMap<String, CameraSelector>() {{ 1352 put(mCameraId, DEFAULT_BACK_CAMERA); 1353 put(mPairedCameraId, CameraSelector.DEFAULT_FRONT_CAMERA); 1354 }}); 1355 mCameraCoordinator.setCameraOperatingMode(CAMERA_OPERATING_MODE_CONCURRENT); 1356 mCameraStateRegistry.onCameraOperatingModeUpdated( 1357 CAMERA_OPERATING_MODE_SINGLE, CAMERA_OPERATING_MODE_CONCURRENT); 1358 1359 // Act. 1360 UseCase preview1 = createUseCase(CameraDevice.TEMPLATE_PREVIEW); 1361 mCamera2CameraImpl.attachUseCases(singletonList(preview1)); 1362 mCamera2CameraImpl.onUseCaseActive(preview1); 1363 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1364 1365 // Assert. 1366 ArgumentCaptor<CameraCaptureResult> captor = 1367 ArgumentCaptor.forClass(CameraCaptureResult.class); 1368 verify(mMockRepeatingCaptureCallback, never()).onCaptureCompleted(anyInt(), 1369 captor.capture()); 1370 1371 // Act. 1372 UseCase preview2 = createUseCase(CameraDevice.TEMPLATE_PREVIEW); 1373 pairedCamera2CameraImpl.attachUseCases(singletonList(preview2)); 1374 pairedCamera2CameraImpl.onUseCaseActive(preview2); 1375 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1376 1377 // Assert. 1378 captor = ArgumentCaptor.forClass(CameraCaptureResult.class); 1379 verify(mMockRepeatingCaptureCallback, timeout(4000).atLeastOnce()) 1380 .onCaptureCompleted(anyInt(), captor.capture()); 1381 CaptureResult captureResult = (captor.getValue()).getCaptureResult(); 1382 assertThat(captureResult.get(CaptureResult.CONTROL_CAPTURE_INTENT)) 1383 .isEqualTo(CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); 1384 1385 mCamera2CameraImpl.detachUseCases(singletonList(preview1)); 1386 pairedCamera2CameraImpl.detachUseCases(singletonList(preview2)); 1387 } 1388 } 1389 1390 @SdkSuppress(minSdkVersion = 35) 1391 @Test lowLightBoostDisabled_whenFrameRateRangeExceed30()1392 public void lowLightBoostDisabled_whenFrameRateRangeExceed30() throws InterruptedException { 1393 assumeTrue(mCamera2CameraImpl.getCameraInfo().isLowLightBoostSupported()); 1394 Set<Range<Integer>> supportedFrameRateRanges = 1395 mCamera2CameraImpl.mCameraInfoInternal.getSupportedFrameRateRanges(); 1396 Range<Integer> fpsRangeExceed30 = null; 1397 for (Range<Integer> fpsRange: supportedFrameRateRanges) { 1398 if (fpsRange.getUpper() > 30) { 1399 fpsRangeExceed30 = fpsRange; 1400 break; 1401 } 1402 } 1403 assumeTrue("The test only runs on devices that support frame rate range exceeding 30.", 1404 fpsRangeExceed30 != null); 1405 1406 UseCase useCase = createUseCase(CameraDevice.TEMPLATE_PREVIEW, DEFAULT_SURFACE_OPTION, 1407 DEFAULT_IMAGE_FORMAT, fpsRangeExceed30, null); 1408 1409 Camera2CameraControlImpl camera2CameraControlImpl = 1410 (Camera2CameraControlImpl) mCamera2CameraImpl.getCameraControlInternal(); 1411 LowLightBoostControl lowLightBoostControl = 1412 camera2CameraControlImpl.getLowLightBoostControl(); 1413 1414 mCamera2CameraImpl.onUseCaseActive(useCase); 1415 mCamera2CameraImpl.attachUseCases(singletonList(useCase)); 1416 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1417 assertThat(lowLightBoostControl.isLowLightBoostDisabledByUseCaseSessionConfig()).isTrue(); 1418 } 1419 1420 @SdkSuppress(minSdkVersion = 35) 1421 @Test lowLightBoostDisabled_whenHdr10BitEnabled()1422 public void lowLightBoostDisabled_whenHdr10BitEnabled() throws InterruptedException { 1423 assumeTrue(mCamera2CameraImpl.getCameraInfo().isLowLightBoostSupported()); 1424 1425 Set<DynamicRange> supportedDynamicRanges = 1426 mCamera2CameraImpl.mCameraInfoInternal.getSupportedDynamicRanges(); 1427 DynamicRange hdr10BitDynamicRange = null; 1428 for (DynamicRange dynamicRange: supportedDynamicRanges) { 1429 if (dynamicRange != DynamicRange.SDR) { 1430 hdr10BitDynamicRange = dynamicRange; 1431 break; 1432 } 1433 } 1434 assumeTrue("The test only runs on devices that support HDR 10-bit dynamic range.", 1435 hdr10BitDynamicRange != null); 1436 1437 UseCase useCase = createUseCase(CameraDevice.TEMPLATE_PREVIEW, DEFAULT_SURFACE_OPTION, 1438 DEFAULT_IMAGE_FORMAT, null, hdr10BitDynamicRange); 1439 1440 Camera2CameraControlImpl camera2CameraControlImpl = 1441 (Camera2CameraControlImpl) mCamera2CameraImpl.getCameraControlInternal(); 1442 LowLightBoostControl lowLightBoostControl = 1443 camera2CameraControlImpl.getLowLightBoostControl(); 1444 1445 mCamera2CameraImpl.onUseCaseActive(useCase); 1446 mCamera2CameraImpl.attachUseCases(singletonList(useCase)); 1447 HandlerUtil.waitForLooperToIdle(sCameraHandler); 1448 assertThat(lowLightBoostControl.isLowLightBoostDisabledByUseCaseSessionConfig()).isTrue(); 1449 } 1450 changeUseCaseSurface(UseCase useCase)1451 private void changeUseCaseSurface(UseCase useCase) { 1452 useCase.updateSuggestedStreamSpec(StreamSpec.builder( 1453 new Size(640, 480)).setImplementationOptions( 1454 StreamUseCaseUtil.getStreamSpecImplementationOptions( 1455 useCase.getCurrentConfig())).build(), null); 1456 } 1457 getDefaultZslDisabled(int templateType)1458 private static boolean getDefaultZslDisabled(int templateType) { 1459 Boolean isZslDisabled = DEFAULT_TEMPLATE_TO_ZSL_DISABLED.get(templateType); 1460 checkState(isZslDisabled != null, "No default mapping from template to zsl disabled"); 1461 return isZslDisabled; 1462 } 1463 1464 public static class TestUseCase extends FakeUseCase { 1465 HandlerThread mHandlerThread = new HandlerThread("HandlerThread"); 1466 Handler mHandler; 1467 FakeUseCaseConfig mConfig; 1468 private DeferrableSurface mDeferrableSurface; 1469 private SurfaceOption mSurfaceOption; 1470 private final CameraCaptureCallback mRepeatingCaptureCallback; 1471 private final int mTemplate; 1472 private SessionConfig.Builder mSessionConfigBuilder; 1473 private final int mImageFormat; 1474 1475 @SuppressWarnings("NewClassNamingConvention") 1476 public enum SurfaceOption { 1477 /** UseCase will not add any surface in SessionConfig. */ 1478 NO_SURFACE, 1479 /** UseCase will add a repeating surface in SessionConfig. */ 1480 REPEATING, 1481 /** UseCase will add a non-repeating surface in SessionConfig. */ 1482 NON_REPEATING, 1483 } 1484 TestUseCase( int template, @NonNull FakeUseCaseConfig config, @NonNull CameraInternal camera, @NonNull CameraCaptureCallback repeatingCaptureCallback, @NonNull SurfaceOption surfaceOption, int imageFormat)1485 TestUseCase( 1486 int template, 1487 @NonNull FakeUseCaseConfig config, 1488 @NonNull CameraInternal camera, 1489 @NonNull CameraCaptureCallback repeatingCaptureCallback, 1490 @NonNull SurfaceOption surfaceOption, 1491 int imageFormat) { 1492 super(config); 1493 // Ensure we're using the combined configuration (user config + defaults) 1494 mConfig = (FakeUseCaseConfig) getCurrentConfig(); 1495 mTemplate = template; 1496 mSurfaceOption = surfaceOption; 1497 mImageFormat = imageFormat; 1498 1499 mRepeatingCaptureCallback = repeatingCaptureCallback; 1500 mHandlerThread.start(); 1501 mHandler = new Handler(mHandlerThread.getLooper()); 1502 bindToCamera(camera, null, null, null); 1503 updateSuggestedStreamSpec(StreamSpec.builder( 1504 new Size(640, 480)).setImplementationOptions( 1505 StreamUseCaseUtil.getStreamSpecImplementationOptions(config)).build(), 1506 null); 1507 } 1508 close()1509 public void close() { 1510 mHandler.removeCallbacksAndMessages(null); 1511 mHandlerThread.quitSafely(); 1512 if (mDeferrableSurface != null) { 1513 mDeferrableSurface.close(); 1514 mDeferrableSurface = null; 1515 } 1516 } 1517 1518 @Override onUnbind()1519 public void onUnbind() { 1520 super.onUnbind(); 1521 close(); 1522 } 1523 1524 @Override onSuggestedStreamSpecUpdated( @onNull StreamSpec primaryStreamSpec, @Nullable StreamSpec secondaryStreamSpec)1525 protected @NonNull StreamSpec onSuggestedStreamSpecUpdated( 1526 @NonNull StreamSpec primaryStreamSpec, 1527 @Nullable StreamSpec secondaryStreamSpec) { 1528 if (mDeferrableSurface != null) { 1529 mDeferrableSurface.close(); 1530 } 1531 mDeferrableSurface = createDeferrableSurface(primaryStreamSpec, mImageFormat); 1532 mSessionConfigBuilder = SessionConfig.Builder.createFrom(mConfig, 1533 primaryStreamSpec.getResolution()); 1534 mSessionConfigBuilder.setTemplateType(mTemplate); 1535 mSessionConfigBuilder.addRepeatingCameraCaptureCallback(mRepeatingCaptureCallback); 1536 mSessionConfigBuilder.setExpectedFrameRateRange( 1537 primaryStreamSpec.getExpectedFrameRateRange()); 1538 updateSessionBuilderBySurfaceOption(); 1539 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 1540 return primaryStreamSpec; 1541 } 1542 setSurfaceOption(@onNull SurfaceOption surfaceOption)1543 public void setSurfaceOption(@NonNull SurfaceOption surfaceOption) { 1544 if (mSurfaceOption != surfaceOption) { 1545 mSurfaceOption = surfaceOption; 1546 updateSessionBuilderBySurfaceOption(); 1547 updateSessionConfig(List.of(mSessionConfigBuilder.build())); 1548 } 1549 } 1550 updateSessionBuilderBySurfaceOption()1551 private void updateSessionBuilderBySurfaceOption() { 1552 checkNotNull(mDeferrableSurface); 1553 mSessionConfigBuilder.clearSurfaces(); 1554 switch (mSurfaceOption) { 1555 case NO_SURFACE: 1556 break; 1557 case REPEATING: 1558 mSessionConfigBuilder.addSurface(mDeferrableSurface, 1559 mConfig.hasDynamicRange() ? mConfig.getDynamicRange() 1560 : DynamicRange.SDR, null, MirrorMode.MIRROR_MODE_UNSPECIFIED); 1561 break; 1562 case NON_REPEATING: 1563 mSessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface, 1564 mConfig.hasDynamicRange() ? mConfig.getDynamicRange() 1565 : DynamicRange.SDR); 1566 break; 1567 } 1568 } 1569 createDeferrableSurface(@onNull StreamSpec streamSpec, int imageFormat)1570 private @NonNull DeferrableSurface createDeferrableSurface(@NonNull StreamSpec streamSpec, 1571 int imageFormat) { 1572 Size suggestedResolution = streamSpec.getResolution(); 1573 Surface surface; 1574 final SurfaceTexture surfaceTexture; 1575 final ImageReader imageReader; 1576 if (imageFormat == PRIVATE) { 1577 surfaceTexture = new SurfaceTexture(0); 1578 surfaceTexture.setDefaultBufferSize(suggestedResolution.getWidth(), 1579 suggestedResolution.getHeight()); 1580 surface = new Surface(surfaceTexture); 1581 imageReader = null; 1582 } else if (imageFormat == YUV_420_888 || imageFormat == JPEG) { 1583 //noinspection resource 1584 imageReader = 1585 ImageReader.newInstance( 1586 suggestedResolution.getWidth(), 1587 suggestedResolution.getHeight(), 1588 imageFormat, /*maxImages*/ 1589 2); 1590 imageReader.setOnImageAvailableListener(reader -> { 1591 try { 1592 Image image = reader.acquireLatestImage(); 1593 if (image != null) { 1594 image.close(); 1595 } 1596 } catch (RuntimeException e) { 1597 // ImageReader could be closed. Ignore. 1598 } 1599 }, mHandler); 1600 surface = imageReader.getSurface(); 1601 surfaceTexture = null; 1602 } else { 1603 throw new IllegalArgumentException("Unsupported image format: " + imageFormat); 1604 } 1605 DeferrableSurface deferrableSurface = new ImmediateSurface(surface); 1606 deferrableSurface.getTerminationFuture().addListener(() -> { 1607 surface.release(); 1608 if (surfaceTexture != null) { 1609 surfaceTexture.release(); 1610 } 1611 if (imageReader != null) { 1612 imageReader.close(); 1613 } 1614 }, CameraXExecutors.directExecutor()); 1615 return deferrableSurface; 1616 } 1617 } 1618 } 1619