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.internal
18 
19 import android.content.Context
20 import android.graphics.SurfaceTexture
21 import android.hardware.camera2.CameraCaptureSession
22 import android.hardware.camera2.CameraCharacteristics
23 import android.hardware.camera2.CameraDevice
24 import android.hardware.camera2.CameraManager
25 import android.hardware.camera2.CameraMetadata
26 import android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY
27 import android.hardware.camera2.CaptureRequest
28 import android.hardware.camera2.CaptureResult
29 import android.hardware.camera2.TotalCaptureResult
30 import android.media.Image
31 import android.media.ImageWriter
32 import android.os.Build
33 import android.view.Surface
34 import androidx.annotation.RequiresApi
35 import androidx.camera.camera2.impl.Camera2ImplConfig
36 import androidx.camera.camera2.internal.Camera2CapturePipeline.ScreenFlashTask
37 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat
38 import androidx.camera.camera2.internal.compat.quirk.AutoFlashUnderExposedQuirk
39 import androidx.camera.camera2.internal.compat.quirk.CameraQuirks
40 import androidx.camera.camera2.internal.compat.quirk.TorchFlashRequiredFor3aUpdateQuirk
41 import androidx.camera.camera2.internal.compat.quirk.UseTorchAsFlashQuirk
42 import androidx.camera.camera2.internal.compat.workaround.OverrideAeModeForStillCapture
43 import androidx.camera.core.ImageCapture
44 import androidx.camera.core.ImageCapture.FLASH_MODE_AUTO
45 import androidx.camera.core.ImageCapture.FLASH_MODE_OFF
46 import androidx.camera.core.ImageCapture.FLASH_MODE_ON
47 import androidx.camera.core.ImageCaptureException
48 import androidx.camera.core.impl.CameraCaptureFailure
49 import androidx.camera.core.impl.CameraCaptureMetaData.AeState
50 import androidx.camera.core.impl.CameraCaptureMetaData.AfState
51 import androidx.camera.core.impl.CameraCaptureMetaData.AwbState
52 import androidx.camera.core.impl.CameraCaptureResult
53 import androidx.camera.core.impl.CameraControlInternal
54 import androidx.camera.core.impl.CaptureConfig
55 import androidx.camera.core.impl.DeferrableSurface
56 import androidx.camera.core.impl.ImmediateSurface
57 import androidx.camera.core.impl.Quirk
58 import androidx.camera.core.impl.Quirks
59 import androidx.camera.core.impl.SessionConfig
60 import androidx.camera.core.impl.utils.executor.CameraXExecutors
61 import androidx.camera.core.impl.utils.futures.Futures
62 import androidx.camera.core.internal.CameraCaptureResultImageInfo
63 import androidx.camera.testing.fakes.FakeCameraCaptureResult
64 import androidx.camera.testing.impl.fakes.FakeImageProxy
65 import androidx.camera.testing.impl.mocks.MockScreenFlash
66 import androidx.concurrent.futures.await
67 import androidx.test.core.app.ApplicationProvider
68 import com.google.common.truth.Truth
69 import com.google.common.truth.Truth.assertThat
70 import com.google.common.util.concurrent.ListenableFuture
71 import java.util.concurrent.CountDownLatch
72 import java.util.concurrent.ExecutionException
73 import java.util.concurrent.Executor
74 import java.util.concurrent.Executors
75 import java.util.concurrent.ScheduledFuture
76 import java.util.concurrent.TimeUnit
77 import java.util.concurrent.TimeoutException
78 import kotlinx.coroutines.runBlocking
79 import kotlinx.coroutines.withTimeout
80 import org.junit.After
81 import org.junit.Assert.assertThrows
82 import org.junit.Assume.assumeTrue
83 import org.junit.Before
84 import org.junit.Ignore
85 import org.junit.Test
86 import org.junit.runner.RunWith
87 import org.mockito.Mockito
88 import org.mockito.Mockito.mock
89 import org.robolectric.ParameterizedRobolectricTestRunner
90 import org.robolectric.annotation.Config
91 import org.robolectric.annotation.internal.DoNotInstrument
92 import org.robolectric.shadow.api.Shadow
93 import org.robolectric.shadows.ShadowCameraCharacteristics
94 import org.robolectric.shadows.ShadowCameraManager
95 
96 private const val CAMERA_ID_0 = "0"
97 
98 @RunWith(ParameterizedRobolectricTestRunner::class)
99 @DoNotInstrument
100 @Config(
101     minSdk = Build.VERSION_CODES.LOLLIPOP,
102 )
103 class Camera2CapturePipelineTest(private val isLowLightBoostEnabled: Boolean) {
104 
105     private val context = ApplicationProvider.getApplicationContext() as Context
106     private val executorService = Executors.newSingleThreadScheduledExecutor()
107 
108     private val baseRepeatingResult: Map<CaptureResult.Key<*>, Any> =
109         mapOf(
110             CaptureResult.CONTROL_MODE to CaptureResult.CONTROL_MODE_AUTO,
111             CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_AUTO,
112             CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_CONVERGED,
113             CaptureResult.CONTROL_AWB_MODE to CaptureResult.CONTROL_AWB_MODE_AUTO,
114             CaptureResult.CONTROL_AE_MODE to CaptureResult.CONTROL_AE_MODE_ON,
115         )
116 
117     private val resultConverged: Map<CaptureResult.Key<*>, Any> =
118         mapOf(
119             CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_AUTO,
120             CaptureResult.CONTROL_AF_STATE to CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED,
121             CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_CONVERGED,
122             CaptureResult.CONTROL_AWB_STATE to CaptureResult.CONTROL_AWB_STATE_CONVERGED,
123         )
124 
125     private val resultConvergedWith3AModeOff: Map<CaptureResult.Key<*>, Any> =
126         mapOf(
127             CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_OFF,
128             CaptureResult.CONTROL_AE_MODE to CaptureResult.CONTROL_AE_MODE_OFF,
129             CaptureResult.CONTROL_AWB_MODE to CaptureResult.CONTROL_AWB_MODE_OFF,
130             CaptureResult.CONTROL_AF_STATE to CaptureResult.CONTROL_AF_STATE_INACTIVE,
131             CaptureResult.CONTROL_AE_STATE to CaptureResult.CONTROL_AE_STATE_INACTIVE,
132             CaptureResult.CONTROL_AWB_STATE to CaptureResult.CONTROL_AWB_STATE_INACTIVE,
133         )
134 
135     private val fakeStillCaptureSurface = ImmediateSurface(Surface(SurfaceTexture(0)))
136 
137     private val singleRequest =
138         CaptureConfig.Builder()
139             .apply {
140                 templateType = CameraDevice.TEMPLATE_STILL_CAPTURE
141                 addSurface(fakeStillCaptureSurface)
142             }
143             .build()
144 
145     private var runningRepeatingStream: ScheduledFuture<*>? = null
146         set(value) {
147             runningRepeatingStream?.cancel(false)
148             field = value
149         }
150 
151     private lateinit var testScreenFlash: MockScreenFlash
152 
153     companion object {
154         @JvmStatic
155         @ParameterizedRobolectricTestRunner.Parameters(name = "isLowLightBoostEnabled: {0}")
156         fun data() = listOf(true, false)
157     }
158 
159     @Before
160     fun setUp() {
161         if (isLowLightBoostEnabled) {
162             assumeTrue(Build.VERSION.SDK_INT >= 35)
163         }
164         initCameras()
165         testScreenFlash = MockScreenFlash()
166     }
167 
168     @After
169     fun tearDown() {
170         runningRepeatingStream = null
171         fakeStillCaptureSurface.close()
172         executorService.shutdown()
173     }
174 
175     @Ignore // b/228856476
176     @Test
177     fun pipelineTest_preCapturePostCaptureShouldCalled() {
178         // Arrange.
179         val fakeTask =
180             object : Camera2CapturePipeline.PipelineTask {
181                 val preCaptureCountDown = CountDownLatch(1)
182                 val postCaptureCountDown = CountDownLatch(1)
183 
184                 override fun preCapture(
185                     captureResult: TotalCaptureResult?
186                 ): ListenableFuture<Boolean> {
187                     preCaptureCountDown.countDown()
188                     return Futures.immediateFuture(false)
189                 }
190 
191                 override fun isCaptureResultNeeded(): Boolean {
192                     return false
193                 }
194 
195                 override fun postCapture() {
196                     postCaptureCountDown.countDown()
197                 }
198             }
199 
200         val cameraControl =
201             createCameraControl().apply {
202                 simulateRepeatingResult(resultParameters = resultConverged)
203             }
204 
205         val pipeline =
206             Camera2CapturePipeline.Pipeline(
207                     CameraDevice.TEMPLATE_PREVIEW,
208                     executorService,
209                     executorService,
210                     cameraControl,
211                     false,
212                     OverrideAeModeForStillCapture(Quirks(emptyList())),
213                 )
214                 .apply { addTask(fakeTask) }
215 
216         // Act.
217         pipeline.executeCapture(
218             listOf(singleRequest),
219             FLASH_MODE_OFF,
220         )
221 
222         // Assert.
223         assertThat(fakeTask.preCaptureCountDown.await(3, TimeUnit.SECONDS)).isTrue()
224         assertThat(fakeTask.postCaptureCountDown.await(3, TimeUnit.SECONDS)).isTrue()
225     }
226 
227     @Test
228     fun maxQuality_afInactive_shouldTriggerAf(): Unit = runBlocking {
229         val cameraControl =
230             createCameraControl().apply {
231 
232                 // Arrange. Simulate the scenario that we need to triggerAF.
233                 simulateRepeatingResult(
234                     initialDelay = 100,
235                     resultParameters =
236                         mapOf(
237                             CaptureResult.CONTROL_AF_MODE to CaptureResult.CONTROL_AF_MODE_AUTO,
238                             CaptureResult.CONTROL_AF_STATE to
239                                 CaptureResult.CONTROL_AF_STATE_INACTIVE,
240                         )
241                 )
242 
243                 // Act.
244                 submitStillCaptureRequests(
245                     listOf(singleRequest),
246                     ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY,
247                     ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
248                 )
249             }
250 
251         // Assert 1, verify the CONTROL_AF_TRIGGER is triggered
252         immediateCompleteCapture.verifyRequestResult {
253             it.requestContains(
254                 CaptureRequest.CONTROL_AF_TRIGGER,
255                 CaptureRequest.CONTROL_AF_TRIGGER_START
256             )
257         }
258 
259         // Switch the repeating result to 3A converged state.
260         cameraControl.simulateRepeatingResult(
261             initialDelay = 500,
262             resultParameters = resultConverged
263         )
264 
265         // Assert 2, that CONTROL_AF_TRIGGER should be cancelled finally.
266         immediateCompleteCapture.verifyRequestResult {
267             it.requestContains(
268                 CaptureRequest.CONTROL_AF_TRIGGER,
269                 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL
270             )
271         }
272     }
273 
274     @Test
275     fun miniLatency_flashOn_shouldTriggerAe() {
276         flashOn_shouldTriggerAe(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
277     }
278 
279     @Test
280     fun maxQuality_flashOn_shouldTriggerAe() {
281         flashOn_shouldTriggerAe(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
282     }
283 
284     private fun flashOn_shouldTriggerAe(imageCaptureMode: Int) {
285         val cameraControl =
286             createCameraControl().apply {
287                 // Arrange.
288                 flashMode = FLASH_MODE_ON
289 
290                 // Act.
291                 submitStillCaptureRequests(
292                     listOf(singleRequest),
293                     imageCaptureMode,
294                     ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
295                 )
296                 simulateRepeatingResult(initialDelay = 100)
297             }
298 
299         // Assert 1, verify the CONTROL_AE_PRECAPTURE_TRIGGER is triggered when low-light boost is
300         // off, otherwise, is not triggered.
301         immediateCompleteCapture.verifyRequestResult {
302             it.requestContains(
303                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
304                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START
305             ) != isLowLightBoostEnabled
306         }
307 
308         // When low-light boost is on, AE pre-capture is not triggered. Therefore, converged stage
309         // is not required.
310         if (isLowLightBoostEnabled) {
311             return
312         }
313 
314         // Switch the repeating result to 3A converged state.
315         cameraControl.simulateRepeatingResult(
316             initialDelay = 500,
317             resultParameters = resultConverged
318         )
319 
320         // Assert 2 that CONTROL_AE_PRECAPTURE_TRIGGER should be cancelled finally when low-light
321         // boost is off, otherwise, TRIGGER_CANCEL is not triggered.
322         if (Build.VERSION.SDK_INT >= 23) {
323             immediateCompleteCapture.verifyRequestResult {
324                 it.requestContains(
325                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
326                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
327                 )
328             }
329         }
330     }
331 
332     @Test
333     fun miniLatency_flashAutoFlashRequired_shouldTriggerAe() {
334         flashAutoFlashRequired_shouldTriggerAe(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
335     }
336 
337     @Test
338     fun maxQuality_flashAutoFlashRequired_shouldTriggerAe(): Unit = runBlocking {
339         flashAutoFlashRequired_shouldTriggerAe(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
340     }
341 
342     private fun flashAutoFlashRequired_shouldTriggerAe(imageCaptureMode: Int) {
343         val cameraControl =
344             createCameraControl().apply {
345                 // Arrange.
346                 flashMode = FLASH_MODE_AUTO
347                 simulateRepeatingResult(
348                     initialDelay = 100,
349                     resultParameters =
350                         mapOf(
351                             CaptureResult.CONTROL_AE_STATE to
352                                 CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED,
353                         )
354                 )
355 
356                 // Act.
357                 submitStillCaptureRequests(
358                     listOf(singleRequest),
359                     imageCaptureMode,
360                     ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
361                 )
362             }
363 
364         // Assert 1, verify the CONTROL_AE_PRECAPTURE_TRIGGER is triggered when low-light boost is
365         // off, otherwise, is not triggered.
366         immediateCompleteCapture.verifyRequestResult {
367             it.requestContains(
368                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
369                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START
370             ) != isLowLightBoostEnabled
371         }
372 
373         // When low-light boost is on, AE pre-capture is not triggered. Therefore, converged stage
374         // is not required.
375         if (isLowLightBoostEnabled) {
376             return
377         }
378 
379         // Switch the repeating result to 3A converged state.
380         cameraControl.simulateRepeatingResult(
381             initialDelay = 500,
382             resultParameters = resultConverged
383         )
384 
385         // Assert 2 that CONTROL_AE_PRECAPTURE_TRIGGER should be cancelled finally when low-light
386         // boost is off, otherwise, TRIGGER_CANCEL is not triggered.
387         if (Build.VERSION.SDK_INT >= 23) {
388             immediateCompleteCapture.verifyRequestResult {
389                 it.requestContains(
390                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
391                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
392                 )
393             }
394         }
395     }
396 
397     @Test
398     fun createPipeline_screenFlashTaskAdded() {
399         val camera2CapturePipeline =
400             Camera2CapturePipeline(
401                 createCameraControl(),
402                 CameraCharacteristicsCompat.toCameraCharacteristicsCompat(
403                     ShadowCameraCharacteristics.newCameraCharacteristics(),
404                     CAMERA_ID_0,
405                 ),
406                 Quirks(emptyList()),
407                 CameraXExecutors.directExecutor(),
408                 CameraXExecutors.myLooperExecutor(),
409             )
410 
411         val pipeline =
412             camera2CapturePipeline.createPipeline(
413                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
414                 ImageCapture.FLASH_MODE_SCREEN,
415                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH
416             )
417 
418         var hasScreenFlashTask = false
419         pipeline.mTasks.forEach { task ->
420             if (task is ScreenFlashTask) {
421                 hasScreenFlashTask = true
422             }
423         }
424 
425         assertThat(hasScreenFlashTask).isTrue()
426     }
427 
428     @Test
429     fun miniLatency_withTorchAsFlashQuirk_shouldOpenTorch() {
430         withTorchAsFlashQuirk_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
431     }
432 
433     @Test
434     fun maxQuality_withTorchAsFlashQuirk_shouldOpenTorch() {
435         withTorchAsFlashQuirk_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
436     }
437 
438     private fun withTorchAsFlashQuirk_shouldOpenTorch(imageCaptureMode: Int) {
439         val cameraControl =
440             createCameraControl(
441                     // Arrange.
442                     quirks = Quirks(listOf(object : UseTorchAsFlashQuirk {}))
443                 )
444                 .apply {
445                     flashMode = FLASH_MODE_ON
446                     simulateRepeatingResult(initialDelay = 100)
447 
448                     // Act.
449                     submitStillCaptureRequests(
450                         listOf(singleRequest),
451                         imageCaptureMode,
452                         ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
453                     )
454                 }
455 
456         // Assert 1 torch should be turned on
457         cameraControl.waitForSessionConfig {
458             if (isLowLightBoostEnabled) {
459                 it.isLowLightBoostEnabled()
460             } else {
461                 it.isTorchParameterEnabled()
462             }
463         }
464 
465         // When low-light boost is on, AE pre-capture is not triggered. Therefore, converged stage
466         // is not required.
467         if (isLowLightBoostEnabled) {
468             return
469         }
470 
471         // Switch the repeating result to 3A converged state.
472         cameraControl.simulateRepeatingResult(
473             initialDelay = 500,
474             resultParameters = resultConverged
475         )
476 
477         // Assert 2 torch should be turned off
478         immediateCompleteCapture.verifyRequestResult { it.isTorchParameterDisabled() }
479     }
480 
481     @Test
482     fun miniLatency_withTemplateRecord_shouldOpenTorch() {
483         withTemplateRecord_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
484     }
485 
486     @Test
487     fun maxQuality_withTemplateRecord_shouldOpenTorch() {
488         withTemplateRecord_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
489     }
490 
491     private fun withTemplateRecord_shouldOpenTorch(imageCaptureMode: Int) {
492 
493         val cameraControl =
494             createCameraControl().apply {
495                 // Arrange.
496                 setTemplate(CameraDevice.TEMPLATE_RECORD)
497                 flashMode = FLASH_MODE_ON
498                 simulateRepeatingResult(initialDelay = 100)
499                 submitStillCaptureRequests(
500                     listOf(singleRequest),
501                     imageCaptureMode,
502                     ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
503                 )
504             }
505 
506         // Assert 1 torch should be turned on
507         cameraControl.waitForSessionConfig {
508             if (isLowLightBoostEnabled) {
509                 it.isLowLightBoostEnabled()
510             } else {
511                 it.isTorchParameterEnabled()
512             }
513         }
514 
515         // When low-light boost is on, AE pre-capture is not triggered. Therefore, converged stage
516         // is not required.
517         if (isLowLightBoostEnabled) {
518             return
519         }
520 
521         // Switch the repeating result to 3A converged state.
522         cameraControl.simulateRepeatingResult(
523             initialDelay = 500,
524             resultParameters = resultConverged
525         )
526 
527         // Assert 2 torch should be turned off
528         immediateCompleteCapture.verifyRequestResult { it.isTorchParameterDisabled() }
529     }
530 
531     @Test
532     fun miniLatency_withFlashTypeTorch_shouldOpenTorch() {
533         withFlashTypeTorch_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
534     }
535 
536     @Test
537     fun maxQuality_withFlashTypeTorch_shouldOpenTorch() {
538         withFlashTypeTorch_shouldOpenTorch(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
539     }
540 
541     private fun withFlashTypeTorch_shouldOpenTorch(imageCaptureMode: Int) {
542         val cameraControl =
543             createCameraControl().apply {
544                 flashMode = FLASH_MODE_ON
545                 simulateRepeatingResult(initialDelay = 100)
546                 submitStillCaptureRequests(
547                     listOf(singleRequest),
548                     imageCaptureMode,
549                     ImageCapture.FLASH_TYPE_USE_TORCH_AS_FLASH,
550                 )
551             }
552 
553         // Assert 1 torch should be turned on
554         cameraControl.waitForSessionConfig {
555             if (isLowLightBoostEnabled) {
556                 it.isLowLightBoostEnabled()
557             } else {
558                 it.isTorchParameterEnabled()
559             }
560         }
561 
562         // When low-light boost is on, AE pre-capture is not triggered. Therefore, converged stage
563         // is not required.
564         if (isLowLightBoostEnabled) {
565             return
566         }
567 
568         // Switch the repeating result to 3A converged state.
569         cameraControl.simulateRepeatingResult(
570             initialDelay = 500,
571             resultParameters = resultConverged
572         )
573 
574         // Assert 2 torch should be turned off
575         immediateCompleteCapture.verifyRequestResult { it.isTorchParameterDisabled() }
576     }
577 
578     @Test
579     fun miniLatency_shouldNoPreCapture(): Unit = runBlocking {
580         // Arrange.
581         val cameraControl =
582             createCameraControl().apply { simulateRepeatingResult(initialDelay = 100) }
583 
584         // Act.
585         cameraControl
586             .submitStillCaptureRequests(
587                 listOf(singleRequest),
588                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
589                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
590             )
591             .await()
592 
593         // Assert, there is only 1 single capture request.
594         assertThat(immediateCompleteCapture.getAllResults().size).isEqualTo(1)
595     }
596 
597     @Test
598     fun submitStillCaptureRequests_withTemplate_templateSent(): Unit = runBlocking {
599         // Arrange.
600         val imageCaptureConfig =
601             CaptureConfig.Builder().let {
602                 it.addSurface(fakeStillCaptureSurface)
603                 it.templateType = CameraDevice.TEMPLATE_MANUAL
604                 it.build()
605             }
606         val cameraControl =
607             createCameraControl().apply { simulateRepeatingResult(initialDelay = 100) }
608 
609         // Act.
610         cameraControl
611             .submitStillCaptureRequests(
612                 listOf(imageCaptureConfig),
613                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
614                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
615             )
616             .await()
617 
618         // Assert.
619         immediateCompleteCapture.verifyRequestResult { captureConfigList ->
620             captureConfigList
621                 .filter { it.surfaces.contains(fakeStillCaptureSurface) }
622                 .map { captureConfig -> captureConfig.templateType }
623                 .contains(CameraDevice.TEMPLATE_MANUAL)
624         }
625     }
626 
627     @Test
628     fun submitStillCaptureRequests_withNoTemplate_templateStillCaptureSent(): Unit = runBlocking {
629         // Arrange.
630         val imageCaptureConfig =
631             CaptureConfig.Builder().apply { addSurface(fakeStillCaptureSurface) }.build()
632         val cameraControl =
633             createCameraControl().apply { simulateRepeatingResult(initialDelay = 100) }
634 
635         // Act.
636         cameraControl
637             .submitStillCaptureRequests(
638                 listOf(imageCaptureConfig),
639                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
640                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
641             )
642             .await()
643 
644         // Assert.
645         immediateCompleteCapture.verifyRequestResult { captureConfigList ->
646             captureConfigList
647                 .filter { it.surfaces.contains(fakeStillCaptureSurface) }
648                 .map { captureConfig -> captureConfig.templateType }
649                 .contains(CameraDevice.TEMPLATE_STILL_CAPTURE)
650         }
651     }
652 
653     @Test
654     fun submitStillCaptureRequests_withTemplateRecord_templateVideoSnapshotSent(): Unit =
655         runBlocking {
656             createCameraControl().apply {
657                 // Arrange.
658                 setTemplate(CameraDevice.TEMPLATE_RECORD)
659                 simulateRepeatingResult(initialDelay = 100)
660 
661                 // Act.
662                 submitStillCaptureRequests(
663                         listOf(singleRequest),
664                         ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
665                         ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
666                     )
667                     .await()
668             }
669 
670             // Assert.
671             immediateCompleteCapture.verifyRequestResult { captureConfigList ->
672                 captureConfigList
673                     .filter { it.surfaces.contains(fakeStillCaptureSurface) }
674                     .map { captureConfig -> captureConfig.templateType }
675                     .contains(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT)
676             }
677         }
678 
679     @Config(minSdk = 23)
680     @Test
681     fun submitZslCaptureRequests_withZslTemplate_templateZeroShutterLagSent(): Unit = runBlocking {
682         // Arrange.
683         val imageCaptureConfig =
684             CaptureConfig.Builder().let {
685                 it.addSurface(fakeStillCaptureSurface)
686                 it.templateType = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
687                 it.build()
688             }
689 
690         val cameraControl =
691             initCameraControlWithZsl(
692                 isZslDisabledByFlashMode = false,
693                 isZslDisabledByUserCaseConfig = false
694             )
695 
696         // Act.
697         cameraControl
698             .submitStillCaptureRequests(
699                 listOf(imageCaptureConfig),
700                 ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG,
701                 FLASH_MODE_OFF,
702             )
703             .await()
704 
705         // Assert.
706         val templateTypeToVerify =
707             if (Build.VERSION.SDK_INT >= 23) CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
708             else CameraDevice.TEMPLATE_STILL_CAPTURE
709 
710         immediateCompleteCapture.verifyRequestResult { captureConfigList ->
711             captureConfigList
712                 .filter { it.surfaces.contains(fakeStillCaptureSurface) }
713                 .map { captureConfig -> captureConfig.templateType }
714                 .contains(templateTypeToVerify)
715         }
716     }
717 
718     @Config(minSdk = 23)
719     @Test
720     fun submitZslCaptureRequests_withZslDisabledByFlashMode_templateStillPictureSent(): Unit =
721         runBlocking {
722             // Arrange.
723             val imageCaptureConfig =
724                 CaptureConfig.Builder().let {
725                     it.addSurface(fakeStillCaptureSurface)
726                     it.templateType = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
727                     it.build()
728                 }
729 
730             val cameraControl =
731                 initCameraControlWithZsl(
732                     isZslDisabledByFlashMode = true,
733                     isZslDisabledByUserCaseConfig = false
734                 )
735 
736             // Act.
737             cameraControl
738                 .submitStillCaptureRequests(
739                     listOf(imageCaptureConfig),
740                     ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG,
741                     FLASH_MODE_OFF,
742                 )
743                 .await()
744 
745             // Assert.
746             immediateCompleteCapture.verifyRequestResult { captureConfigList ->
747                 captureConfigList
748                     .filter { it.surfaces.contains(fakeStillCaptureSurface) }
749                     .map { captureConfig -> captureConfig.templateType }
750                     .contains(CameraDevice.TEMPLATE_STILL_CAPTURE)
751             }
752         }
753 
754     @Config(minSdk = 23)
755     @Test
756     fun submitZslCaptureRequests_withZslDisabledByUseCaseConfig_templateStillPictureSent(): Unit =
757         runBlocking {
758             // Arrange.
759             val imageCaptureConfig =
760                 CaptureConfig.Builder().let {
761                     it.addSurface(fakeStillCaptureSurface)
762                     it.templateType = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
763                     it.build()
764                 }
765 
766             val cameraControl =
767                 initCameraControlWithZsl(
768                     isZslDisabledByFlashMode = false,
769                     isZslDisabledByUserCaseConfig = true
770                 )
771 
772             // Act.
773             cameraControl
774                 .submitStillCaptureRequests(
775                     listOf(imageCaptureConfig),
776                     ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG,
777                     FLASH_MODE_OFF,
778                 )
779                 .await()
780 
781             // Assert.
782             immediateCompleteCapture.verifyRequestResult { captureConfigList ->
783                 captureConfigList
784                     .filter { it.surfaces.contains(fakeStillCaptureSurface) }
785                     .map { captureConfig -> captureConfig.templateType }
786                     .contains(CameraDevice.TEMPLATE_STILL_CAPTURE)
787             }
788         }
789 
790     @Config(minSdk = 23)
791     @Test
792     fun submitZslCaptureRequests_withNoTemplate_templateStillPictureSent(): Unit = runBlocking {
793         // Arrange.
794         val imageCaptureConfig =
795             CaptureConfig.Builder().let {
796                 it.addSurface(fakeStillCaptureSurface)
797                 it.build()
798             }
799         val cameraControl =
800             initCameraControlWithZsl(
801                 isZslDisabledByFlashMode = false,
802                 isZslDisabledByUserCaseConfig = false
803             )
804 
805         // Act.
806         cameraControl
807             .submitStillCaptureRequests(
808                 listOf(imageCaptureConfig),
809                 ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG,
810                 FLASH_MODE_OFF,
811             )
812             .await()
813 
814         // Assert.
815         immediateCompleteCapture.verifyRequestResult { captureConfigList ->
816             captureConfigList
817                 .filter { it.surfaces.contains(fakeStillCaptureSurface) }
818                 .map { captureConfig -> captureConfig.templateType }
819                 .contains(CameraDevice.TEMPLATE_STILL_CAPTURE)
820         }
821     }
822 
823     @Test
824     fun captureFailure_taskShouldFailure() {
825         // Arrange.
826         val immediateFailureCapture =
827             object : CameraControlInternal.ControlUpdateCallback {
828 
829                 override fun onCameraControlUpdateSessionConfig() {}
830 
831                 override fun onCameraControlCaptureRequests(
832                     captureConfigs: MutableList<CaptureConfig>
833                 ) {
834                     captureConfigs.forEach { captureConfig ->
835                         captureConfig.cameraCaptureCallbacks.forEach {
836                             it.onCaptureFailed(
837                                 CaptureConfig.DEFAULT_ID,
838                                 CameraCaptureFailure(CameraCaptureFailure.Reason.ERROR)
839                             )
840                         }
841                     }
842                 }
843             }
844         val cameraControl = createCameraControl(updateCallback = immediateFailureCapture)
845 
846         // Act.
847         val future =
848             cameraControl.submitStillCaptureRequests(
849                 listOf(CaptureConfig.Builder().build()),
850                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
851                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
852             )
853 
854         // Assert.
855         val exception =
856             assertThrows(ExecutionException::class.java) { future.get(1, TimeUnit.SECONDS) }
857         assertThat(exception.cause).isInstanceOf(ImageCaptureException::class.java)
858         assertThat((exception.cause as ImageCaptureException).imageCaptureError)
859             .isEqualTo(ImageCapture.ERROR_CAPTURE_FAILED)
860     }
861 
862     @Test
863     fun captureCancel_taskShouldFailureWithCAMERA_CLOSED() {
864         // Arrange.
865         val immediateCancelCapture =
866             object : CameraControlInternal.ControlUpdateCallback {
867 
868                 override fun onCameraControlUpdateSessionConfig() {}
869 
870                 override fun onCameraControlCaptureRequests(
871                     captureConfigs: MutableList<CaptureConfig>
872                 ) {
873                     captureConfigs.forEach { captureConfig ->
874                         captureConfig.cameraCaptureCallbacks.forEach {
875                             it.onCaptureCancelled(CaptureConfig.DEFAULT_ID)
876                         }
877                     }
878                 }
879             }
880         val cameraControl = createCameraControl(updateCallback = immediateCancelCapture)
881 
882         // Act.
883         val future =
884             cameraControl.submitStillCaptureRequests(
885                 listOf(CaptureConfig.Builder().build()),
886                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
887                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
888             )
889 
890         // Assert.
891         val exception =
892             assertThrows(ExecutionException::class.java) { future.get(1, TimeUnit.SECONDS) }
893         assertThat(exception.cause).isInstanceOf(ImageCaptureException::class.java)
894         assertThat((exception.cause as ImageCaptureException).imageCaptureError)
895             .isEqualTo(ImageCapture.ERROR_CAMERA_CLOSED)
896     }
897 
898     @Test
899     fun overrideAeModeForStillCapture_quirkAbsent_notOverride(): Unit = runBlocking {
900         // Arrange. Not have the quirk.
901         val cameraControl =
902             createCameraControl(quirks = Quirks(emptyList())).apply {
903                 flashMode = FLASH_MODE_ON // Set flash ON to enable aePreCapture
904                 simulateRepeatingResult(initialDelay = 100) // Make sures flashMode is updated.
905             }
906 
907         // Act.
908         val deferred =
909             cameraControl.submitStillCaptureRequests(
910                 listOf(singleRequest),
911                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
912                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
913             )
914         // Switch the repeating result to 3A converged state.
915         cameraControl.simulateRepeatingResult(
916             initialDelay = 500,
917             resultParameters = resultConverged
918         )
919 
920         deferred.await()
921 
922         // Assert.
923         // AE mode should not be overridden
924         immediateCompleteCapture
925             .getAllResults()
926             .flatten()
927             .filter { it.surfaces.contains(fakeStillCaptureSurface) }
928             .let { stillCaptureRequests ->
929                 assertThat(stillCaptureRequests).isNotEmpty()
930                 stillCaptureRequests.forEach { config ->
931                     assertThat(
932                             config
933                                 .toCamera2Config()
934                                 .getCaptureRequestOption(CaptureRequest.CONTROL_AE_MODE)
935                         )
936                         .isNull()
937                 }
938             }
939     }
940 
941     @Test
942     @Ignore("AutoFlashUnderExposedQuirk was disabled, ignoring the test.")
943     fun overrideAeModeForStillCapture_aePrecaptureStarted_override(): Unit = runBlocking {
944         // Arrange.
945         val cameraControl =
946             createCameraControl(quirks = Quirks(listOf(AutoFlashUnderExposedQuirk()))).apply {
947                 flashMode = FLASH_MODE_AUTO // Set flash auto to enable aePreCapture
948                 simulateRepeatingResult(
949                     initialDelay = 100,
950                     resultParameters =
951                         mapOf(
952                             CaptureResult.CONTROL_AE_STATE to
953                                 CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED,
954                         )
955                 ) // Make sures flashMode is updated and the flash is required.
956             }
957 
958         // Act.
959         cameraControl.submitStillCaptureRequests(
960             listOf(singleRequest),
961             ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
962             ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
963         )
964 
965         // Switch the repeating result to 3A converged state.
966         cameraControl.simulateRepeatingResult(
967             initialDelay = 500,
968             resultParameters = resultConverged
969         )
970 
971         // Assert.
972         // AE mode should be overridden to CONTROL_AE_MODE_ON_ALWAYS_FLASH
973         immediateCompleteCapture.verifyRequestResult { configList ->
974             configList.requestContains(
975                 CaptureRequest.CONTROL_AE_MODE,
976                 CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH
977             ) && configList.surfaceContains(fakeStillCaptureSurface)
978         }
979     }
980 
981     @Test
982     fun overrideAeModeForStillCapture_aePrecaptureFinish_notOverride(): Unit = runBlocking {
983         // Arrange.
984         val cameraControl =
985             createCameraControl(quirks = Quirks(listOf(AutoFlashUnderExposedQuirk()))).apply {
986                 flashMode = FLASH_MODE_AUTO // Set flash auto to enable aePreCapture
987                 simulateRepeatingResult(
988                     initialDelay = 100,
989                     resultParameters =
990                         mapOf(
991                             CaptureResult.CONTROL_AE_STATE to
992                                 CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED,
993                         )
994                 ) // Make sures flashMode is updated and the flash is required.
995             }
996         val firstCapture =
997             cameraControl.submitStillCaptureRequests(
998                 listOf(singleRequest),
999                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
1000                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
1001             )
1002 
1003         // Switch the repeating result to 3A converged state.
1004         cameraControl.simulateRepeatingResult(
1005             initialDelay = 500,
1006             resultParameters = resultConverged
1007         )
1008         firstCapture.await()
1009         immediateCompleteCapture.clearAllResults() // Clear the result of the firstCapture
1010 
1011         // Act.
1012         // Set flash OFF to disable aePreCapture for testing
1013         cameraControl.flashMode = FLASH_MODE_OFF
1014         val result =
1015             cameraControl
1016                 .submitStillCaptureRequests(
1017                     listOf(singleRequest),
1018                     ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
1019                     ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
1020                 )
1021                 .await()
1022 
1023         // Assert. The second capturing should not override the AE mode.
1024         assertThat(result.size).isEqualTo(1)
1025         immediateCompleteCapture
1026             .getAllResults()
1027             .flatten()
1028             .filter { it.surfaces.contains(fakeStillCaptureSurface) }
1029             .let { stillCaptureRequests ->
1030                 assertThat(stillCaptureRequests).isNotEmpty()
1031                 stillCaptureRequests.forEach { config ->
1032                     assertThat(
1033                             config
1034                                 .toCamera2Config()
1035                                 .getCaptureRequestOption(CaptureRequest.CONTROL_AE_MODE)
1036                         )
1037                         .isNull()
1038                 }
1039             }
1040     }
1041 
1042     @Test
1043     fun overrideAeModeForStillCapture_noAePrecaptureTriggered_notOverride(): Unit = runBlocking {
1044         // Arrange.
1045         val cameraControl =
1046             createCameraControl(quirks = Quirks(listOf(AutoFlashUnderExposedQuirk()))).apply {
1047                 flashMode = FLASH_MODE_AUTO // Set flash auto to enable aePreCapture
1048 
1049                 // Make sures flashMode is updated but the flash is not required.
1050                 simulateRepeatingResult(initialDelay = 100)
1051             }
1052 
1053         // Act.
1054         val deferred =
1055             cameraControl.submitStillCaptureRequests(
1056                 listOf(singleRequest),
1057                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
1058                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
1059             )
1060 
1061         // Switch the repeating result to 3A converged state.
1062         cameraControl.simulateRepeatingResult(
1063             initialDelay = 500,
1064             resultParameters = resultConverged
1065         )
1066 
1067         deferred.await()
1068 
1069         // Assert.
1070         // AE mode should not be overridden
1071         immediateCompleteCapture
1072             .getAllResults()
1073             .flatten()
1074             .filter { it.surfaces.contains(fakeStillCaptureSurface) }
1075             .let { stillCaptureRequests ->
1076                 assertThat(stillCaptureRequests).isNotEmpty()
1077                 stillCaptureRequests.forEach { config ->
1078                     assertThat(
1079                             config
1080                                 .toCamera2Config()
1081                                 .getCaptureRequestOption(CaptureRequest.CONTROL_AE_MODE)
1082                         )
1083                         .isNull()
1084                 }
1085             }
1086     }
1087 
1088     @Test
1089     fun skip3AConvergenceInFlashOn_when3AModeOff(): Unit = runBlocking {
1090         // Arrange. Not have the quirk.
1091         val cameraControl =
1092             createCameraControl(quirks = Quirks(emptyList())).apply {
1093                 flashMode = FLASH_MODE_ON // Set flash ON
1094                 simulateRepeatingResult(initialDelay = 100) // Make sures flashMode is updated.
1095             }
1096 
1097         // Act.
1098         val deferred =
1099             cameraControl.submitStillCaptureRequests(
1100                 listOf(singleRequest),
1101                 ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
1102                 ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
1103             )
1104         // Switch the repeating result to 3A converged state with 3A modes being set to OFF.
1105         cameraControl.simulateRepeatingResult(
1106             initialDelay = 500,
1107             resultParameters = resultConvergedWith3AModeOff
1108         )
1109 
1110         // Ensure 3A is converged (skips 3A check) and capture request is sent.
1111         withTimeout(2000) { assertThat(deferred.await()) }
1112     }
1113 
1114     @Test
1115     fun waitForResultCompletes_whenCaptureResultProvided_noTimeout_noCheckingCondition() {
1116         val cameraControl =
1117             createCameraControl().apply { simulateRepeatingResult(initialDelay = 1) }
1118 
1119         val future = Camera2CapturePipeline.waitForResult(cameraControl, null)
1120 
1121         future.get(500, TimeUnit.MILLISECONDS)
1122     }
1123 
1124     @Test
1125     fun waitForResultCompletes_whenCaptureResultProvided_noTimeout_specificCheckingCondition() {
1126         val cameraControl =
1127             createCameraControl().apply { simulateRepeatingResult(initialDelay = 1) }
1128 
1129         cameraControl.simulateRepeatingResult(initialDelay = 50, resultParameters = resultConverged)
1130 
1131         val future =
1132             Camera2CapturePipeline.waitForResult(cameraControl) { result ->
1133                 Camera2CapturePipeline.is3AConverged(result, false)
1134             }
1135 
1136         future.get(500, TimeUnit.MILLISECONDS).verifyResultFields(resultConverged)
1137     }
1138 
1139     @Test
1140     fun waitForResultDoesNotComplete_whenNoResult_noCheckingCondition() {
1141         // tested for 500ms
1142         Camera2CapturePipeline.waitForResult(createCameraControl(), null)
1143             .awaitException(500, TimeoutException::class.java)
1144     }
1145 
1146     @Test
1147     fun waitForResultDoesNotComplete_whenNoMatchingResult() {
1148         // tested for 500ms
1149         Camera2CapturePipeline.waitForResult(
1150                 createCameraControl().apply { simulateRepeatingResult(initialDelay = 1) }
1151             ) { result ->
1152                 Camera2CapturePipeline.is3AConverged(result, false)
1153             }
1154             .awaitException(500, TimeoutException::class.java)
1155     }
1156 
1157     @Test
1158     fun waitForResultCompletesWithNullResult_whenNoResultWithinTimeout_noCheckingCondition() {
1159         val result =
1160             Camera2CapturePipeline.waitForResult(
1161                     TimeUnit.MILLISECONDS.toNanos(500),
1162                     executorService,
1163                     createCameraControl(),
1164                     null
1165                 )
1166                 .get(
1167                     1,
1168                     TimeUnit.SECONDS
1169                 ) // timeout exception will be thrown if not completed within 1s
1170 
1171         assertThat(result).isNull()
1172     }
1173 
1174     @Test
1175     fun waitForResultCompletesWithNullResult_whenNoMatchingResultWithinTimeout() {
1176         val result =
1177             Camera2CapturePipeline.waitForResult(
1178                     TimeUnit.MILLISECONDS.toNanos(500),
1179                     executorService,
1180                     createCameraControl().apply { simulateRepeatingResult(initialDelay = 1) }
1181                 ) { result ->
1182                     Camera2CapturePipeline.is3AConverged(result, false)
1183                 }
1184                 .get(
1185                     1,
1186                     TimeUnit.SECONDS
1187                 ) // timeout exception will be thrown if not completed within 1s
1188 
1189         assertThat(result).isNull()
1190     }
1191 
1192     private fun TotalCaptureResult.verifyResultFields(
1193         expectedFields: Map<CaptureResult.Key<*>, Any>
1194     ) {
1195         assertThat(this).isNotNull()
1196         expectedFields.forEach { entry -> assertThat(this[entry.key]).isEqualTo(entry.value) }
1197     }
1198 
1199     private fun ListenableFuture<*>.awaitException(timeoutMillis: Long, exceptionType: Class<*>) {
1200         try {
1201             get(timeoutMillis, TimeUnit.MILLISECONDS)
1202         } catch (e: ExecutionException) {
1203             if (exceptionType != ExecutionException::class.java) {
1204                 assertThat(e.cause).isInstanceOf(exceptionType)
1205             }
1206         } catch (e: Exception) {
1207             assertThat(e).isInstanceOf(exceptionType)
1208         }
1209     }
1210 
1211     private fun Camera2CameraControlImpl.waitForSessionConfig(
1212         checkResult: (sessionConfig: SessionConfig) -> Boolean = { true }
1213     ) {
1214         var verifyCount = 0
1215         while (true) {
1216             immediateCompleteCapture.waitForSessionConfigUpdate()
1217             if (checkResult(sessionConfig)) {
1218                 return
1219             }
1220             Truth.assertWithMessage("Verify over 5 times").that(++verifyCount).isLessThan(5)
1221         }
1222     }
1223 
1224     private fun SessionConfig.isTorchParameterEnabled(): Boolean {
1225         val config = toCamera2Config()
1226 
1227         return config.getCaptureRequestOption(CaptureRequest.CONTROL_AE_MODE, null) ==
1228             CaptureRequest.CONTROL_AE_MODE_ON &&
1229             config.getCaptureRequestOption(CaptureRequest.FLASH_MODE, null) ==
1230                 CameraMetadata.FLASH_MODE_TORCH
1231     }
1232 
1233     private fun SessionConfig.isLowLightBoostEnabled(): Boolean {
1234         val config = toCamera2Config()
1235 
1236         return config.getCaptureRequestOption(CaptureRequest.CONTROL_AE_MODE, null) ==
1237             CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY
1238     }
1239 
1240     private fun List<CaptureConfig>.isTorchParameterDisabled() =
1241         requestContains(
1242             CaptureRequest.CONTROL_AE_MODE,
1243             CaptureRequest.CONTROL_AE_MODE_ON,
1244         ) &&
1245             requestContains(
1246                 CaptureRequest.FLASH_MODE,
1247                 CaptureRequest.FLASH_MODE_OFF,
1248             )
1249 
1250     private fun List<CaptureConfig>.requestContains(
1251         key: CaptureRequest.Key<*>,
1252         value: Any?
1253     ): Boolean {
1254         forEach { config ->
1255             if (value == config.toCamera2Config().getCaptureRequestOption(key, null)) {
1256                 return true
1257             }
1258         }
1259         return false
1260     }
1261 
1262     private fun List<CaptureConfig>.surfaceContains(surface: DeferrableSurface): Boolean {
1263         forEach { config ->
1264             if (config.surfaces.contains(surface)) {
1265                 return true
1266             }
1267         }
1268         return false
1269     }
1270 
1271     private fun Camera2CameraControlImpl.simulateRepeatingResult(
1272         initialDelay: Long = 100,
1273         period: Long = 100, // in milliseconds
1274         resultParameters: Map<CaptureResult.Key<*>, Any> = mutableMapOf(),
1275         requestCountLatch: CountDownLatch? = null,
1276         scheduledRunnableExecutor: Executor = executorService
1277     ) {
1278         runningRepeatingStream =
1279             executorService.scheduleAtFixedRate(
1280                 {
1281                     scheduledRunnableExecutor.execute {
1282                         val tagBundle = sessionConfig.repeatingCaptureConfig.tagBundle
1283                         val requestOptions =
1284                             sessionConfig.repeatingCaptureConfig.implementationOptions
1285                         val resultOptions =
1286                             baseRepeatingResult.toMutableMap().apply { putAll(resultParameters) }
1287                         sendRepeatingResult(tagBundle, requestOptions.toParameters(), resultOptions)
1288                         requestCountLatch?.countDown()
1289                     }
1290                 },
1291                 initialDelay,
1292                 period,
1293                 TimeUnit.MILLISECONDS
1294             )
1295     }
1296 
1297     private fun Camera2CameraControlImpl.sendRepeatingResult(
1298         requestTag: Any? = null,
1299         requestParameters: Map<CaptureRequest.Key<*>, Any>,
1300         resultParameters: Map<CaptureResult.Key<*>, Any>,
1301     ) {
1302         val request = mock(CaptureRequest::class.java)
1303         Mockito.`when`(request.tag).thenReturn(requestTag)
1304         requestParameters.forEach { (key, any) -> Mockito.`when`(request.get(key)).thenReturn(any) }
1305 
1306         val result = mock(TotalCaptureResult::class.java)
1307         Mockito.`when`(result.request).thenReturn(request)
1308         resultParameters.forEach { (key, any) -> Mockito.`when`(result.get(key)).thenReturn(any) }
1309 
1310         sessionConfig.repeatingCameraCaptureCallbacks.toList().forEach {
1311             CaptureCallbackConverter.toCaptureCallback(it)
1312                 .onCaptureCompleted(mock(CameraCaptureSession::class.java), request, result)
1313         }
1314     }
1315 
1316     private fun CaptureConfig.toCamera2Config() = Camera2ImplConfig(implementationOptions)
1317 
1318     private fun SessionConfig.toCamera2Config() = Camera2ImplConfig(implementationOptions)
1319 
1320     private fun createCameraControl(
1321         cameraId: String = CAMERA_ID_0,
1322         quirks: Quirks? = null,
1323         updateCallback: CameraControlInternal.ControlUpdateCallback = immediateCompleteCapture,
1324         addTorchFlashRequiredFor3aUpdateQuirk: Boolean = false,
1325         executor: Executor = executorService,
1326     ): Camera2CameraControlImpl {
1327         val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
1328         val characteristics = cameraManager.getCameraCharacteristics(cameraId)
1329         val characteristicsCompat =
1330             CameraCharacteristicsCompat.toCameraCharacteristicsCompat(characteristics, cameraId)
1331         var cameraQuirk = quirks ?: CameraQuirks.get(cameraId, characteristicsCompat)
1332 
1333         if (addTorchFlashRequiredFor3aUpdateQuirk) {
1334             cameraQuirk =
1335                 Quirks(
1336                     cameraQuirk.getAll(Quirk::class.java).apply {
1337                         add(TorchFlashRequiredFor3aUpdateQuirk(characteristicsCompat))
1338                     }
1339                 )
1340         }
1341 
1342         return Camera2CameraControlImpl(
1343                 characteristicsCompat,
1344                 executorService,
1345                 executor,
1346                 updateCallback,
1347                 cameraQuirk
1348             )
1349             .apply {
1350                 setActive(true)
1351                 incrementUseCount()
1352                 this.screenFlash = testScreenFlash
1353                 // Applies low-light boost setting
1354                 enableLowLightBoostAndAssert(this)
1355             }
1356     }
1357 
1358     private fun initCameras() {
1359         Shadow.extract<ShadowCameraManager>(context.getSystemService(Context.CAMERA_SERVICE))
1360             .apply { addCamera(CAMERA_ID_0, intiCharacteristic0()) }
1361     }
1362 
1363     private fun intiCharacteristic0(): CameraCharacteristics {
1364         return ShadowCameraCharacteristics.newCameraCharacteristics().also {
1365             Shadow.extract<ShadowCameraCharacteristics>(it).apply {
1366                 set(CameraCharacteristics.FLASH_INFO_AVAILABLE, true)
1367                 set(
1368                     CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES,
1369                     mutableListOf<Int>()
1370                         .apply {
1371                             add(CaptureRequest.CONTROL_AE_MODE_OFF)
1372                             add(CaptureRequest.CONTROL_AE_MODE_ON)
1373                             add(CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)
1374                             add(CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH)
1375                             add(CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE)
1376                             add(CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH)
1377                             if (Build.VERSION.SDK_INT >= 35) {
1378                                 add(CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY)
1379                             }
1380                         }
1381                         .toIntArray()
1382                 )
1383                 set(CameraCharacteristics.LENS_FACING, CameraMetadata.LENS_FACING_BACK)
1384             }
1385         }
1386     }
1387 
1388     private val immediateCompleteCapture =
1389         object : CameraControlInternal.ControlUpdateCallback {
1390             private val lock = Any()
1391             private val allResults: MutableList<List<CaptureConfig>> = mutableListOf()
1392             val waitingList =
1393                 mutableListOf<
1394                     Pair<CountDownLatch, (captureRequests: List<CaptureConfig>) -> Boolean>
1395                 >()
1396             var updateSessionCountDown = CountDownLatch(1)
1397 
1398             fun verifyRequestResult(
1399                 timeout: Long = TimeUnit.SECONDS.toMillis(5),
1400                 verifyResults: (captureRequests: List<CaptureConfig>) -> Boolean = { true }
1401             ) {
1402                 val resultPair = Pair(CountDownLatch(1), verifyResults)
1403                 synchronized(lock) {
1404                     allResults.forEach {
1405                         if (verifyResults(it)) {
1406                             return
1407                         }
1408                     }
1409                     waitingList.add(resultPair)
1410                 }
1411                 assertThat(resultPair.first.await(timeout, TimeUnit.MILLISECONDS)).isTrue()
1412                 waitingList.remove(resultPair)
1413             }
1414 
1415             fun waitForSessionConfigUpdate(timeout: Long = TimeUnit.SECONDS.toMillis(5)) {
1416                 // No matter onCameraControlUpdateSessionConfig is called before or after
1417                 // the waitForSessionConfigUpdate call, the count down operation should be
1418                 // executed correctly on the updateSessionCountDown object
1419                 updateSessionCountDown.await(timeout, TimeUnit.MILLISECONDS)
1420 
1421                 // Reset count down latch here for next call of waitForSessionConfigUpdate
1422                 updateSessionCountDown = CountDownLatch(1)
1423             }
1424 
1425             override fun onCameraControlUpdateSessionConfig() {
1426                 // Only count down when count is still larger than 1
1427                 if (updateSessionCountDown.count > 0) {
1428                     updateSessionCountDown.countDown()
1429                 }
1430             }
1431 
1432             override fun onCameraControlCaptureRequests(
1433                 captureConfigs: MutableList<CaptureConfig>
1434             ) {
1435                 synchronized(lock) { allResults.add(captureConfigs) }
1436                 waitingList.toList().forEach {
1437                     if (it.second(captureConfigs)) {
1438                         it.first.countDown()
1439                     }
1440                 }
1441 
1442                 // Complete the single capture with an empty result.
1443                 captureConfigs.forEach { captureConfig ->
1444                     captureConfig.cameraCaptureCallbacks.forEach {
1445                         it.onCaptureCompleted(
1446                             CaptureConfig.DEFAULT_ID,
1447                             CameraCaptureResult.EmptyCameraCaptureResult()
1448                         )
1449                     }
1450                 }
1451             }
1452 
1453             fun clearAllResults() = synchronized(lock) { allResults.clear() }
1454 
1455             fun getAllResults() = synchronized(lock) { allResults.toList() }
1456         }
1457 
1458     /** Convert the Config to the CaptureRequest key-value map. */
1459     private fun androidx.camera.core.impl.Config.toParameters(): Map<CaptureRequest.Key<*>, Any> {
1460         val parameters = mutableMapOf<CaptureRequest.Key<*>, Any>()
1461         for (configOption in listOptions()) {
1462             val requestKey = configOption.token as? CaptureRequest.Key<*> ?: continue
1463             val value = retrieveOption(configOption) ?: continue
1464             parameters[requestKey] = value
1465         }
1466 
1467         return parameters
1468     }
1469 
1470     private fun createCameraCharacteristicsCompat(
1471         hasCapabilities: Boolean,
1472         isYuvReprocessingSupported: Boolean,
1473         isPrivateReprocessingSupported: Boolean
1474     ): CameraCharacteristicsCompat {
1475         val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
1476         val shadowCharacteristics = Shadow.extract<ShadowCameraCharacteristics>(characteristics)
1477 
1478         val capabilities = arrayListOf<Int>()
1479         if (isYuvReprocessingSupported) {
1480             capabilities.add(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING)
1481         }
1482         if (isPrivateReprocessingSupported) {
1483             capabilities.add(
1484                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING
1485             )
1486         }
1487 
1488         if (hasCapabilities) {
1489             shadowCharacteristics.set(
1490                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES,
1491                 capabilities.toIntArray()
1492             )
1493         }
1494 
1495         return CameraCharacteristicsCompat.toCameraCharacteristicsCompat(
1496             characteristics,
1497             CAMERA_ID_0
1498         )
1499     }
1500 
1501     @RequiresApi(23)
1502     private fun initCameraControlWithZsl(
1503         isZslDisabledByFlashMode: Boolean,
1504         isZslDisabledByUserCaseConfig: Boolean
1505     ): Camera2CameraControlImpl {
1506         val cameraControl =
1507             createCameraControl().apply { simulateRepeatingResult(initialDelay = 100) }
1508 
1509         val zslControl =
1510             ZslControlImpl(
1511                 createCameraCharacteristicsCompat(
1512                     hasCapabilities = true,
1513                     isYuvReprocessingSupported = true,
1514                     isPrivateReprocessingSupported = true
1515                 ),
1516                 executorService
1517             )
1518 
1519         // Only need to initialize when not disabled
1520         if (!isZslDisabledByFlashMode && !isZslDisabledByUserCaseConfig) {
1521             val captureResult = FakeCameraCaptureResult()
1522             captureResult.afState = AfState.LOCKED_FOCUSED
1523             captureResult.aeState = AeState.CONVERGED
1524             captureResult.awbState = AwbState.CONVERGED
1525             val imageProxy = FakeImageProxy(CameraCaptureResultImageInfo(captureResult))
1526             imageProxy.image = mock(Image::class.java)
1527             zslControl.mImageRingBuffer.enqueue(imageProxy)
1528             zslControl.mReprocessingImageWriterHolder =
1529                 ZslControlImpl.ImageWriterHolder(executorService).apply {
1530                     onImageWriterCreated(mock(ImageWriter::class.java))
1531                 }
1532         }
1533 
1534         zslControl.isZslDisabledByFlashMode = isZslDisabledByFlashMode
1535         zslControl.isZslDisabledByUserCaseConfig = isZslDisabledByUserCaseConfig
1536 
1537         cameraControl.mZslControl = zslControl
1538 
1539         // Applies low-light boost setting
1540         enableLowLightBoostAndAssert(cameraControl)
1541 
1542         return cameraControl
1543     }
1544 
1545     private fun enableLowLightBoostAndAssert(cameraControlImpl: Camera2CameraControlImpl) {
1546         if (isLowLightBoostEnabled) {
1547             executorService.run { cameraControlImpl.enableLowLightBoostInternal(true) }
1548         }
1549         assertThat(cameraControlImpl.isLowLightBoostOn).isEqualTo(isLowLightBoostEnabled)
1550     }
1551 }
1552