1 /* <lambda>null2 * Copyright 2021 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.pipe.integration 18 19 import android.content.Context 20 import android.graphics.SurfaceTexture 21 import android.hardware.camera2.CameraCharacteristics 22 import android.hardware.camera2.CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES 23 import android.hardware.camera2.CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES 24 import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AE 25 import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AF 26 import android.hardware.camera2.CameraCharacteristics.CONTROL_MAX_REGIONS_AWB 27 import android.hardware.camera2.CameraDevice 28 import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_OFF 29 import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON 30 import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH 31 import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH 32 import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY 33 import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_AUTO 34 import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE 35 import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO 36 import android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_OFF 37 import android.hardware.camera2.CameraMetadata.FLASH_MODE_OFF 38 import android.hardware.camera2.CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION 39 import android.hardware.camera2.CaptureRequest.CONTROL_AE_MODE 40 import android.hardware.camera2.CaptureRequest.CONTROL_AE_REGIONS 41 import android.hardware.camera2.CaptureRequest.CONTROL_AF_MODE 42 import android.hardware.camera2.CaptureRequest.CONTROL_AF_REGIONS 43 import android.hardware.camera2.CaptureRequest.CONTROL_AWB_REGIONS 44 import android.hardware.camera2.CaptureRequest.CONTROL_EFFECT_MODE 45 import android.hardware.camera2.CaptureRequest.CONTROL_ZOOM_RATIO 46 import android.hardware.camera2.CaptureRequest.FLASH_MODE 47 import android.hardware.camera2.CaptureRequest.FLASH_MODE_TORCH 48 import android.hardware.camera2.CaptureRequest.SCALER_CROP_REGION 49 import android.hardware.camera2.params.MeteringRectangle 50 import android.os.Build 51 import android.util.Size 52 import android.view.Surface 53 import androidx.camera.camera2.pipe.CameraGraph 54 import androidx.camera.camera2.pipe.FrameInfo 55 import androidx.camera.camera2.pipe.RequestMetadata 56 import androidx.camera.camera2.pipe.integration.adapter.CameraControlAdapter 57 import androidx.camera.camera2.pipe.integration.compat.quirk.CrashWhenTakingPhotoWithAutoFlashAEModeQuirk 58 import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks 59 import androidx.camera.camera2.pipe.integration.compat.quirk.ImageCaptureFailWithAutoFlashQuirk 60 import androidx.camera.camera2.pipe.integration.compat.workaround.AutoFlashAEModeDisablerImpl 61 import androidx.camera.camera2.pipe.integration.impl.ComboRequestListener 62 import androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo 63 import androidx.camera.camera2.pipe.integration.interop.CaptureRequestOptions 64 import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop 65 import androidx.camera.camera2.pipe.testing.VerifyResultListener 66 import androidx.camera.camera2.pipe.testing.toCameraControlAdapter 67 import androidx.camera.camera2.pipe.testing.toCameraInfoAdapter 68 import androidx.camera.core.Camera 69 import androidx.camera.core.CameraControl 70 import androidx.camera.core.CameraSelector 71 import androidx.camera.core.FocusMeteringAction 72 import androidx.camera.core.ImageAnalysis 73 import androidx.camera.core.ImageCapture 74 import androidx.camera.core.Preview 75 import androidx.camera.core.SurfaceOrientedMeteringPointFactory 76 import androidx.camera.core.UseCase 77 import androidx.camera.core.impl.DeferrableSurface 78 import androidx.camera.core.impl.Quirks 79 import androidx.camera.core.impl.SessionConfig 80 import androidx.camera.core.impl.utils.futures.Futures 81 import androidx.camera.core.internal.CameraUseCaseAdapter 82 import androidx.camera.testing.impl.CameraUtil 83 import androidx.camera.testing.impl.CameraXUtil 84 import androidx.camera.testing.impl.SurfaceTextureProvider 85 import androidx.camera.testing.impl.fakes.FakeUseCase 86 import androidx.camera.testing.impl.fakes.FakeUseCaseConfig 87 import androidx.concurrent.futures.await 88 import androidx.test.core.app.ApplicationProvider 89 import androidx.test.ext.junit.runners.AndroidJUnit4 90 import androidx.test.filters.LargeTest 91 import androidx.test.filters.SdkSuppress 92 import com.google.common.truth.Truth 93 import com.google.common.util.concurrent.ListenableFuture 94 import java.util.concurrent.ExecutionException 95 import java.util.concurrent.TimeUnit 96 import kotlinx.coroutines.Dispatchers 97 import kotlinx.coroutines.asExecutor 98 import kotlinx.coroutines.runBlocking 99 import kotlinx.coroutines.withContext 100 import org.hamcrest.CoreMatchers.equalTo 101 import org.hamcrest.CoreMatchers.notNullValue 102 import org.junit.After 103 import org.junit.Assert 104 import org.junit.Assume.assumeThat 105 import org.junit.Assume.assumeTrue 106 import org.junit.Before 107 import org.junit.Rule 108 import org.junit.Test 109 import org.junit.runner.RunWith 110 111 private val TIMEOUT = TimeUnit.SECONDS.toMillis(10) 112 113 @LargeTest 114 @RunWith(AndroidJUnit4::class) 115 @OptIn(ExperimentalCamera2Interop::class) 116 @SdkSuppress(minSdkVersion = 21) 117 class CameraControlAdapterDeviceTest { 118 private lateinit var cameraSelector: CameraSelector 119 private lateinit var context: Context 120 private lateinit var camera: CameraUseCaseAdapter 121 private lateinit var cameraControl: CameraControlAdapter 122 private lateinit var comboListener: ComboRequestListener 123 private lateinit var characteristics: CameraCharacteristics 124 private var hasFlashUnit: Boolean = false 125 private var testEffectMode: Int? = null 126 127 private val imageCapture = ImageCapture.Builder().build() 128 private val imageAnalysis = 129 ImageAnalysis.Builder().build().apply { 130 // set analyzer to make it active. 131 setAnalyzer(Dispatchers.Default.asExecutor()) { 132 // Fake analyzer, do nothing. Close the ImageProxy immediately to prevent the 133 // closing 134 // of the CameraDevice from being stuck. 135 it.close() 136 } 137 } 138 139 @get:Rule val useCamera = CameraUtil.grantCameraPermissionAndPreTestAndPostTest() 140 141 @Before 142 fun setUp() { 143 assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK)) 144 145 context = ApplicationProvider.getApplicationContext() 146 CameraXUtil.initialize(context, CameraPipeConfig.defaultConfig()) 147 cameraSelector = 148 CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build() 149 camera = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector) 150 cameraControl = camera.cameraControl.toCameraControlAdapter() 151 comboListener = cameraControl.camera2cameraControl.requestListener 152 153 characteristics = CameraUtil.getCameraCharacteristics(CameraSelector.LENS_FACING_BACK)!! 154 155 hasFlashUnit = camera.cameraInfo.hasFlashUnit() 156 testEffectMode = camera.getTestEffectMode() 157 } 158 159 @After 160 fun tearDown(): Unit = runBlocking { 161 if (::camera.isInitialized) { 162 withContext(Dispatchers.Main) { camera.removeUseCases(camera.useCases) } 163 } 164 165 CameraXUtil.shutdown()[10000, TimeUnit.MILLISECONDS] 166 } 167 168 // TODO: test all public API of the CameraControl to ensure the RequestOptions still exist 169 // after adding/removing the UseCase. 170 @Test 171 fun addUseCase_requestOptionsShouldSetToCamera(): Unit = runBlocking { 172 // Arrange. 173 bindUseCase(imageAnalysis) 174 arrangeRequestOptions() 175 176 // Act. 177 withContext(Dispatchers.Main) { camera.addUseCases(listOf(imageCapture)) } 178 179 // Assert. Attaching a new UseCase should not change the RequestOptions that we set the 180 // UseCaseCamera, The CaptureRequest after the new UseCase is attached should have the 181 // same RequestOptions as before. The verify block will verify the CaptureRequest has the 182 // same RequestOptions as we arranged. 183 verifyRequestOptions() 184 } 185 186 // TODO: test all public API of the CameraControl to ensure the RequestOptions still exist 187 // after adding/removing the UseCase. 188 @Test 189 fun removeUseCase_requestOptionsShouldSetToCamera(): Unit = runBlocking { 190 // Arrange. 191 bindUseCase(imageAnalysis, imageCapture) 192 arrangeRequestOptions() 193 194 // Act. 195 withContext(Dispatchers.Main) { camera.removeUseCases(listOf(imageCapture)) } 196 197 // Assert. Removing one of the UseCases (not all) should not change the 198 // RequestOptions that we set the UseCaseCamera, the CaptureRequest after the UseCase 199 // removal should have the same RequestOptions as before. The verify block will verify 200 // the CaptureRequest has the same RequestOptions as we arranged. 201 verifyRequestOptions() 202 } 203 204 @Test 205 fun setFlashModeAuto_aeModeSetAndRequestUpdated(): Unit = runBlocking { 206 assumeTrue(hasFlashUnit) 207 bindUseCase(imageAnalysis) 208 cameraControl.flashMode = ImageCapture.FLASH_MODE_AUTO 209 210 waitForResult(captureCount = 60) 211 .verify( 212 { requestMeta: RequestMetadata, _ -> 213 requestMeta.isAeMode(CONTROL_AE_MODE_ON_AUTO_FLASH) 214 }, 215 TIMEOUT 216 ) 217 Truth.assertThat(cameraControl.flashMode).isEqualTo(ImageCapture.FLASH_MODE_AUTO) 218 } 219 220 @Test 221 fun setFlashModeOff_aeModeSetAndRequestUpdated(): Unit = runBlocking { 222 assumeTrue(hasFlashUnit) 223 bindUseCase(imageAnalysis) 224 cameraControl.flashMode = ImageCapture.FLASH_MODE_OFF 225 226 waitForResult(captureCount = 60) 227 .verify( 228 { requestMeta: RequestMetadata, _ -> requestMeta.isAeMode(CONTROL_AE_MODE_ON) }, 229 TIMEOUT 230 ) 231 Truth.assertThat(cameraControl.flashMode).isEqualTo(ImageCapture.FLASH_MODE_OFF) 232 } 233 234 @Test 235 fun setFlashModeOn_aeModeSetAndRequestUpdated(): Unit = runBlocking { 236 assumeTrue(hasFlashUnit) 237 bindUseCase(imageAnalysis) 238 cameraControl.flashMode = ImageCapture.FLASH_MODE_ON 239 240 waitForResult(captureCount = 60) 241 .verify( 242 { requestMeta: RequestMetadata, _ -> 243 requestMeta.isAeMode(CONTROL_AE_MODE_ON_ALWAYS_FLASH) 244 }, 245 TIMEOUT 246 ) 247 Truth.assertThat(cameraControl.flashMode).isEqualTo(ImageCapture.FLASH_MODE_ON) 248 } 249 250 @Test 251 fun enableTorch_aeModeSetAndRequestUpdated(): Unit = runBlocking { 252 assumeTrue(hasFlashUnit) 253 bindUseCase(imageAnalysis) 254 cameraControl.enableTorch(true).await() 255 256 waitForResult(captureCount = 30) 257 .verify( 258 { requestMeta: RequestMetadata, frameInfo: FrameInfo -> 259 frameInfo.requestMetadata[FLASH_MODE] == FLASH_MODE_TORCH && 260 requestMeta.isAeMode(CONTROL_AE_MODE_ON) 261 }, 262 TIMEOUT 263 ) 264 } 265 266 @Test 267 fun disableTorchFlashModeAuto_aeModeSetAndRequestUpdated(): Unit = runBlocking { 268 assumeTrue(hasFlashUnit) 269 bindUseCase(imageAnalysis) 270 cameraControl.flashMode = ImageCapture.FLASH_MODE_AUTO 271 cameraControl.enableTorch(false).await() 272 273 waitForResult(captureCount = 30) 274 .verify( 275 { requestMeta: RequestMetadata, frameInfo: FrameInfo -> 276 frameInfo.requestMetadata[FLASH_MODE] != FLASH_MODE_TORCH && 277 requestMeta.isAeMode(CONTROL_AE_MODE_ON_AUTO_FLASH) 278 }, 279 TIMEOUT 280 ) 281 } 282 283 @Test 284 @SdkSuppress(minSdkVersion = 35) 285 fun enableLowLightBoost_aeModeSetAndRequestUpdated(): Unit = runBlocking { 286 assumeTrue(camera.cameraInfo.isLowLightBoostSupported) 287 bindUseCase(imageAnalysis) 288 cameraControl.enableLowLightBoostAsync(true).await() 289 290 waitForResult(captureCount = 30) 291 .verify( 292 { requestMeta: RequestMetadata, frameInfo: FrameInfo -> 293 requestMeta.isAeMode(CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY) && 294 frameInfo.requestMetadata[FLASH_MODE] == FLASH_MODE_OFF 295 }, 296 TIMEOUT 297 ) 298 } 299 300 @Test 301 @SdkSuppress(minSdkVersion = 35) 302 fun disableLowLightBoostFlashModeAuto_aeModeSetAndRequestUpdated(): Unit = runBlocking { 303 assumeTrue(camera.cameraInfo.isLowLightBoostSupported) 304 bindUseCase(imageAnalysis) 305 cameraControl.flashMode = ImageCapture.FLASH_MODE_AUTO 306 cameraControl.enableLowLightBoostAsync(false).await() 307 308 waitForResult(captureCount = 30) 309 .verify( 310 { requestMeta: RequestMetadata, _ -> 311 requestMeta.isAeMode(CONTROL_AE_MODE_ON_AUTO_FLASH) 312 }, 313 TIMEOUT 314 ) 315 } 316 317 @Test 318 fun startFocusAndMetering_3ARegionsUpdated() = runBlocking { 319 assumeTrue( 320 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AF) > 0 || 321 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AE) > 0 || 322 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AWB) > 0 323 ) 324 val factory = SurfaceOrientedMeteringPointFactory(1.0f, 1.0f) 325 val action = FocusMeteringAction.Builder(factory.createPoint(0f, 0f)).build() 326 bindUseCase(imageAnalysis) 327 328 // Act. 329 cameraControl.startFocusAndMetering(action).await() 330 331 // Assert. Here we verify only 3A region count is correct. 332 val expectedAfCount = 333 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AF).coerceAtMost(1) 334 val expectedAeCount = 335 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AE).coerceAtMost(1) 336 val expectedAwbCount = 337 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AWB).coerceAtMost(1) 338 waitForResult(captureCount = 60) 339 .verify( 340 { requestMeta: RequestMetadata, _ -> 341 // some devices may have a 0 weight region added by default, so 342 // weightedRegionCount 343 344 val afRegionMatched = 345 requestMeta 346 .getOrDefault(CONTROL_AF_REGIONS, emptyArray()) 347 .weightedRegionCount == expectedAfCount 348 349 val aeRegionMatched = 350 requestMeta 351 .getOrDefault(CONTROL_AE_REGIONS, emptyArray()) 352 .weightedRegionCount == expectedAeCount 353 354 val awbRegionMatched = 355 requestMeta 356 .getOrDefault(CONTROL_AWB_REGIONS, emptyArray()) 357 .weightedRegionCount == expectedAwbCount 358 359 afRegionMatched && aeRegionMatched && awbRegionMatched 360 }, 361 TIMEOUT 362 ) 363 } 364 365 @Test 366 fun cancelFocusAndMetering_3ARegionsReset() = runBlocking { 367 assumeTrue( 368 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AF) > 0 || 369 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AE) > 0 || 370 characteristics.getMaxRegionCount(CONTROL_MAX_REGIONS_AWB) > 0 371 ) 372 val factory = SurfaceOrientedMeteringPointFactory(1.0f, 1.0f) 373 val action = FocusMeteringAction.Builder(factory.createPoint(0f, 0f)).build() 374 bindUseCase(imageAnalysis) 375 376 // Act. 377 cameraControl.startFocusAndMetering(action).await() 378 cameraControl.cancelFocusAndMetering().await() 379 380 // Assert. The regions are reset to the default. 381 waitForResult(captureCount = 60) 382 .verify( 383 { requestMeta: RequestMetadata, _ -> 384 val isDefaultAfRegion = 385 requestMeta 386 .getOrDefault( 387 CONTROL_AF_REGIONS, 388 CameraGraph.Constants3A.METERING_REGIONS_DEFAULT 389 ) 390 .contentEquals(CameraGraph.Constants3A.METERING_REGIONS_DEFAULT) 391 392 val isDefaultAeRegion = 393 requestMeta 394 .getOrDefault( 395 CONTROL_AE_REGIONS, 396 CameraGraph.Constants3A.METERING_REGIONS_DEFAULT 397 ) 398 .contentEquals(CameraGraph.Constants3A.METERING_REGIONS_DEFAULT) 399 400 val isDefaultAwbRegion = 401 requestMeta 402 .getOrDefault( 403 CONTROL_AWB_REGIONS, 404 CameraGraph.Constants3A.METERING_REGIONS_DEFAULT 405 ) 406 .contentEquals(CameraGraph.Constants3A.METERING_REGIONS_DEFAULT) 407 408 isDefaultAfRegion && isDefaultAeRegion && isDefaultAwbRegion 409 }, 410 TIMEOUT 411 ) 412 } 413 414 @Test 415 fun setTemplatePreview_afModeToContinuousPicture() = runBlocking { 416 assumeTrue( 417 "Device does not support continuous picture AF mode, ignore the test", 418 characteristics.isAfModeSupported(CONTROL_AF_MODE_CONTINUOUS_PICTURE), 419 ) 420 421 bindUseCase(createPreview()) 422 423 // Assert. Verify the afMode. 424 waitForResult(captureCount = 60) 425 .verify( 426 { requestMeta: RequestMetadata, _ -> 427 requestMeta.isAfMode(CONTROL_AF_MODE_CONTINUOUS_PICTURE) 428 }, 429 TIMEOUT 430 ) 431 } 432 433 @Test 434 fun setTemplateRecord_afModeToContinuousVideo() = runBlocking { 435 assumeTrue( 436 "Device does not support continuous video AF mode, ignore the test", 437 characteristics.isAfModeSupported(CONTROL_AF_MODE_CONTINUOUS_VIDEO), 438 ) 439 440 bindUseCase(createFakeRecordingUseCase()) 441 442 // Assert. Verify the afMode. 443 waitForResult(captureCount = 60) 444 .verify( 445 { requestMeta: RequestMetadata, _ -> 446 requestMeta.isAfMode(CONTROL_AF_MODE_CONTINUOUS_VIDEO) 447 }, 448 TIMEOUT 449 ) 450 } 451 452 @Test 453 fun setZoomRatio_operationCanceledExceptionIfNoUseCase() { 454 val ratio = camera.getMaxSupportedZoomRatio() 455 assertFutureFailedWithOperationCancellation(cameraControl.setZoomRatio(ratio)) 456 } 457 458 private fun <T> assertFutureFailedWithOperationCancellation(future: ListenableFuture<T>) { 459 Assert.assertThrows(ExecutionException::class.java) { future[3, TimeUnit.SECONDS] } 460 .apply { 461 Truth.assertThat(cause) 462 .isInstanceOf(CameraControl.OperationCanceledException::class.java) 463 } 464 } 465 466 private fun CameraCharacteristics.getMaxRegionCount( 467 optionMaxRegions: CameraCharacteristics.Key<Int> 468 ) = get(optionMaxRegions) ?: 0 469 470 private suspend fun arrangeRequestOptions() { 471 cameraControl.setExposureCompensationIndex(1) 472 cameraControl.setZoomRatio(camera.getMaxSupportedZoomRatio()) 473 testEffectMode?.let { effectMode -> 474 cameraControl.camera2cameraControl 475 .setCaptureRequestOptions( 476 CaptureRequestOptions.Builder() 477 .setCaptureRequestOption(CONTROL_EFFECT_MODE, effectMode) 478 .build() 479 ) 480 .await() 481 } 482 483 // Ensure the requests are already set to the CaptureRequest. 484 waitForResult() 485 .verify( 486 { captureRequest: RequestMetadata, _ -> 487 // Ensure the EV working before testing 488 assumeThat( 489 "EV Request doesn't set to CaptureRequest, ignore the test", 490 captureRequest.request[CONTROL_AE_EXPOSURE_COMPENSATION], 491 equalTo(1) 492 ) 493 494 // Ensure the Camera2Interop working before testing 495 if (testEffectMode != null) { 496 assumeThat( 497 "Camera2Interop Request doesn't set to CaptureRequest, ignore the test", 498 captureRequest.request[CONTROL_EFFECT_MODE], 499 equalTo(testEffectMode) 500 ) 501 } 502 503 // Ensure the Zoom working before testing 504 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 505 assumeThat( 506 "Zoom Request doesn't set to CaptureRequest, ignore the test", 507 captureRequest.request[CONTROL_ZOOM_RATIO], 508 notNullValue() 509 ) 510 } else { 511 assumeThat( 512 "Zoom Request doesn't set to CaptureRequest, ignore the test", 513 captureRequest.request[SCALER_CROP_REGION], 514 notNullValue() 515 ) 516 } 517 return@verify true 518 }, 519 TIMEOUT 520 ) 521 } 522 523 private fun Camera.getTestEffectMode(): Int? { 524 return Camera2CameraInfo.from(cameraInfo) 525 .getCameraCharacteristic(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS) 526 ?.getOrNull(0) 527 } 528 529 private fun Camera.getMaxSupportedZoomRatio(): Float { 530 return cameraInfo.toCameraInfoAdapter().zoomState.value!!.maxZoomRatio 531 } 532 533 private suspend fun verifyRequestOptions() { 534 waitForResult(captureCount = 30) 535 .verify( 536 { metadata: RequestMetadata, _ -> 537 val checkEV = metadata.request[CONTROL_AE_EXPOSURE_COMPENSATION] == 1 538 val checkEffectMode = 539 testEffectMode?.let { metadata.request[CONTROL_EFFECT_MODE] == it } != false 540 val checkZoom = 541 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 542 metadata.request[CONTROL_ZOOM_RATIO] != null 543 } else { 544 metadata.request[SCALER_CROP_REGION] != null 545 } 546 547 checkEV && checkEffectMode && checkZoom 548 }, 549 TIMEOUT 550 ) 551 } 552 553 private fun waitForResult(captureCount: Int = 1): VerifyResultListener = 554 VerifyResultListener(captureCount).also { 555 comboListener.addListener(it, Dispatchers.Default.asExecutor()) 556 } 557 558 private fun bindUseCase(vararg useCases: UseCase) { 559 camera = 560 CameraUtil.createCameraAndAttachUseCase( 561 context, 562 cameraSelector, 563 *useCases, 564 ) 565 cameraControl = camera.cameraControl.toCameraControlAdapter() 566 } 567 568 private fun createFakeRecordingUseCase(): FakeUseCase { 569 return FakeTestUseCase( 570 FakeUseCaseConfig.Builder().setTargetName("FakeRecordingUseCase").useCaseConfig 571 ) 572 .apply { initAndActive() } 573 } 574 575 private class FakeTestUseCase(config: FakeUseCaseConfig) : FakeUseCase(config) { 576 577 val deferrableSurface = 578 object : DeferrableSurface() { 579 init { 580 terminationFuture.addListener({ cleanUp() }, Dispatchers.IO.asExecutor()) 581 } 582 583 private val surfaceTexture = 584 SurfaceTexture(0).also { it.setDefaultBufferSize(640, 480) } 585 val testSurface = Surface(surfaceTexture) 586 587 override fun provideSurface(): ListenableFuture<Surface> { 588 return Futures.immediateFuture(testSurface) 589 } 590 591 fun cleanUp() { 592 testSurface.release() 593 surfaceTexture.release() 594 } 595 } 596 597 fun initAndActive() { 598 val sessionConfigBuilder = 599 SessionConfig.Builder().apply { 600 setTemplateType(CameraDevice.TEMPLATE_RECORD) 601 addSurface(deferrableSurface) 602 } 603 604 updateSessionConfig(listOf(sessionConfigBuilder.build())) 605 notifyActive() 606 } 607 608 override fun onUnbind() { 609 super.onUnbind() 610 deferrableSurface.close() 611 } 612 } 613 614 private suspend fun createPreview(): Preview = 615 Preview.Builder().build().also { preview -> 616 withContext(Dispatchers.Main) { preview.surfaceProvider = getSurfaceProvider() } 617 } 618 619 private fun getSurfaceProvider(): Preview.SurfaceProvider { 620 return SurfaceTextureProvider.createSurfaceTextureProvider( 621 object : SurfaceTextureProvider.SurfaceTextureCallback { 622 override fun onSurfaceTextureReady( 623 surfaceTexture: SurfaceTexture, 624 resolution: Size 625 ) { 626 // No-op 627 } 628 629 override fun onSafeToRelease(surfaceTexture: SurfaceTexture) { 630 surfaceTexture.release() 631 } 632 } 633 ) 634 } 635 636 private fun RequestMetadata.isAfMode(afMode: Int): Boolean { 637 return if (characteristics.isAfModeSupported(afMode)) { 638 getOrDefault(CONTROL_AF_MODE, null) == afMode 639 } else { 640 val fallbackMode = 641 if (characteristics.isAfModeSupported(CONTROL_AF_MODE_CONTINUOUS_PICTURE)) { 642 CONTROL_AF_MODE_CONTINUOUS_PICTURE 643 } else if (characteristics.isAfModeSupported(CONTROL_AF_MODE_AUTO)) { 644 CONTROL_AF_MODE_AUTO 645 } else { 646 CONTROL_AF_MODE_OFF 647 } 648 getOrDefault(CONTROL_AF_MODE, null) == fallbackMode 649 } 650 } 651 652 private fun RequestMetadata.isAeMode(aeMode: Int): Boolean { 653 val aeQuirkEnabled = 654 camera.getCameraQuirks().contains(ImageCaptureFailWithAutoFlashQuirk::class.java) || 655 DeviceQuirks[CrashWhenTakingPhotoWithAutoFlashAEModeQuirk::class.java] != null 656 val aeModeCorrected = 657 if (aeQuirkEnabled) AutoFlashAEModeDisablerImpl.getCorrectedAeMode(aeMode) else aeMode 658 659 return if (characteristics.isAeModeSupported(aeModeCorrected)) { 660 getOrDefault(CONTROL_AE_MODE, null) == aeModeCorrected 661 } else { 662 val fallbackMode = 663 if (characteristics.isAeModeSupported(CONTROL_AE_MODE_ON)) { 664 CONTROL_AE_MODE_ON 665 } else { 666 CONTROL_AE_MODE_OFF 667 } 668 getOrDefault(CONTROL_AE_MODE, null) == fallbackMode 669 } 670 } 671 672 private fun Camera.getCameraQuirks(): Quirks { 673 return cameraInfo.toCameraInfoAdapter().cameraQuirks 674 } 675 676 private fun CameraCharacteristics.isAfModeSupported(afMode: Int) = 677 (get(CONTROL_AF_AVAILABLE_MODES) ?: intArrayOf(-1)).contains(afMode) 678 679 private fun CameraCharacteristics.isAeModeSupported(aeMode: Int) = 680 (get(CONTROL_AE_AVAILABLE_MODES) ?: intArrayOf(-1)).contains(aeMode) 681 682 /** Returns the number of metering regions whose weight is greater than 0. */ 683 private val Array<MeteringRectangle>.weightedRegionCount: Int 684 get() { 685 var count = 0 686 forEach { count += if (it.meteringWeight != 0) 1 else 0 } 687 return count 688 } 689 } 690