1 /*
<lambda>null2  * Copyright 2022 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 package androidx.camera.integration.core.camera2
17 
18 import android.content.Context
19 import android.graphics.ImageFormat
20 import android.graphics.Point
21 import android.graphics.SurfaceTexture
22 import android.hardware.camera2.CameraCaptureSession
23 import android.hardware.camera2.CameraCaptureSession.CaptureCallback
24 import android.hardware.camera2.CameraCharacteristics
25 import android.hardware.camera2.CaptureRequest
26 import android.hardware.camera2.CaptureResult
27 import android.hardware.camera2.TotalCaptureResult
28 import android.os.Build
29 import android.util.Range
30 import android.util.Rational
31 import android.util.Size
32 import android.view.Surface
33 import androidx.camera.camera2.Camera2Config
34 import androidx.camera.camera2.internal.DisplayInfoManager
35 import androidx.camera.camera2.interop.Camera2Interop
36 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
37 import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
38 import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraCroppingQuirk
39 import androidx.camera.core.AspectRatio
40 import androidx.camera.core.CameraSelector
41 import androidx.camera.core.CameraXConfig
42 import androidx.camera.core.DynamicRange
43 import androidx.camera.core.MirrorMode
44 import androidx.camera.core.Preview
45 import androidx.camera.core.SurfaceRequest
46 import androidx.camera.core.SurfaceRequest.TransformationInfo
47 import androidx.camera.core.UseCaseGroup
48 import androidx.camera.core.ViewPort
49 import androidx.camera.core.impl.CameraInfoInternal
50 import androidx.camera.core.impl.ImageOutputConfig
51 import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR
52 import androidx.camera.core.impl.SessionConfig
53 import androidx.camera.core.impl.utils.Threads.runOnMainSync
54 import androidx.camera.core.impl.utils.executor.CameraXExecutors
55 import androidx.camera.core.internal.utils.SizeUtil
56 import androidx.camera.core.resolutionselector.AspectRatioStrategy
57 import androidx.camera.core.resolutionselector.ResolutionFilter
58 import androidx.camera.core.resolutionselector.ResolutionSelector
59 import androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
60 import androidx.camera.core.resolutionselector.ResolutionStrategy
61 import androidx.camera.integration.core.util.CameraInfoUtil
62 import androidx.camera.lifecycle.ProcessCameraProvider
63 import androidx.camera.testing.impl.CameraPipeConfigTestRule
64 import androidx.camera.testing.impl.CameraUtil
65 import androidx.camera.testing.impl.CameraUtil.PreTestCameraIdList
66 import androidx.camera.testing.impl.ExtensionsUtil
67 import androidx.camera.testing.impl.SurfaceTextureProvider
68 import androidx.camera.testing.impl.WakelockEmptyActivityRule
69 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
70 import androidx.camera.testing.impl.fakes.FakeSessionProcessor
71 import androidx.camera.video.Recorder
72 import androidx.camera.video.VideoCapture
73 import androidx.concurrent.futures.await
74 import androidx.core.util.Consumer
75 import androidx.test.core.app.ApplicationProvider
76 import androidx.test.filters.LargeTest
77 import androidx.test.filters.SdkSuppress
78 import androidx.test.platform.app.InstrumentationRegistry
79 import androidx.testutils.fail
80 import com.google.common.truth.Truth.assertThat
81 import java.util.concurrent.ExecutionException
82 import java.util.concurrent.Executor
83 import java.util.concurrent.Executors
84 import java.util.concurrent.Semaphore
85 import java.util.concurrent.ThreadFactory
86 import java.util.concurrent.TimeUnit
87 import java.util.concurrent.TimeoutException
88 import java.util.concurrent.atomic.AtomicReference
89 import kotlin.math.abs
90 import kotlinx.coroutines.CompletableDeferred
91 import kotlinx.coroutines.Dispatchers
92 import kotlinx.coroutines.delay
93 import kotlinx.coroutines.runBlocking
94 import kotlinx.coroutines.withContext
95 import kotlinx.coroutines.withTimeout
96 import kotlinx.coroutines.withTimeoutOrNull
97 import org.junit.After
98 import org.junit.Assume.assumeFalse
99 import org.junit.Assume.assumeTrue
100 import org.junit.Before
101 import org.junit.Rule
102 import org.junit.Test
103 import org.junit.runner.RunWith
104 import org.junit.runners.Parameterized
105 
106 @LargeTest
107 @RunWith(Parameterized::class)
108 @SdkSuppress(minSdkVersion = 21)
109 class PreviewTest(private val implName: String, private val cameraConfig: CameraXConfig) {
110     @get:Rule
111     val cameraPipeConfigTestRule =
112         CameraPipeConfigTestRule(
113             active = implName == CameraPipeConfig::class.simpleName,
114         )
115 
116     @get:Rule
117     val cameraRule =
118         CameraUtil.grantCameraPermissionAndPreTestAndPostTest(PreTestCameraIdList(cameraConfig))
119 
120     // Launch activity when testing in Vivo devices to prevent testing process from being killed.
121     @get:Rule
122     val wakelockEmptyActivityRule = WakelockEmptyActivityRule(brandsToEnable = listOf("vivo"))
123 
124     companion object {
125         private const val ANY_THREAD_NAME = "any-thread-name"
126         private val DEFAULT_RESOLUTION: Size by lazy { Size(640, 480) }
127         private val DEFAULT_RESOLUTION_PORTRAIT: Size by lazy { Size(480, 640) }
128         private const val FRAMES_TO_VERIFY = 10
129         private const val RESULT_TIMEOUT = 5000L
130         private const val TOLERANCE = 0.1f
131 
132         @JvmStatic
133         @Parameterized.Parameters(name = "{0}")
134         fun data() =
135             listOf(
136                 arrayOf(Camera2Config::class.simpleName, Camera2Config.defaultConfig()),
137                 arrayOf(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig())
138             )
139     }
140 
141     private val instrumentation = InstrumentationRegistry.getInstrumentation()
142     private lateinit var cameraProvider: ProcessCameraProvider
143     private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
144     private var previewResolution: Size? = null
145     private var frameSemaphore: Semaphore? = null
146     private val context: Context = ApplicationProvider.getApplicationContext()
147     private val lifecycleOwner = FakeLifecycleOwner()
148 
149     @Before
150     @Throws(ExecutionException::class, InterruptedException::class)
151     fun setUp() = runBlocking {
152         assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!))
153 
154         ProcessCameraProvider.configureInstance(cameraConfig)
155         cameraProvider = ProcessCameraProvider.getInstance(context).await()
156         frameSemaphore = Semaphore(/* permits= */ 0)
157         lifecycleOwner.startAndResume()
158     }
159 
160     @After
161     @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)
162     fun tearDown() {
163         cameraProvider.shutdownAsync()[10000, TimeUnit.MILLISECONDS]
164     }
165 
166     // ======================================================
167     //   Section 1: SurfaceProvider behavior testing
168     // ======================================================
169     @Test
170     fun surfaceProvider_isUsedAfterSetting() = runBlocking {
171         val preview = Preview.Builder().build()
172         val completableDeferred = CompletableDeferred<Unit>()
173 
174         instrumentation.runOnMainSync {
175             preview.setSurfaceProvider { request ->
176                 val surfaceTexture = SurfaceTexture(0)
177                 surfaceTexture.setDefaultBufferSize(
178                     request.resolution.width,
179                     request.resolution.height
180                 )
181                 surfaceTexture.detachFromGLContext()
182                 val surface = Surface(surfaceTexture)
183                 request.provideSurface(surface, CameraXExecutors.directExecutor()) {
184                     surface.release()
185                     surfaceTexture.release()
186                 }
187                 completableDeferred.complete(Unit)
188             }
189 
190             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
191         }
192         withTimeout(3_000) { completableDeferred.await() }
193     }
194 
195     private fun <T> CompletableDeferred<T>.completeOnceOnly(value: T) {
196         if (!this.complete(value)) {
197             throw IllegalStateException("Result Listener being invoked twice")
198         }
199     }
200 
201     @Test
202     @Throws(InterruptedException::class)
203     fun previewUnbound_RESULT_SURFACE_USED_SUCCESSFULLY_isCalled() = runBlocking {
204         // Arrange.
205         val preview = Preview.Builder().build()
206         val resultDeferred = CompletableDeferred<Int>()
207         // Act.
208         instrumentation.runOnMainSync {
209             preview.setSurfaceProvider(
210                 CameraXExecutors.mainThreadExecutor(),
211                 getSurfaceProvider(
212                     frameAvailableListener = { frameSemaphore!!.release() },
213                     resultListener = { result ->
214                         resultDeferred.completeOnceOnly(result.resultCode)
215                     }
216                 )
217             )
218             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
219         }
220 
221         // Wait until preview gets frame.
222         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 5)
223 
224         // Ensure resultListener is not invoked before unbind.
225         assertThat(withTimeoutOrNull(500) { resultDeferred.await() }).isNull()
226 
227         // Remove the UseCase from the camera
228         instrumentation.runOnMainSync { cameraProvider.unbind(preview) }
229 
230         // Assert.
231         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() })
232             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
233     }
234 
235     @Test
236     @Throws(InterruptedException::class)
237     fun setSurfaceProviderBeforeBind_getsFrame() {
238         // Arrange.
239         val preview = Preview.Builder().build()
240         instrumentation.runOnMainSync {
241             preview.setSurfaceProvider(
242                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
243             )
244             // Act.
245             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
246         }
247 
248         // Assert.
249         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
250     }
251 
252     @Test
253     @Throws(InterruptedException::class)
254     fun setSurfaceProviderBeforeBind_providesSurfaceOnWorkerExecutorThread() {
255         val threadName = AtomicReference<String>()
256 
257         // Arrange.
258         val preview = Preview.Builder().build()
259         instrumentation.runOnMainSync {
260             preview.setSurfaceProvider(
261                 workExecutorWithNamedThread,
262                 getSurfaceProvider(
263                     threadNameConsumer = { newValue: String -> threadName.set(newValue) },
264                     frameAvailableListener = { frameSemaphore!!.release() }
265                 )
266             )
267 
268             // Act.
269             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
270         }
271 
272         // Assert.
273         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
274         assertThat(threadName.get()).isEqualTo(ANY_THREAD_NAME)
275     }
276 
277     @Test
278     @Throws(InterruptedException::class)
279     fun setSurfaceProviderAfterBind_getsFrame() {
280         // Arrange.
281         val preview = Preview.Builder().build()
282 
283         instrumentation.runOnMainSync {
284             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
285 
286             // Act.
287             preview.setSurfaceProvider(
288                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
289             )
290         }
291 
292         // Assert.
293         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
294     }
295 
296     @Test
297     @Throws(InterruptedException::class)
298     fun setSurfaceProviderAfterBind_providesSurfaceOnWorkerExecutorThread() {
299         val threadName = AtomicReference<String>()
300 
301         // Arrange.
302         val preview = Preview.Builder().build()
303 
304         instrumentation.runOnMainSync {
305             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
306             // Act.
307             preview.setSurfaceProvider(
308                 workExecutorWithNamedThread,
309                 getSurfaceProvider(
310                     threadNameConsumer = { newValue: String -> threadName.set(newValue) },
311                     frameAvailableListener = { frameSemaphore!!.release() }
312                 )
313             )
314         }
315 
316         // Assert.
317         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
318         assertThat(threadName.get()).isEqualTo(ANY_THREAD_NAME)
319     }
320 
321     @Test
322     @Throws(InterruptedException::class)
323     fun setMultipleNonNullSurfaceProviders_getsFrame() {
324         val preview = Preview.Builder().build()
325 
326         instrumentation.runOnMainSync {
327             // Set a different SurfaceProvider which will provide a different surface to be used
328             // for preview.
329             preview.setSurfaceProvider(
330                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
331             )
332             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
333         }
334         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
335 
336         // Use another sSemaphore to monitor the frames from the 2nd surfaceProvider.
337         val frameSemaphore2 = Semaphore(/* permits= */ 0)
338 
339         instrumentation.runOnMainSync {
340             // Set a different SurfaceProvider which will provide a different surface to be used
341             // for preview.
342             preview.setSurfaceProvider(
343                 getSurfaceProvider(frameAvailableListener = { frameSemaphore2.release() })
344             )
345         }
346         frameSemaphore2.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
347     }
348 
349     @Test
350     @Throws(InterruptedException::class)
351     fun setMultipleNullableSurfaceProviders_getsFrame() {
352         val preview = Preview.Builder().build()
353 
354         instrumentation.runOnMainSync {
355             // Set a different SurfaceProvider which will provide a different surface to be used
356             // for preview.
357             preview.setSurfaceProvider(
358                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
359             )
360             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
361         }
362         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
363 
364         // Recreate the semaphore to monitor the frame available callback.
365         val frameSemaphore2 = Semaphore(/* permits= */ 0)
366 
367         instrumentation.runOnMainSync {
368             // Set the SurfaceProvider to null in order to force the Preview into an inactive
369             // state before setting a different SurfaceProvider for preview.
370             preview.setSurfaceProvider(null)
371             preview.setSurfaceProvider(
372                 getSurfaceProvider(frameAvailableListener = { frameSemaphore2.release() })
373             )
374         }
375         frameSemaphore2.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
376     }
377 
378     @Test
379     fun willNotProvideSurface_resultCode_WILL_NOT_PROVIDE_SURFACE(): Unit = runBlocking {
380         val preview = Preview.Builder().build()
381 
382         val result: Int =
383             withContext(Dispatchers.Main) {
384                 val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>()
385                 preview.setSurfaceProvider { request ->
386                     if (!surfaceRequestDeferred.complete(request)) {
387                         // Ignore any new results. Could also call preview.setSurfaceProvider(null)
388                         // on successful completion to ensure no further requests are sent.
389                         request.willNotProvideSurface()
390                     }
391                 }
392 
393                 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
394 
395                 withTimeoutOrNull(RESULT_TIMEOUT) { surfaceRequestDeferred.await() }
396                     ?.let { request ->
397                         val resultDeferred = CompletableDeferred<Int>()
398                         request.willNotProvideSurface()
399                         val surface = Surface(SurfaceTexture(0))
400                         // can't provideSurface successfully after willNotProvideSurface.
401                         // RESULT_WILL_NOT_PROVIDE_SURFACE will be notified.
402                         request.provideSurface(surface, CameraXExecutors.directExecutor()) { result
403                             ->
404                             resultDeferred.completeOnceOnly(result.resultCode)
405                         }
406 
407                         withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() }
408                             ?: fail("Timed out while waiting for surface result.")
409                     } ?: fail("Timed out while waiting for surface request.")
410             }
411 
412         assertThat(result).isEqualTo(SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE)
413     }
414 
415     @Test
416     fun provideSurfaceTwice_resultCode_SURFACE_ALREADY_PROVIDED(): Unit = runBlocking {
417         val preview = Preview.Builder().build()
418 
419         val resultDeferred1 = CompletableDeferred<Int>()
420         val resultDeferred2 = CompletableDeferred<Int>()
421         instrumentation.runOnMainSync {
422             preview.setSurfaceProvider { surfaceRequest ->
423                 runBlocking {
424                     val surfaceTextureHolder =
425                         SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(
426                                 surfaceRequest.resolution.width,
427                                 surfaceRequest.resolution.height,
428                                 { frameSemaphore!!.release() }
429                             )
430                             .await()
431                     val surface = Surface(surfaceTextureHolder!!.surfaceTexture)
432                     surfaceRequest.provideSurface(
433                         surface,
434                         CameraXExecutors.directExecutor(),
435                         { result ->
436                             surfaceTextureHolder.close()
437                             surface.release()
438                             resultDeferred1.completeOnceOnly(result.resultCode)
439                         }
440                     )
441 
442                     // Invoking provideSurface twice is a no-op and the result will be
443                     // RESULT_SURFACE_ALREADY_PROVIDED
444                     surfaceRequest.provideSurface(
445                         Surface(SurfaceTexture(1)),
446                         CameraXExecutors.directExecutor()
447                     ) { result ->
448                         resultDeferred2.completeOnceOnly(result.resultCode)
449                     }
450                 }
451             }
452             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
453         }
454 
455         // Wait until preview gets frame.
456         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
457 
458         instrumentation.runOnMainSync { cameraProvider.unbind(preview) }
459 
460         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred2.await() })
461             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED)
462 
463         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() })
464             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
465     }
466 
467     @Test
468     fun surfaceRequestCancelled_resultCode_REQUEST_CANCELLED() = runBlocking {
469         val preview = Preview.Builder().build()
470 
471         val resultDeferred = CompletableDeferred<Int>()
472         val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>()
473         instrumentation.runOnMainSync {
474             preview.setSurfaceProvider { surfaceRequest ->
475                 surfaceRequestDeferred.complete(surfaceRequest)
476             }
477             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
478             // unbind the use case to cancel the surface request.
479             cameraProvider.unbind(preview)
480         }
481 
482         // Small delay to allow the SurfaceRequest to be cancelled.
483         delay(500)
484 
485         val surfaceRequest = surfaceRequestDeferred.await()
486         instrumentation.runOnMainSync {
487             surfaceRequest.provideSurface(
488                 Surface(SurfaceTexture(0)),
489                 CameraXExecutors.directExecutor()
490             ) { result ->
491                 resultDeferred.completeOnceOnly(result.resultCode)
492             }
493         }
494 
495         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() })
496             .isEqualTo(SurfaceRequest.Result.RESULT_REQUEST_CANCELLED)
497     }
498 
499     @Test
500     fun newSurfaceProviderAfterSurfaceProvided_resultCode_SURFACE_USED_SUCCESSFULLY() =
501         runBlocking {
502             val preview = Preview.Builder().build()
503 
504             val resultDeferred1 = CompletableDeferred<Int>()
505             val resultDeferred2 = CompletableDeferred<Int>()
506             instrumentation.runOnMainSync {
507                 preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor()) { surfaceRequest
508                     ->
509                     val surface = Surface(SurfaceTexture(0))
510                     surfaceRequest.provideSurface(surface, CameraXExecutors.directExecutor()) {
511                         result ->
512                         resultDeferred1.completeOnceOnly(result.resultCode)
513                     }
514 
515                     // After the surface is provided, if there is a new request (here we trigger by
516                     // setting another surfaceProvider), the previous surfaceRequest will receive
517                     // RESULT_SURFACE_USED_SUCCESSFULLY.
518                     preview.setSurfaceProvider(
519                         getSurfaceProvider(
520                             frameAvailableListener = { frameSemaphore!!.release() },
521                             resultListener = { resultDeferred2.completeOnceOnly(it.resultCode) }
522                         )
523                     )
524                 }
525                 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
526             }
527 
528             assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() })
529                 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
530 
531             // Wait until preview gets frame.
532             frameSemaphore!!.verifyFramesReceived(
533                 frameCount = FRAMES_TO_VERIFY,
534                 timeoutInSeconds = 5
535             )
536 
537             instrumentation.runOnMainSync { cameraProvider.unbindAll() }
538 
539             assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred2.await() })
540                 .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
541         }
542 
543     @Test
544     fun newSurfaceRequestAfterSurfaceProvided_resultCode_SURFACE_USED_SUCCESSFULLY() = runBlocking {
545         val preview = Preview.Builder().build()
546 
547         val resultDeferred1 = CompletableDeferred<Int>()
548         val resultDeferred2 = CompletableDeferred<Int>()
549 
550         var surfaceRequestCount = 0
551         instrumentation.runOnMainSync {
552             preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor()) { surfaceRequest ->
553                 // the surface will be requested twice on the same SurfaceProvider instance.
554                 if (surfaceRequestCount == 0) {
555                     val surfaceTexture = SurfaceTexture(0)
556                     val surface = Surface(surfaceTexture)
557                     surfaceRequest.provideSurface(surface, CameraXExecutors.directExecutor()) {
558                         result ->
559                         surfaceTexture.release()
560                         surface.release()
561                         resultDeferred1.completeOnceOnly(result.resultCode)
562                     }
563 
564                     // After the surface is provided, if there is a new request (here we trigger by
565                     // unbind and rebind), the previous surfaceRequest will receive
566                     // RESULT_SURFACE_USED_SUCCESSFULLY.
567                     cameraProvider.unbind(preview)
568                     surfaceRequestCount++
569                     cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
570                 } else {
571                     runBlocking {
572                         val surfaceTextureHolder =
573                             SurfaceTextureProvider.createAutoDrainingSurfaceTextureAsync(
574                                     surfaceRequest.resolution.width,
575                                     surfaceRequest.resolution.height,
576                                     { frameSemaphore!!.release() }
577                                 )
578                                 .await()
579                         val surface = Surface(surfaceTextureHolder.surfaceTexture)
580                         surfaceRequest.provideSurface(surface, CameraXExecutors.directExecutor()) {
581                             result ->
582                             surfaceTextureHolder.close()
583                             surface.release()
584                             resultDeferred2.completeOnceOnly(result.resultCode)
585                         }
586                     }
587                 }
588             }
589             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
590         }
591 
592         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() })
593             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
594 
595         // Wait until preview gets frame.
596         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 5)
597 
598         instrumentation.runOnMainSync { cameraProvider.unbindAll() }
599 
600         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred2.await() })
601             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
602     }
603 
604     @Test
605     @Throws(InterruptedException::class)
606     fun setNullSurfaceProvider_shouldStopPreview() {
607         // Arrange.
608         val preview = Preview.Builder().build()
609 
610         // Act.
611         instrumentation.runOnMainSync {
612             preview.setSurfaceProvider(
613                 CameraXExecutors.mainThreadExecutor(),
614                 SurfaceTextureProvider.createAutoDrainingSurfaceTextureProvider {
615                     frameSemaphore!!.release()
616                 }
617             )
618             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
619         }
620 
621         // Assert.
622         // Wait until preview gets frame.
623         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
624 
625         // Act.
626         instrumentation.runOnMainSync {
627             preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor(), null)
628         }
629 
630         // Assert.
631         // No frame coming for 3 seconds in 10 seconds timeout.
632         assertThat(noFrameCome(3000L, 10000L)).isTrue()
633     }
634 
635     @Test
636     fun surfaceClosed_resultCode_INVALID_SURFACE() = runBlocking {
637         // Arrange.
638         val preview = Preview.Builder().build()
639         val resultDeferred1 = CompletableDeferred<Int>()
640 
641         // Act.
642         instrumentation.runOnMainSync {
643             preview.setSurfaceProvider(
644                 CameraXExecutors.mainThreadExecutor(),
645                 { request ->
646                     request.provideSurface(
647                         Surface(SurfaceTexture(0)).also { it.release() }, // invalid surface
648                         CameraXExecutors.directExecutor()
649                     ) { result ->
650                         resultDeferred1.completeOnceOnly(result.resultCode)
651                     }
652                 }
653             )
654 
655             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
656         }
657 
658         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred1.await() })
659             .isEqualTo(SurfaceRequest.Result.RESULT_INVALID_SURFACE)
660     }
661 
662     // ======================================================
663     // Section 2: targetResolution / targetRotation / targetAspectRatio
664     // ======================================================
665 
666     @Test
667     fun defaultAspectRatioWillBeSet_whenTargetResolutionIsNotSet() =
668         runBlocking(Dispatchers.Main) {
669             val useCase = Preview.Builder().build()
670             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
671             val config = useCase.currentConfig as ImageOutputConfig
672             assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_4_3)
673         }
674 
675     @Suppress("DEPRECATION") // test for legacy resolution API
676     @Test
677     fun defaultAspectRatioWillBeSet_whenRatioDefaultIsSet() =
678         runBlocking(Dispatchers.Main) {
679             assumeTrue(
680                 !hasExtraCroppingQuirk() &&
681                     isAspectRatioResolutionSupported(4.0f / 3.0f, isLegacyApi = true)
682             )
683             val useCase = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_DEFAULT).build()
684             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
685             val config = useCase.currentConfig as ImageOutputConfig
686             assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_4_3)
687             val resolution = useCase.resolutionInfo!!.resolution
688             assertThat(resolution.width.toFloat() / resolution.height)
689                 .isWithin(TOLERANCE)
690                 .of(4.0f / 3.0f)
691         }
692 
693     @Suppress("DEPRECATION") // test for legacy resolution API
694     @Test
695     fun aspectRatio4_3_resolutionIsSet() =
696         runBlocking(Dispatchers.Main) {
697             assumeTrue(
698                 !hasExtraCroppingQuirk() &&
699                     isAspectRatioResolutionSupported(4.0f / 3.0f, isLegacyApi = true)
700             )
701 
702             val useCase = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).build()
703             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
704             val config = useCase.currentConfig as ImageOutputConfig
705             assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_4_3)
706             val resolution = useCase.resolutionInfo!!.resolution
707             assertThat(resolution.width.toFloat() / resolution.height)
708                 .isWithin(TOLERANCE)
709                 .of(4.0f / 3.0f)
710         }
711 
712     @Suppress("DEPRECATION") // test for legacy resolution API
713     @Test
714     fun aspectRatio16_9_resolutionIsSet() =
715         runBlocking(Dispatchers.Main) {
716             assumeTrue(
717                 !hasAspectRatioLegacyApi21Quirk() &&
718                     isAspectRatioResolutionSupported(16.0f / 9.0f, isLegacyApi = true)
719             )
720             val useCase = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9).build()
721             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
722             val config = useCase.currentConfig as ImageOutputConfig
723             assertThat(config.targetAspectRatio).isEqualTo(AspectRatio.RATIO_16_9)
724             val resolution = useCase.resolutionInfo!!.resolution
725             assertThat(resolution.width.toFloat() / resolution.height)
726                 .isWithin(TOLERANCE)
727                 .of(16.0f / 9.0f)
728         }
729 
730     private fun isAspectRatioResolutionSupported(
731         targetAspectRatioValue: Float,
732         isLegacyApi: Boolean = false
733     ): Boolean {
734         val cameraCharacteristics =
735             (cameraProvider.getCameraInfo(cameraSelector) as CameraInfoInternal)
736                 .cameraCharacteristics as CameraCharacteristics
737         val map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
738         val previewSizes = map!!.getOutputSizes(SurfaceTexture::class.java)
739         return previewSizes.find { size ->
740             val aspectRatioVal = size.width.toFloat() / size.height.toFloat()
741             abs(targetAspectRatioValue - aspectRatioVal) <= TOLERANCE &&
742                 isValidPreviewSize(size, isLegacyApi)
743         } != null
744     }
745 
746     private fun isValidPreviewSize(size: Size, isLegacyApi: Boolean): Boolean {
747         val previewDefinitionSize = getPreviewDefinitionSize()
748         val sizeArea = SizeUtil.getArea(size)
749         val previewSizeArea = SizeUtil.getArea(previewDefinitionSize)
750 
751         // When using ResolutionSelector API, all sizes equal to or smaller than PREVIEW size can
752         // be selected.
753         if (!isLegacyApi) {
754             return sizeArea <= previewSizeArea
755         }
756 
757         // When using Legacy API, if the PREVIEW size is smaller than 640x480, all output sizes
758         // smaller than it can be selected. The reason is, for some devices, there might be 16:9
759         // output sizes smaller than 480P. But it might make the preview have bad image quality
760         // when the display size is larger than 480P. Therefore, those sizes can be selected to use
761         // only when the device display size is smaller than 480P or apps explicitly set a target
762         // resolution smaller than 480P.
763         if (SizeUtil.isSmallerByArea(previewDefinitionSize, SizeUtil.RESOLUTION_480P)) {
764             return SizeUtil.isSmallerByArea(size, previewDefinitionSize)
765         }
766 
767         // Otherwise, only sizes between PREVIEW size and 640x480 can be selected
768         val vgaSizeArea = SizeUtil.getArea(SizeUtil.RESOLUTION_VGA)
769 
770         return sizeArea in vgaSizeArea..previewSizeArea
771     }
772 
773     @Suppress("DEPRECATION") // getRealSize
774     private fun getPreviewDefinitionSize(): Size {
775         val point = Point()
776         DisplayInfoManager.getInstance(context).getMaxSizeDisplay(false).also {
777             it.getRealSize(point)
778         }
779         val previewSize =
780             if (point.x > point.y) {
781                 Size(point.x, point.y)
782             } else {
783                 Size(point.y, point.x)
784             }
785         return if (SizeUtil.isSmallerByArea(previewSize, SizeUtil.RESOLUTION_1080P)) {
786             previewSize
787         } else {
788             SizeUtil.RESOLUTION_1080P
789         }
790     }
791 
792     private fun hasExtraCroppingQuirk(): Boolean {
793         return (implName.contains(CameraPipeConfig::class.simpleName!!) &&
794             DeviceQuirks[ExtraCroppingQuirk::class.java] != null) ||
795             androidx.camera.camera2.internal.compat.quirk.DeviceQuirks.get(
796                 androidx.camera.camera2.internal.compat.quirk.ExtraCroppingQuirk::class.java
797             ) != null
798     }
799 
800     // Checks whether it is the device for AspectRatioLegacyApi21Quirk
801     private fun hasAspectRatioLegacyApi21Quirk(): Boolean {
802         val quirks =
803             (cameraProvider.getCameraInfo(cameraSelector) as CameraInfoInternal).cameraQuirks
804         return if (implName == CameraPipeConfig::class.simpleName) {
805             quirks.contains(
806                 androidx.camera.camera2.pipe.integration.compat.quirk
807                         .AspectRatioLegacyApi21Quirk::class
808                     .java
809             )
810         } else {
811             quirks.contains(
812                 androidx.camera.camera2.internal.compat.quirk.AspectRatioLegacyApi21Quirk::class
813                     .java
814             )
815         }
816     }
817 
818     @Suppress("DEPRECATION") // legacy resolution API
819     @Test
820     fun defaultAspectRatioWontBeSet_andResolutionIsSet_whenTargetResolutionIsSet() =
821         runBlocking(Dispatchers.Main) {
822             assumeTrue(
823                 CameraUtil.isCameraSensorPortraitInNativeOrientation(cameraSelector.lensFacing!!) &&
824                     !hasExtraCroppingQuirk()
825             )
826             val useCase =
827                 Preview.Builder()
828                     .setTargetResolution(DEFAULT_RESOLUTION_PORTRAIT)
829                     .setTargetRotation(Surface.ROTATION_0) // enforce native orientation.
830                     .build()
831             assertThat(
832                     useCase.currentConfig.containsOption(
833                         ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO
834                     )
835                 )
836                 .isFalse()
837 
838             cameraProvider.bindToLifecycle(
839                 lifecycleOwner,
840                 CameraSelector.DEFAULT_BACK_CAMERA,
841                 useCase
842             )
843 
844             assertThat(
845                     useCase.currentConfig.containsOption(
846                         ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO
847                     )
848                 )
849                 .isFalse()
850             assertThat(useCase.resolutionInfo!!.resolution).isEqualTo(DEFAULT_RESOLUTION)
851         }
852 
853     @Test
854     fun targetRotationIsRetained_whenUseCaseIsReused() =
855         runBlocking(Dispatchers.Main) {
856             val useCase = Preview.Builder().build()
857             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
858 
859             // Generally, the device can't be rotated to Surface.ROTATION_180. Therefore,
860             // use it to do the test.
861             useCase.targetRotation = Surface.ROTATION_180
862             cameraProvider.unbind(useCase)
863 
864             // Check the target rotation is kept when the use case is unbound.
865             assertThat(useCase.targetRotation).isEqualTo(Surface.ROTATION_180)
866 
867             // Check the target rotation is kept when the use case is rebound to the
868             // lifecycle.
869             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
870             assertThat(useCase.targetRotation).isEqualTo(Surface.ROTATION_180)
871         }
872 
873     @Test
874     fun targetRotationReturnsDisplayRotationIfNotSet() =
875         runBlocking(Dispatchers.Main) {
876             val displayRotation =
877                 DisplayInfoManager.getInstance(context).getMaxSizeDisplay(true).rotation
878             val useCase = Preview.Builder().build()
879             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCase)
880 
881             assertThat(useCase.targetRotation).isEqualTo(displayRotation)
882         }
883 
884     @Test
885     fun returnValidTargetRotation_afterUseCaseIsCreated() {
886         val preview = Preview.Builder().build()
887         assertThat(preview.targetRotation).isNotEqualTo(ImageOutputConfig.INVALID_ROTATION)
888     }
889 
890     @Test
891     fun returnCorrectTargetRotation_afterUseCaseIsBound() =
892         runBlocking(Dispatchers.Main) {
893             val preview = Preview.Builder().setTargetRotation(Surface.ROTATION_180).build()
894             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
895             assertThat(preview.targetRotation).isEqualTo(Surface.ROTATION_180)
896         }
897 
898     @Suppress("DEPRECATION") // legacy resolution API
899     @Test
900     fun setTargetRotationOnBuilder_ResolutionIsSetCorrectly() =
901         runBlocking(Dispatchers.Main) {
902             assumeTrue(
903                 CameraUtil.isCameraSensorPortraitInNativeOrientation(cameraSelector.lensFacing!!) &&
904                     !hasExtraCroppingQuirk()
905             )
906             val preview =
907                 Preview.Builder()
908                     .setTargetRotation(Surface.ROTATION_90)
909                     .setTargetResolution(DEFAULT_RESOLUTION)
910                     .build()
911             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
912             assertThat(preview.resolutionInfo!!.resolution).isEqualTo(DEFAULT_RESOLUTION)
913             assertThat(preview.resolutionInfo!!.rotationDegrees).isEqualTo(0)
914         }
915 
916     @Test
917     fun setTargetRotationAfterBind_transformationInfoIsUpdated() =
918         runBlocking(Dispatchers.Main) {
919             assumeTrue(
920                 CameraUtil.isCameraSensorPortraitInNativeOrientation(cameraSelector.lensFacing!!)
921             )
922 
923             var transformationInfoDeferred = CompletableDeferred<TransformationInfo>()
924             val surfaceProvidedDeferred = CompletableDeferred<SurfaceRequest>()
925 
926             val preview = Preview.Builder().setTargetRotation(Surface.ROTATION_0).build()
927             preview.surfaceProvider =
928                 Preview.SurfaceProvider { request ->
929                     request.setTransformationInfoListener(CameraXExecutors.directExecutor()) {
930                         transformationInfoDeferred.complete(it)
931                     }
932                     request.provideSurface(
933                         Surface(SurfaceTexture(0)),
934                         CameraXExecutors.directExecutor()
935                     ) {}
936                     surfaceProvidedDeferred.complete(request)
937                 }
938 
939             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
940             surfaceProvidedDeferred.await()
941             var transformationInfo = withTimeoutOrNull(5000) { transformationInfoDeferred.await() }
942             assertThat(transformationInfo).isNotNull()
943             assertThat(transformationInfo!!.rotationDegrees).isEqualTo(90)
944 
945             transformationInfoDeferred = CompletableDeferred()
946             preview.targetRotation = Surface.ROTATION_90
947 
948             transformationInfo = withTimeoutOrNull(5000) { transformationInfoDeferred.await() }
949             assertThat(transformationInfo).isNotNull()
950             assertThat(transformationInfo!!.rotationDegrees).isEqualTo(0)
951         }
952 
953     @Test
954     fun viewPort_OverwriteTransformation() = runBlocking {
955         // Arrange.
956         val rotation =
957             if (CameraUtil.getSensorOrientation(CameraSelector.LENS_FACING_BACK)!! % 180 != 0)
958                 Surface.ROTATION_90
959             else Surface.ROTATION_0
960         val transformationInfoDeferred = CompletableDeferred<TransformationInfo>()
961         val preview = Preview.Builder().setTargetRotation(rotation).build()
962         val viewPort = ViewPort.Builder(Rational(2, 1), preview.targetRotation).build()
963 
964         // Act.
965         withContext(Dispatchers.Main) {
966             preview.setSurfaceProvider { request ->
967                 request.setTransformationInfoListener(CameraXExecutors.directExecutor()) {
968                     transformationInfoDeferred.complete(it)
969                 }
970             }
971             val useCaseGroup =
972                 UseCaseGroup.Builder().setViewPort(viewPort).addUseCase(preview).build()
973             cameraProvider.bindToLifecycle(
974                 lifecycleOwner,
975                 CameraSelector.DEFAULT_BACK_CAMERA,
976                 useCaseGroup
977             )
978         }
979         val transformationInfo = withTimeoutOrNull(5000) { transformationInfoDeferred.await() }
980 
981         // Assert.
982         assertThat(
983                 Rational(
984                         transformationInfo!!.cropRect.width(),
985                         transformationInfo.cropRect.height()
986                     )
987                     .toFloat()
988             )
989             .isWithin(TOLERANCE)
990             .of(viewPort.aspectRatio.toFloat())
991     }
992 
993     // ======================================================
994     // Section 3: UseCase Reusability Test
995     // ======================================================
996 
997     @Test
998     fun useCaseConfigCanBeReset_afterUnbind() =
999         runBlocking(Dispatchers.Main) {
1000             val preview = Preview.Builder().build()
1001             val initialConfig = preview.currentConfig
1002             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1003             cameraProvider.unbind(preview)
1004             val configAfterUnbinding = preview.currentConfig
1005             assertThat(initialConfig == configAfterUnbinding).isTrue()
1006         }
1007 
1008     @Test
1009     @Throws(InterruptedException::class)
1010     fun useCaseCanBeReusedInSameCamera() = runBlocking {
1011         val preview = Preview.Builder().build()
1012         var resultDeferred = CompletableDeferred<Int>()
1013 
1014         withContext(Dispatchers.Main) {
1015             preview.setSurfaceProvider(
1016                 getSurfaceProvider(
1017                     frameAvailableListener = { frameSemaphore!!.release() },
1018                     resultListener = { result ->
1019                         resultDeferred.completeOnceOnly(result.resultCode)
1020                     }
1021                 )
1022             )
1023             // This is the first time the use case bound to the lifecycle.
1024             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1025         }
1026 
1027         // Check the frame available callback is called.
1028         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1029         withContext(Dispatchers.Main) { cameraProvider.unbind(preview) }
1030 
1031         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() })
1032             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
1033 
1034         // Recreate the semaphore / deferred to monitor the frame available callback.
1035         frameSemaphore = Semaphore(/* permits= */ 0)
1036         // Recreate the resultDeferred to monitor the result listener again.
1037         resultDeferred = CompletableDeferred()
1038 
1039         withContext(Dispatchers.Main) {
1040             // Rebind the use case to the same camera.
1041             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1042         }
1043 
1044         // Check the frame available callback can be called after reusing the use case.
1045         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1046         withContext(Dispatchers.Main) { cameraProvider.unbind(preview) }
1047         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() })
1048             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
1049     }
1050 
1051     @Test
1052     @Throws(InterruptedException::class)
1053     fun useCaseCanBeReusedInDifferentCamera() = runBlocking {
1054         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT))
1055 
1056         val preview = Preview.Builder().build()
1057         var resultDeferred = CompletableDeferred<Int>()
1058         instrumentation.runOnMainSync {
1059             preview.setSurfaceProvider(
1060                 getSurfaceProvider(
1061                     frameAvailableListener = { frameSemaphore!!.release() },
1062                     resultListener = { result ->
1063                         resultDeferred.completeOnceOnly(result.resultCode)
1064                     }
1065                 )
1066             )
1067             // This is the first time the use case bound to the lifecycle.
1068             cameraProvider.bindToLifecycle(
1069                 lifecycleOwner,
1070                 CameraSelector.DEFAULT_BACK_CAMERA,
1071                 preview
1072             )
1073         }
1074 
1075         // Check the frame available callback is called.
1076         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1077         // Unbind and rebind the use case to the same lifecycle.
1078         instrumentation.runOnMainSync { cameraProvider.unbind(preview) }
1079 
1080         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() })
1081             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
1082 
1083         // Recreate the semaphore to monitor the frame available callback.
1084         frameSemaphore = Semaphore(/* permits= */ 0)
1085         // Recreate the resultDeferred to monitor the result listener again.
1086         resultDeferred = CompletableDeferred()
1087 
1088         instrumentation.runOnMainSync {
1089             // Rebind the use case to different camera.
1090             cameraProvider.bindToLifecycle(
1091                 lifecycleOwner,
1092                 CameraSelector.DEFAULT_FRONT_CAMERA,
1093                 preview
1094             )
1095         }
1096 
1097         // Check the frame available callback can be called after reusing the use case.
1098         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1099         withContext(Dispatchers.Main) { cameraProvider.unbind(preview) }
1100         assertThat(withTimeoutOrNull(RESULT_TIMEOUT) { resultDeferred.await() })
1101             .isEqualTo(SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY)
1102     }
1103 
1104     // ======================================================
1105     // Section 4: ResolutionSelector
1106     // ======================================================
1107 
1108     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
1109     @Test
1110     fun verifyHighResolutionIsDisabledForPreview() = runBlocking {
1111         val highResolutionOutputSizes =
1112             CameraInfoUtil.getHighResolutionOutputSizes(
1113                 cameraProvider.getCameraInfo(CameraSelector.DEFAULT_BACK_CAMERA),
1114                 ImageFormat.PRIVATE
1115             )
1116         // Only runs the test when the device has high resolution output sizes
1117         assumeTrue(highResolutionOutputSizes.isNotEmpty())
1118 
1119         // Arrange.
1120         // Sets the mode to allow high resolution support and uses a ResolutionFilter to verify the
1121         // high resolution output sizes are not included in the provided sizes list
1122         val resolutionSelector =
1123             ResolutionSelector.Builder()
1124                 .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE)
1125                 .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY)
1126                 .setResolutionFilter { outputSizes, _ ->
1127                     assertThat(outputSizes).containsNoneIn(highResolutionOutputSizes)
1128                     outputSizes
1129                 }
1130                 .build()
1131         val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build()
1132 
1133         withContext(Dispatchers.Main) {
1134             preview.setSurfaceProvider(
1135                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1136             )
1137             // Act.
1138             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1139         }
1140 
1141         // Assert.
1142         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1143     }
1144 
1145     @Test
1146     fun getsFrame_withHighestAvailableResolutionStrategy() = runBlocking {
1147         // Arrange.
1148         val resolutionSelector =
1149             ResolutionSelector.Builder()
1150                 .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY)
1151                 .build()
1152         val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build()
1153 
1154         withContext(Dispatchers.Main) {
1155             preview.setSurfaceProvider(
1156                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1157             )
1158             // Act.
1159             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1160         }
1161 
1162         // Assert.
1163         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1164     }
1165 
1166     @Test
1167     fun getsFrame_withAspectRatio_4_3_strategy() = runBlocking {
1168         assumeTrue(!hasExtraCroppingQuirk() && isAspectRatioResolutionSupported(4.0f / 3.0f))
1169         // Arrange.
1170         val resolutionSelector =
1171             ResolutionSelector.Builder()
1172                 .setAspectRatioStrategy(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
1173                 .build()
1174         val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build()
1175 
1176         withContext(Dispatchers.Main) {
1177             preview.setSurfaceProvider(
1178                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1179             )
1180             // Act.
1181             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1182         }
1183 
1184         // Assert.
1185         val resolution = preview.resolutionInfo!!.resolution
1186         assertThat(resolution.width.toFloat() / resolution.height)
1187             .isWithin(TOLERANCE)
1188             .of(4.0f / 3.0f)
1189         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1190     }
1191 
1192     @Test
1193     fun getsFrame_withAspectRatio_16_9_strategy() = runBlocking {
1194         assumeTrue(
1195             !hasAspectRatioLegacyApi21Quirk() && isAspectRatioResolutionSupported(16.0f / 9.0f)
1196         )
1197 
1198         // Arrange.
1199         val resolutionSelector =
1200             ResolutionSelector.Builder()
1201                 .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY)
1202                 .build()
1203         val preview = Preview.Builder().setResolutionSelector(resolutionSelector).build()
1204 
1205         withContext(Dispatchers.Main) {
1206             preview.setSurfaceProvider(
1207                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1208             )
1209             // Act.
1210             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1211         }
1212 
1213         // Assert.
1214         val resolution = preview.resolutionInfo!!.resolution
1215         assertThat(resolution.width.toFloat() / resolution.height)
1216             .isWithin(TOLERANCE)
1217             .of(16.0f / 9.0f)
1218         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1219     }
1220 
1221     @Test
1222     fun defaultMaxResolutionCanBeKept_whenResolutionStrategyIsNotSet() =
1223         runBlocking(Dispatchers.Main) {
1224             assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
1225             val useCase = Preview.Builder().build()
1226             cameraProvider.bindToLifecycle(
1227                 lifecycleOwner,
1228                 CameraSelector.DEFAULT_BACK_CAMERA,
1229                 useCase
1230             )
1231 
1232             assertThat(
1233                     useCase.currentConfig.containsOption(ImageOutputConfig.OPTION_MAX_RESOLUTION)
1234                 )
1235                 .isTrue()
1236         }
1237 
1238     @Test
1239     fun defaultMaxResolutionCanBeRemoved_whenResolutionStrategyIsSet() =
1240         runBlocking(Dispatchers.Main) {
1241             assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
1242             val useCase =
1243                 Preview.Builder()
1244                     .setResolutionSelector(
1245                         ResolutionSelector.Builder()
1246                             .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY)
1247                             .build()
1248                     )
1249                     .build()
1250 
1251             cameraProvider.bindToLifecycle(
1252                 lifecycleOwner,
1253                 CameraSelector.DEFAULT_BACK_CAMERA,
1254                 useCase
1255             )
1256             assertThat(
1257                     useCase.currentConfig.containsOption(ImageOutputConfig.OPTION_MAX_RESOLUTION)
1258                 )
1259                 .isFalse()
1260         }
1261 
1262     @Test
1263     fun resolutionSelectorConfigCorrectlyMerged_afterBindToLifecycle() =
1264         runBlocking(Dispatchers.Main) {
1265             val resolutionFilter = ResolutionFilter { supportedSizes, _ -> supportedSizes }
1266             val useCase =
1267                 Preview.Builder()
1268                     .setResolutionSelector(
1269                         ResolutionSelector.Builder()
1270                             .setResolutionFilter(resolutionFilter)
1271                             .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE)
1272                             .build()
1273                     )
1274                     .build()
1275             cameraProvider.bindToLifecycle(
1276                 lifecycleOwner,
1277                 CameraSelector.DEFAULT_BACK_CAMERA,
1278                 useCase
1279             )
1280 
1281             val resolutionSelector =
1282                 useCase.currentConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR)
1283             // The default 4:3 AspectRatioStrategy is kept
1284             assertThat(resolutionSelector!!.aspectRatioStrategy)
1285                 .isEqualTo(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
1286             // The default highest available ResolutionStrategy is kept
1287             assertThat(resolutionSelector.resolutionStrategy)
1288                 .isEqualTo(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY)
1289             // The set resolutionFilter is kept
1290             assertThat(resolutionSelector.resolutionFilter).isEqualTo(resolutionFilter)
1291             // The set allowedResolutionMode is kept
1292             assertThat(resolutionSelector.allowedResolutionMode)
1293                 .isEqualTo(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE)
1294         }
1295 
1296     // ======================================================
1297     // Section 5: Session error handling
1298     // ======================================================
1299 
1300     @Test
1301     fun sessionErrorListenerReceivesError_getsFrame(): Unit = runBlocking {
1302         // Arrange.
1303         val preview = Preview.Builder().build()
1304         withContext(Dispatchers.Main) {
1305             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1306             // Act.
1307             preview.surfaceProvider =
1308                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1309         }
1310 
1311         // Retrieves the initial session config
1312         val initialSessionConfig = preview.sessionConfig
1313 
1314         // Checks that image can be received successfully when onError is received.
1315         triggerOnErrorAndVerifyNewImageReceived(initialSessionConfig)
1316 
1317         // Rebinds to different camera
1318         if (CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_FRONT)) {
1319             withContext(Dispatchers.Main) {
1320                 cameraProvider.unbind(preview)
1321                 cameraProvider.bindToLifecycle(
1322                     lifecycleOwner,
1323                     CameraSelector.DEFAULT_FRONT_CAMERA,
1324                     preview
1325                 )
1326             }
1327 
1328             // Checks that image can be received successfully when onError is received by the old
1329             // error listener.
1330             triggerOnErrorAndVerifyNewImageReceived(initialSessionConfig)
1331         }
1332 
1333         val sessionConfigBeforeValidErrorNotification = preview.sessionConfig
1334         // Checks that image can be received successfully when onError is received by the new
1335         // error listener.
1336         triggerOnErrorAndVerifyNewImageReceived(preview.sessionConfig)
1337         // Checks that triggering onError to valid listener has recreated the pipeline
1338         assertThat(preview.sessionConfig).isNotEqualTo(sessionConfigBeforeValidErrorNotification)
1339     }
1340 
1341     private fun triggerOnErrorAndVerifyNewImageReceived(sessionConfig: SessionConfig) {
1342         frameSemaphore = Semaphore(0)
1343         // Forces invoke the onError callback
1344         runOnMainSync {
1345             sessionConfig.errorListener!!.onError(
1346                 sessionConfig,
1347                 SessionConfig.SessionError.SESSION_ERROR_UNKNOWN
1348             )
1349         }
1350         // Assert.
1351         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1352     }
1353 
1354     // ======================================================
1355     // Section 6: Preview Stabilization, Dynamic Range, Frame rate
1356     // ======================================================
1357     @Test
1358     fun previewStabilizationIsSetCorrectly(): Unit = runBlocking {
1359         val preview = Preview.Builder().setPreviewStabilizationEnabled(true).build()
1360         assertThat(preview.isPreviewStabilizationEnabled).isTrue()
1361     }
1362 
1363     private fun isPreviewStabilizationModeSupported(cameraSelector: CameraSelector): Boolean {
1364         val cameraInfoInternal = cameraProvider.getCameraInfo(cameraSelector) as CameraInfoInternal
1365         val cameraCharacteristics =
1366             cameraInfoInternal.cameraCharacteristics as CameraCharacteristics
1367         val stabilizationModes =
1368             cameraCharacteristics.get(
1369                 CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES
1370             ) as IntArray
1371         return stabilizationModes.contains(
1372             CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
1373         )
1374     }
1375 
1376     @Test
1377     fun getPreviewCapabilitiesStabilizationSupportIsCorrect() {
1378         val capabilities =
1379             Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1380 
1381         assertThat(capabilities.isStabilizationSupported())
1382             .isEqualTo(isPreviewStabilizationModeSupported(cameraSelector))
1383     }
1384 
1385     @Test
1386     fun previewStabilizationOn_videoStabilizationModeIsPreviewStabilization(): Unit = runBlocking {
1387         val previewCapabilities =
1388             Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1389         assumeTrue(previewCapabilities.isStabilizationSupported)
1390 
1391         val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true)
1392         verifyVideoStabilizationModeInResultAndFramesAvailable(
1393             cameraSelector = cameraSelector,
1394             previewBuilder = previewBuilder,
1395             expectedMode = CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
1396         )
1397     }
1398 
1399     @Test
1400     fun previewStabilizationOnAndVideoOff_videoStabilizationModeIsOff(): Unit = runBlocking {
1401         val previewCapabilities =
1402             Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1403         val videoCaptureCapabilities =
1404             Recorder.getVideoCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1405         assumeTrue(
1406             previewCapabilities.isStabilizationSupported &&
1407                 videoCaptureCapabilities.isStabilizationSupported
1408         )
1409 
1410         val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true)
1411         val videoCapture =
1412             VideoCapture.Builder(Recorder.Builder().build())
1413                 .setVideoStabilizationEnabled(false)
1414                 .build()
1415 
1416         verifyVideoStabilizationModeInResultAndFramesAvailable(
1417             cameraSelector = cameraSelector,
1418             previewBuilder = previewBuilder,
1419             videoCapture = videoCapture,
1420             expectedMode = CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_OFF
1421         )
1422     }
1423 
1424     @Test
1425     fun previewStabilizationOnAndVideoOn_videoStabilizationModeIsPreviewStabilization(): Unit =
1426         runBlocking {
1427             val previewCapabilities =
1428                 Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1429             val videoCaptureCapabilities =
1430                 Recorder.getVideoCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1431             assumeTrue(
1432                 previewCapabilities.isStabilizationSupported &&
1433                     videoCaptureCapabilities.isStabilizationSupported
1434             )
1435 
1436             val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true)
1437             val videoCapture =
1438                 VideoCapture.Builder(Recorder.Builder().build())
1439                     .setVideoStabilizationEnabled(true)
1440                     .build()
1441 
1442             verifyVideoStabilizationModeInResultAndFramesAvailable(
1443                 cameraSelector = cameraSelector,
1444                 previewBuilder = previewBuilder,
1445                 videoCapture = videoCapture,
1446                 expectedMode = CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
1447             )
1448         }
1449 
1450     @Test
1451     @SdkSuppress(minSdkVersion = 23)
1452     fun getPreviewCapabilitiesStabilizationSupportIsCorrect_whenNotSupportedInExtensions() {
1453         assumeTrue(isPreviewStabilizationModeSupported(CameraSelector.DEFAULT_BACK_CAMERA))
1454         val sessionProcessor =
1455             FakeSessionProcessor(
1456                 extensionSpecificChars =
1457                     listOf(
1458                         android.util.Pair(
1459                             CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
1460                             intArrayOf(CameraCharacteristics.CONTROL_VIDEO_STABILIZATION_MODE_OFF)
1461                         )
1462                     )
1463             )
1464         val cameraSelector =
1465             ExtensionsUtil.getCameraSelectorWithSessionProcessor(
1466                 cameraProvider,
1467                 CameraSelector.DEFAULT_BACK_CAMERA,
1468                 sessionProcessor
1469             )
1470         val capabilities =
1471             Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1472 
1473         assertThat(capabilities.isStabilizationSupported()).isFalse()
1474     }
1475 
1476     @Test
1477     @SdkSuppress(minSdkVersion = 23)
1478     fun getPreviewCapabilitiesStabilizationSupportIsCorrect_whenSupportedInExtensions() {
1479         assumeFalse(isPreviewStabilizationModeSupported(CameraSelector.DEFAULT_BACK_CAMERA))
1480         val sessionProcessor =
1481             FakeSessionProcessor(
1482                 extensionSpecificChars =
1483                     listOf(
1484                         android.util.Pair(
1485                             CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
1486                             intArrayOf(
1487                                 CameraCharacteristics
1488                                     .CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
1489                             )
1490                         )
1491                     )
1492             )
1493         val cameraSelector =
1494             ExtensionsUtil.getCameraSelectorWithSessionProcessor(
1495                 cameraProvider,
1496                 CameraSelector.DEFAULT_BACK_CAMERA,
1497                 sessionProcessor
1498             )
1499         val capabilities =
1500             Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
1501 
1502         assertThat(capabilities.isStabilizationSupported()).isTrue()
1503     }
1504 
1505     @Test
1506     @SdkSuppress(minSdkVersion = 23)
1507     fun previewStabilizationCanBeSet_whenSupportedInExtensions() = runBlocking {
1508         assumeTrue(isPreviewStabilizationModeSupported(CameraSelector.DEFAULT_BACK_CAMERA))
1509         val sessionProcessor =
1510             FakeSessionProcessor(
1511                 extensionSpecificChars =
1512                     listOf(
1513                         android.util.Pair(
1514                             CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
1515                             intArrayOf(
1516                                 CameraCharacteristics
1517                                     .CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
1518                             )
1519                         )
1520                     )
1521             )
1522         val cameraSelectorWithExtensions =
1523             ExtensionsUtil.getCameraSelectorWithSessionProcessor(
1524                 cameraProvider,
1525                 CameraSelector.DEFAULT_BACK_CAMERA,
1526                 sessionProcessor
1527             )
1528 
1529         val previewBuilder = Preview.Builder().setPreviewStabilizationEnabled(true)
1530         verifyVideoStabilizationModeInResultAndFramesAvailable(
1531             cameraSelector = cameraSelectorWithExtensions,
1532             previewBuilder = previewBuilder,
1533             expectedMode = CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
1534         )
1535     }
1536 
1537     @Test
1538     @SdkSuppress(minSdkVersion = 21, maxSdkVersion = 32)
1539     fun setMirrorModeIsNoOp_priorToAPI33() = runBlocking {
1540         // Skip for b/404348154
1541         assumeFalse("Skip test for API 26.", Build.VERSION.SDK_INT == 26)
1542         verifyMirrorMode(
1543             CameraSelector.DEFAULT_BACK_CAMERA,
1544             mirrorMode = MirrorMode.MIRROR_MODE_ON,
1545             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1546             expectedIsMirroringInTransformationInfo = false
1547         )
1548 
1549         verifyMirrorMode(
1550             CameraSelector.DEFAULT_FRONT_CAMERA,
1551             mirrorMode = MirrorMode.MIRROR_MODE_ON,
1552             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1553             expectedIsMirroringInTransformationInfo = true
1554         )
1555     }
1556 
1557     @Test
1558     @SdkSuppress(minSdkVersion = 33)
1559     fun defaultMirrorMode() = runBlocking {
1560         verifyMirrorMode(
1561             CameraSelector.DEFAULT_BACK_CAMERA,
1562             mirrorMode = null, // don't set the mirror mode
1563             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1564             expectedIsMirroringInTransformationInfo = false
1565         )
1566 
1567         verifyMirrorMode(
1568             CameraSelector.DEFAULT_FRONT_CAMERA,
1569             mirrorMode = null, // don't set the mirror mode
1570             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1571             expectedIsMirroringInTransformationInfo = true
1572         )
1573     }
1574 
1575     @Test
1576     @SdkSuppress(minSdkVersion = 33)
1577     fun mirrorModeOn() = runBlocking {
1578         verifyMirrorMode(
1579             CameraSelector.DEFAULT_BACK_CAMERA,
1580             mirrorMode = MirrorMode.MIRROR_MODE_ON,
1581             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON,
1582             expectedIsMirroringInTransformationInfo = true
1583         )
1584 
1585         verifyMirrorMode(
1586             CameraSelector.DEFAULT_FRONT_CAMERA,
1587             mirrorMode = MirrorMode.MIRROR_MODE_ON,
1588             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON,
1589             expectedIsMirroringInTransformationInfo = true
1590         )
1591     }
1592 
1593     @Test
1594     @SdkSuppress(minSdkVersion = 33)
1595     fun mirrorModeOff() = runBlocking {
1596         verifyMirrorMode(
1597             CameraSelector.DEFAULT_BACK_CAMERA,
1598             mirrorMode = MirrorMode.MIRROR_MODE_OFF,
1599             expectedMirrorMode = MirrorMode.MIRROR_MODE_OFF,
1600             expectedIsMirroringInTransformationInfo = false
1601         )
1602 
1603         verifyMirrorMode(
1604             CameraSelector.DEFAULT_FRONT_CAMERA,
1605             mirrorMode = MirrorMode.MIRROR_MODE_OFF,
1606             expectedMirrorMode = MirrorMode.MIRROR_MODE_OFF,
1607             expectedIsMirroringInTransformationInfo = false
1608         )
1609     }
1610 
1611     @Test
1612     @SdkSuppress(minSdkVersion = 33)
1613     fun mirrorModeOnFrontOnly() = runBlocking {
1614         verifyMirrorMode(
1615             CameraSelector.DEFAULT_BACK_CAMERA,
1616             mirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1617             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1618             expectedIsMirroringInTransformationInfo = false
1619         )
1620 
1621         verifyMirrorMode(
1622             CameraSelector.DEFAULT_FRONT_CAMERA,
1623             MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1624             expectedMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY,
1625             expectedIsMirroringInTransformationInfo = true
1626         )
1627     }
1628 
1629     private suspend fun verifyMirrorMode(
1630         cameraSelector: CameraSelector,
1631         mirrorMode: Int? = null,
1632         expectedMirrorMode: Int,
1633         expectedIsMirroringInTransformationInfo: Boolean
1634     ) {
1635         val preview =
1636             Preview.Builder()
1637                 .also { builder -> mirrorMode?.let { builder.setMirrorMode(it) } }
1638                 .build()
1639 
1640         val transformationInfoDeferred = CompletableDeferred<TransformationInfo>()
1641         withContext(Dispatchers.Main) {
1642             cameraProvider.unbindAll()
1643             preview.surfaceProvider =
1644                 SurfaceTextureProvider.createAutoDrainingSurfaceTextureProvider(
1645                     null,
1646                     { surfaceRequest ->
1647                         surfaceRequest.setTransformationInfoListener(
1648                             CameraXExecutors.directExecutor(),
1649                             { transformationInfo ->
1650                                 transformationInfoDeferred.complete(transformationInfo)
1651                             }
1652                         )
1653                     },
1654                     {}
1655                 )
1656             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1657         }
1658 
1659         // TODO: better check the Camera2 OutputConfiguration but currently there is no way do that.
1660         assertThat(preview.sessionConfig.outputConfigs.get(0).mirrorMode)
1661             .isEqualTo(expectedMirrorMode)
1662         assertThat(withTimeoutOrNull(1000) { transformationInfoDeferred.await() }!!.isMirroring)
1663             .isEqualTo(expectedIsMirroringInTransformationInfo)
1664     }
1665 
1666     private suspend fun verifyVideoStabilizationModeInResultAndFramesAvailable(
1667         cameraSelector: CameraSelector,
1668         previewBuilder: Preview.Builder,
1669         videoCapture: VideoCapture<Recorder>? = null,
1670         expectedMode: Int
1671     ) {
1672         val captureResultDeferred = CompletableDeferred<TotalCaptureResult>()
1673         Camera2Interop.Extender(previewBuilder)
1674             .setSessionCaptureCallback(
1675                 object : CaptureCallback() {
1676                     override fun onCaptureCompleted(
1677                         session: CameraCaptureSession,
1678                         request: CaptureRequest,
1679                         result: TotalCaptureResult
1680                     ) {
1681                         captureResultDeferred.complete(result)
1682                     }
1683                 }
1684             )
1685         val useCaseGroupBuilder = UseCaseGroup.Builder()
1686         val preview = previewBuilder.build()
1687         useCaseGroupBuilder.addUseCase(preview)
1688         videoCapture?.let { useCaseGroupBuilder.addUseCase(it) }
1689 
1690         withContext(Dispatchers.Main) {
1691             preview.surfaceProvider =
1692                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1693             cameraProvider.bindToLifecycle(
1694                 lifecycleOwner,
1695                 cameraSelector,
1696                 useCaseGroupBuilder.build()
1697             )
1698         }
1699 
1700         assertThat(
1701                 withTimeoutOrNull(5000) { captureResultDeferred.await() }!!
1702                     .request
1703                     .get(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE)
1704             )
1705             .isEqualTo(expectedMode)
1706         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1707     }
1708 
1709     @Test
1710     fun dynamicRange_HLG10BIT(): Unit = runBlocking {
1711         assumeTrue(
1712             cameraProvider
1713                 .getCameraInfo(cameraSelector)
1714                 .querySupportedDynamicRanges(setOf(DynamicRange.HLG_10_BIT))
1715                 .contains(DynamicRange.HLG_10_BIT)
1716         )
1717 
1718         val preview = Preview.Builder().setDynamicRange(DynamicRange.HLG_10_BIT).build()
1719         withContext(Dispatchers.Main) {
1720             preview.surfaceProvider =
1721                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1722             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1723         }
1724 
1725         assertThat(preview.dynamicRange).isEqualTo(DynamicRange.HLG_10_BIT)
1726         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 5)
1727     }
1728 
1729     @Test
1730     fun dynamicRangeIsSetInSurfaceRequest(): Unit = runBlocking {
1731         assumeTrue(
1732             cameraProvider
1733                 .getCameraInfo(cameraSelector)
1734                 .querySupportedDynamicRanges(setOf(DynamicRange.HLG_10_BIT))
1735                 .contains(DynamicRange.HLG_10_BIT)
1736         )
1737 
1738         val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>()
1739         val preview = Preview.Builder().setDynamicRange(DynamicRange.HLG_10_BIT).build()
1740         withContext(Dispatchers.Main) {
1741             preview.surfaceProvider =
1742                 Preview.SurfaceProvider { surfaceRequest ->
1743                     surfaceRequestDeferred.complete(surfaceRequest)
1744                 }
1745             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1746         }
1747 
1748         assertThat(withTimeoutOrNull(3000) { surfaceRequestDeferred.await() }!!.dynamicRange)
1749             .isEqualTo(DynamicRange.HLG_10_BIT)
1750     }
1751 
1752     @Test
1753     fun dynamicRangeIsNotSet_SDRInSurfaceRequest(): Unit = runBlocking {
1754         val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>()
1755         val preview = Preview.Builder().build()
1756         withContext(Dispatchers.Main) {
1757             preview.surfaceProvider =
1758                 Preview.SurfaceProvider { surfaceRequest ->
1759                     surfaceRequestDeferred.complete(surfaceRequest)
1760                 }
1761             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1762         }
1763 
1764         assertThat(withTimeoutOrNull(3000) { surfaceRequestDeferred.await() }!!.dynamicRange)
1765             .isEqualTo(DynamicRange.SDR)
1766     }
1767 
1768     @Test
1769     fun canSetFrameRate30_30(): Unit = runBlocking {
1770         val fpsToVerify = Range(30, 30)
1771         assumeTrue(
1772             cameraProvider
1773                 .getCameraInfo(cameraSelector)
1774                 .supportedFrameRateRanges
1775                 .contains(fpsToVerify)
1776         )
1777         val previewBuilder = Preview.Builder().setTargetFrameRate(fpsToVerify)
1778         verifyFrameRateRangeInResultAndFramesAvailable(
1779             previewBuilder = previewBuilder,
1780             expectedFpsRange = fpsToVerify
1781         )
1782     }
1783 
1784     @Test
1785     fun frameRateIsSetInSurfaceRequest(): Unit = runBlocking {
1786         val fpsToVerify = Range(30, 30)
1787         assumeTrue(
1788             cameraProvider
1789                 .getCameraInfo(cameraSelector)
1790                 .supportedFrameRateRanges
1791                 .contains(fpsToVerify)
1792         )
1793         val previewBuilder = Preview.Builder().setTargetFrameRate(fpsToVerify)
1794 
1795         val preview = previewBuilder.build()
1796         val surfaceRequestDeferred = CompletableDeferred<SurfaceRequest>()
1797 
1798         withContext(Dispatchers.Main) {
1799             preview.surfaceProvider =
1800                 Preview.SurfaceProvider { surfaceRequest ->
1801                     surfaceRequestDeferred.complete(surfaceRequest)
1802                 }
1803             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1804         }
1805         assertThat(withTimeoutOrNull(3000) { surfaceRequestDeferred.await() }!!.expectedFrameRate)
1806             .isEqualTo(fpsToVerify)
1807     }
1808 
1809     @Test
1810     fun canSetFrameRate60_60(): Unit = runBlocking {
1811         val fpsToVerify = Range(60, 60)
1812         assumeTrue(
1813             cameraProvider
1814                 .getCameraInfo(cameraSelector)
1815                 .supportedFrameRateRanges
1816                 .contains(fpsToVerify)
1817         )
1818         val previewBuilder = Preview.Builder().setTargetFrameRate(fpsToVerify)
1819 
1820         verifyFrameRateRangeInResultAndFramesAvailable(
1821             previewBuilder = previewBuilder,
1822             expectedFpsRange = fpsToVerify
1823         )
1824     }
1825 
1826     private suspend fun verifyFrameRateRangeInResultAndFramesAvailable(
1827         previewBuilder: Preview.Builder,
1828         expectedFpsRange: Range<Int>
1829     ) {
1830         val captureResultDeferred = CompletableDeferred<TotalCaptureResult>()
1831         Camera2Interop.Extender(previewBuilder)
1832             .setSessionCaptureCallback(
1833                 object : CaptureCallback() {
1834                     override fun onCaptureCompleted(
1835                         session: CameraCaptureSession,
1836                         request: CaptureRequest,
1837                         result: TotalCaptureResult
1838                     ) {
1839                         captureResultDeferred.complete(result)
1840                     }
1841                 }
1842             )
1843         val preview = previewBuilder.build()
1844         assertThat(preview.targetFrameRate).isEqualTo(expectedFpsRange)
1845 
1846         withContext(Dispatchers.Main) {
1847             preview.surfaceProvider =
1848                 getSurfaceProvider(frameAvailableListener = { frameSemaphore!!.release() })
1849             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
1850         }
1851 
1852         val captureResult = withTimeoutOrNull(5000) { captureResultDeferred.await() }
1853         assertThat(captureResult).isNotNull()
1854         val fpsRangeInResult =
1855             captureResult!!.request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE)
1856         // ignore the case CONTROL_AE_TARGET_FPS_RANGE is null
1857         assumeTrue(fpsRangeInResult != null)
1858         assertThat(fpsRangeInResult).isEqualTo(expectedFpsRange)
1859         frameSemaphore!!.verifyFramesReceived(frameCount = FRAMES_TO_VERIFY, timeoutInSeconds = 10)
1860     }
1861 
1862     private val workExecutorWithNamedThread: Executor
1863         get() {
1864             val threadFactory = ThreadFactory { runnable: Runnable? ->
1865                 Thread(runnable, ANY_THREAD_NAME)
1866             }
1867             return Executors.newSingleThreadExecutor(threadFactory)
1868         }
1869 
1870     private fun getSurfaceProvider(
1871         threadNameConsumer: Consumer<String>? = null,
1872         resultListener: Consumer<SurfaceRequest.Result>? = null,
1873         frameAvailableListener: SurfaceTexture.OnFrameAvailableListener? = null
1874     ): Preview.SurfaceProvider {
1875         return SurfaceTextureProvider.createAutoDrainingSurfaceTextureProvider(
1876             frameAvailableListener,
1877             { surfaceRequest ->
1878                 previewResolution = surfaceRequest.resolution
1879                 threadNameConsumer?.accept(Thread.currentThread().name)
1880             },
1881             resultListener
1882         )
1883     }
1884 
1885     /*
1886      * Check if there is no frame callback for `noFrameIntervalMs` milliseconds, then it will
1887      * return true; If the total check time is over `timeoutMs` milliseconds, then it will return
1888      * false.
1889      */
1890     @Throws(InterruptedException::class)
1891     private fun noFrameCome(noFrameIntervalMs: Long, timeoutMs: Long): Boolean {
1892         require(!(noFrameIntervalMs <= 0 || timeoutMs <= 0)) { "Time can't be negative value." }
1893         require(timeoutMs >= noFrameIntervalMs) {
1894             "timeoutMs should be larger than noFrameIntervalMs."
1895         }
1896         val checkFrequency = 200L
1897         var totalCheckTime = 0L
1898         var zeroFrameTimer = 0L
1899         do {
1900             Thread.sleep(checkFrequency)
1901             if (frameSemaphore!!.availablePermits() > 0) {
1902                 // Has frame, reset timer and frame count.
1903                 zeroFrameTimer = 0
1904                 frameSemaphore!!.drainPermits()
1905             } else {
1906                 zeroFrameTimer += checkFrequency
1907             }
1908             if (zeroFrameTimer > noFrameIntervalMs) {
1909                 return true
1910             }
1911             totalCheckTime += checkFrequency
1912         } while (totalCheckTime < timeoutMs)
1913         return false
1914     }
1915 
1916     private fun Semaphore.verifyFramesReceived(frameCount: Int, timeoutInSeconds: Long = 10) {
1917         assertThat(this.tryAcquire(frameCount, timeoutInSeconds, TimeUnit.SECONDS)).isTrue()
1918     }
1919 }
1920