1 /*
<lambda>null2  * Copyright 2020 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.extensions
18 
19 import android.content.Context
20 import android.hardware.camera2.CameraCharacteristics
21 import android.hardware.camera2.CaptureRequest
22 import android.util.Pair
23 import android.util.Range
24 import android.util.Size
25 import androidx.camera.core.Camera
26 import androidx.camera.core.CameraInfo
27 import androidx.camera.core.CameraSelector
28 import androidx.camera.core.CameraXConfig
29 import androidx.camera.core.impl.AdapterCameraInfo
30 import androidx.camera.core.impl.CameraInfoInternal
31 import androidx.camera.core.impl.SessionProcessor
32 import androidx.camera.extensions.impl.ExtensionsTestlibControl
33 import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl
34 import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl
35 import androidx.camera.extensions.impl.advanced.OutputSurfaceConfigurationImpl
36 import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl
37 import androidx.camera.extensions.impl.advanced.RequestProcessorImpl
38 import androidx.camera.extensions.impl.advanced.SessionProcessorImpl
39 import androidx.camera.extensions.internal.Camera2ExtensionsUtil.shouldUseCamera2Extensions
40 import androidx.camera.extensions.internal.ClientVersion
41 import androidx.camera.extensions.internal.ExtensionVersion
42 import androidx.camera.extensions.internal.ExtensionsUtils
43 import androidx.camera.extensions.internal.VendorExtender
44 import androidx.camera.extensions.internal.Version
45 import androidx.camera.extensions.internal.sessionprocessor.AdvancedSessionProcessor
46 import androidx.camera.extensions.internal.sessionprocessor.BasicExtenderSessionProcessor
47 import androidx.camera.extensions.internal.sessionprocessor.Camera2ExtensionsSessionProcessor
48 import androidx.camera.extensions.util.ExtensionsTestUtil
49 import androidx.camera.extensions.util.ExtensionsTestUtil.CAMERA_PIPE_IMPLEMENTATION_OPTION
50 import androidx.camera.lifecycle.ProcessCameraProvider
51 import androidx.camera.testing.impl.CameraPipeConfigTestRule
52 import androidx.camera.testing.impl.CameraUtil
53 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
54 import androidx.camera.testing.impl.fakes.FakeUseCase
55 import androidx.test.core.app.ApplicationProvider
56 import androidx.test.filters.SdkSuppress
57 import androidx.test.filters.SmallTest
58 import androidx.test.platform.app.InstrumentationRegistry
59 import androidx.testutils.assertThrows
60 import com.google.common.truth.Truth.assertThat
61 import java.util.Collections
62 import java.util.concurrent.TimeUnit
63 import kotlinx.coroutines.Dispatchers
64 import kotlinx.coroutines.runBlocking
65 import kotlinx.coroutines.withContext
66 import org.junit.After
67 import org.junit.Assume.assumeFalse
68 import org.junit.Assume.assumeTrue
69 import org.junit.Before
70 import org.junit.Rule
71 import org.junit.Test
72 import org.junit.runner.RunWith
73 import org.junit.runners.Parameterized
74 
75 @SmallTest
76 @RunWith(Parameterized::class)
77 @SdkSuppress(minSdkVersion = 21)
78 class ExtensionsManagerTest(
79     private val implName: String,
80     private val cameraXConfig: CameraXConfig,
81     private val implType: ExtensionsTestlibControl.ImplementationType,
82     @field:ExtensionMode.Mode @param:ExtensionMode.Mode private val extensionMode: Int,
83     @field:CameraSelector.LensFacing @param:CameraSelector.LensFacing private val lensFacing: Int
84 ) {
85     @get:Rule
86     val cameraPipeConfigTestRule =
87         CameraPipeConfigTestRule(active = implName == CAMERA_PIPE_IMPLEMENTATION_OPTION)
88 
89     private val context = InstrumentationRegistry.getInstrumentation().context
90 
91     private lateinit var cameraProvider: ProcessCameraProvider
92 
93     private lateinit var extensionsManager: ExtensionsManager
94 
95     private lateinit var baseCameraSelector: CameraSelector
96 
97     @Before
98     @Throws(Exception::class)
99     fun setUp() {
100         assumeTrue(
101             ExtensionsTestUtil.isTargetDeviceAvailableForExtensions(lensFacing, extensionMode)
102         )
103 
104         ProcessCameraProvider.configureInstance(cameraXConfig)
105         cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
106 
107         assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
108 
109         baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
110         ExtensionsTestlibControl.getInstance().setImplementationType(implType)
111     }
112 
113     @After
114     fun teardown(): Unit = runBlocking {
115         if (::cameraProvider.isInitialized) {
116             cameraProvider.shutdownAsync()[10000, TimeUnit.MILLISECONDS]
117         }
118 
119         if (::extensionsManager.isInitialized) {
120             extensionsManager.shutdown()[10000, TimeUnit.MILLISECONDS]
121         }
122     }
123 
124     companion object {
125         val context: Context = ApplicationProvider.getApplicationContext()
126 
127         @JvmStatic
128         @Parameterized.Parameters(
129             name = "cameraXConfig = {0}, implType = {2}, mode = {3}, facing = {4}"
130         )
131         fun data(): Collection<Array<Any>> {
132             return ExtensionsTestUtil.getAllImplExtensionsLensFacingCombinations(context, false)
133         }
134     }
135 
136     @Test
137     fun getInstanceSuccessfully_whenExtensionAvailabilityIsNotAvailable() {
138         extensionsManager =
139             ExtensionsManager.getInstanceAsync(context, cameraProvider, ClientVersion("99.0.0"))[
140                     10000, TimeUnit.MILLISECONDS]
141 
142         assumeTrue(
143             extensionsManager.extensionsAvailability !=
144                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
145         )
146         assertThat(extensionsManager).isNotNull()
147     }
148 
149     @Test
150     fun getExtensionsCameraSelectorThrowsException_whenExtensionAvailabilityIsNotAvailable() {
151         extensionsManager =
152             ExtensionsManager.getInstanceAsync(context, cameraProvider, ClientVersion("99.0.0"))[
153                     10000, TimeUnit.MILLISECONDS]
154 
155         assumeTrue(
156             extensionsManager.extensionsAvailability !=
157                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
158         )
159 
160         val baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
161 
162         assertThrows<IllegalArgumentException> {
163             extensionsManager.getExtensionEnabledCameraSelector(baseCameraSelector, extensionMode)
164         }
165     }
166 
167     @Test
168     fun getExtensionsCameraSelectorThrowsException_whenExtensionModeIsNotSupported() {
169         extensionsManager =
170             ExtensionsManager.getInstanceAsync(context, cameraProvider)[
171                     10000, TimeUnit.MILLISECONDS]
172         val baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
173 
174         assumeFalse(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
175 
176         assertThrows<IllegalArgumentException> {
177             extensionsManager.getExtensionEnabledCameraSelector(baseCameraSelector, extensionMode)
178         }
179     }
180 
181     @Test
182     fun returnNewCameraSelector_whenExtensionModeIsSupprted() {
183         checkExtensionAvailabilityAndInit()
184 
185         val resultCameraSelector =
186             extensionsManager.getExtensionEnabledCameraSelector(baseCameraSelector, extensionMode)
187         assertThat(resultCameraSelector).isNotNull()
188         assertThat(resultCameraSelector).isNotEqualTo(baseCameraSelector)
189     }
190 
191     @Test
192     fun correctAvailability_whenExtensionIsNotAvailable() {
193         // Skips the test if extensions availability is disabled by quirk.
194         assumeFalse(
195             ExtensionsTestUtil.extensionsDisabledByQuirk(
196                 CameraUtil.getCameraIdWithLensFacing(lensFacing)!!
197             )
198         )
199 
200         extensionsManager =
201             ExtensionsManager.getInstanceAsync(context, cameraProvider)[
202                     10000, TimeUnit.MILLISECONDS]
203         val baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
204 
205         assumeFalse(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
206 
207         for (cameraInfo in cameraProvider.availableCameraInfos) {
208             val characteristics =
209                 (cameraInfo as CameraInfoInternal).cameraCharacteristics as CameraCharacteristics
210             // Checks lens facing first
211             val currentLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
212             if (currentLensFacing != lensFacing) {
213                 continue
214             }
215 
216             // Checks whether the specified extension mode is available by camera info and it
217             // must be false
218             assertThat(isExtensionAvailableByCameraInfo(cameraInfo)).isFalse()
219         }
220     }
221 
222     @Test
223     fun filterCorrectCamera_whenExtensionIsAvailable(): Unit = runBlocking {
224         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
225 
226         // Calls bind to lifecycle to get the selected camera
227         lateinit var camera: Camera
228         withContext(Dispatchers.Main) {
229             camera = cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
230         }
231 
232         val cameraId = (camera.cameraInfo as CameraInfoInternal).cameraId
233 
234         // Checks each camera in the available camera list that the selected camera must be the
235         // first one supporting the specified extension mode in the same lens facing
236         for (cameraInfo in cameraProvider.availableCameraInfos) {
237             val characteristics =
238                 (cameraInfo as CameraInfoInternal).cameraCharacteristics as CameraCharacteristics
239 
240             // Checks lens facing first
241             val currentLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
242             if (currentLensFacing != lensFacing) {
243                 continue
244             }
245 
246             // Checks whether the specified extension mode is available by camera info
247             val isSupported = isExtensionAvailableByCameraInfo(cameraInfo)
248             val currentCameraId = cameraInfo.cameraId
249 
250             if (currentCameraId.equals(cameraId)) {
251                 assertThat(isSupported).isTrue()
252                 break
253             } else {
254                 // Any other camera in front of the selected camera in the available cameras list
255                 // must not support the specified extension mode.
256                 assertThat(isSupported).isFalse()
257             }
258         }
259     }
260 
261     @Test
262     fun correctCameraConfigIsSet_withSupportedExtensionCameraSelector(): Unit = runBlocking {
263         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
264 
265         lateinit var camera: Camera
266         withContext(Dispatchers.Main) {
267             camera = cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
268         }
269 
270         var extensionsConfig = camera.extendedConfig as ExtensionsConfig
271         assertThat(extensionsConfig.extensionMode).isEqualTo(extensionMode)
272     }
273 
274     @Test
275     fun getEstimatedCaptureLatencyRange_returnValueFromExtender() {
276         extensionsManager =
277             ExtensionsManager.getInstanceAsync(
278                     context,
279                     cameraProvider,
280                 )[10000, TimeUnit.MILLISECONDS]
281 
282         assumeTrue(
283             extensionsManager.extensionsAvailability ==
284                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
285         )
286         // Skips the test when the extension version is 1.1 or below. It is the case that the
287         // device has its own implementation and ExtensionsInfo will directly return null to impact
288         // the test result.
289         assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_2)
290 
291         val estimatedCaptureLatency = Range(100L, 1000L)
292 
293         val fakeVendorExtender =
294             object : VendorExtender {
295                 override fun isExtensionAvailable(
296                     cameraId: String,
297                     characteristicsMap: MutableMap<String, CameraCharacteristics>
298                 ): Boolean {
299                     return true
300                 }
301 
302                 override fun getEstimatedCaptureLatencyRange(size: Size?): Range<Long> {
303                     return estimatedCaptureLatency
304                 }
305             }
306         extensionsManager.setVendorExtenderFactory { _, _ -> fakeVendorExtender }
307 
308         assertThat(
309                 extensionsManager.getEstimatedCaptureLatencyRange(baseCameraSelector, extensionMode)
310             )
311             .isEqualTo(estimatedCaptureLatency)
312     }
313 
314     @Test
315     fun getEstimatedCaptureLatencyRangeReturnNull_whenExtensionAvailabilityIsNotAvailable() {
316         extensionsManager =
317             ExtensionsManager.getInstanceAsync(context, cameraProvider, ClientVersion("99.0.0"))[
318                     10000, TimeUnit.MILLISECONDS]
319 
320         assumeTrue(
321             extensionsManager.extensionsAvailability !=
322                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
323         )
324 
325         assertThat(
326                 extensionsManager.getEstimatedCaptureLatencyRange(baseCameraSelector, extensionMode)
327             )
328             .isNull()
329     }
330 
331     @Test
332     fun getEstimatedCaptureLatencyRangeReturnNull_belowVersion1_2() {
333         assumeTrue(ExtensionVersion.getRuntimeVersion()!!.compareTo(Version.VERSION_1_2) < 0)
334 
335         checkExtensionAvailabilityAndInit()
336 
337         // This call should not cause any exception even if the vendor library doesn't implement
338         // the getEstimatedCaptureLatencyRange function.
339         val latencyInfo =
340             extensionsManager.getEstimatedCaptureLatencyRange(baseCameraSelector, extensionMode)
341 
342         assertThat(latencyInfo).isNull()
343     }
344 
345     @Test
346     fun getEstimatedCaptureLatencyRangeReturnsNull_whenNoCameraCanBeFound() {
347         checkExtensionAvailabilityAndInit()
348 
349         val emptyCameraSelector =
350             CameraSelector.Builder().addCameraFilter { _ -> ArrayList<CameraInfo>() }.build()
351 
352         assertThat(
353                 extensionsManager.getEstimatedCaptureLatencyRange(
354                     emptyCameraSelector,
355                     extensionMode
356                 )
357             )
358             .isNull()
359     }
360 
361     @Test
362     fun canSetExtensionsConfig_whenNoUseCase(): Unit = runBlocking {
363         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
364 
365         withContext(Dispatchers.Main) {
366             cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
367         }
368     }
369 
370     @Test
371     fun canNotSetExtensionsConfig_whenUseCaseHasExisted(): Unit = runBlocking {
372         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
373 
374         withContext(Dispatchers.Main) {
375             val fakeLifecycleOwner = FakeLifecycleOwner()
376 
377             // This test works only if the camera is the same no matter running normal or
378             // extension modes.
379             val normalCamera =
380                 cameraProvider.bindToLifecycle(fakeLifecycleOwner, baseCameraSelector)
381             val extensionCamera =
382                 cameraProvider.bindToLifecycle(fakeLifecycleOwner, extensionCameraSelector)
383             assumeTrue(extensionCamera == normalCamera)
384 
385             // Binds a use case with the basic camera selector first.
386             cameraProvider.bindToLifecycle(fakeLifecycleOwner, baseCameraSelector, FakeUseCase())
387 
388             // IllegalStateException should be thrown when bindToLifecycle is called with
389             // different extension camera config
390             assertThrows<IllegalStateException> {
391                 cameraProvider.bindToLifecycle(fakeLifecycleOwner, extensionCameraSelector)
392             }
393         }
394     }
395 
396     @Test
397     fun canSetSameExtensionsConfig_whenUseCaseHasExisted(): Unit = runBlocking {
398         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
399 
400         withContext(Dispatchers.Main) {
401             val fakeLifecycleOwner = FakeLifecycleOwner()
402 
403             // Binds a use case with extension camera config first.
404             cameraProvider.bindToLifecycle(
405                 fakeLifecycleOwner,
406                 extensionCameraSelector,
407                 FakeUseCase()
408             )
409 
410             // Binds another use case with the same extension camera config.
411             cameraProvider.bindToLifecycle(
412                 fakeLifecycleOwner,
413                 extensionCameraSelector,
414                 FakeUseCase()
415             )
416         }
417     }
418 
419     @Test
420     fun canSwitchExtendedCameraConfig_afterUnbindUseCases(): Unit = runBlocking {
421         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
422 
423         withContext(Dispatchers.Main) {
424             val fakeLifecycleOwner = FakeLifecycleOwner()
425 
426             // Binds a use case with extension camera config first.
427             cameraProvider.bindToLifecycle(
428                 fakeLifecycleOwner,
429                 extensionCameraSelector,
430                 FakeUseCase()
431             )
432 
433             // Unbinds all use cases
434             cameraProvider.unbindAll()
435 
436             // Binds another use case with the basic camera selector.
437             cameraProvider.bindToLifecycle(fakeLifecycleOwner, baseCameraSelector, FakeUseCase())
438         }
439     }
440 
441     @Test
442     fun isImageAnalysisSupportedReturnsFalse_whenHasNoAnalysisSizes() {
443         extensionsManager =
444             ExtensionsManager.getInstanceAsync(
445                     context,
446                     cameraProvider,
447                 )[10000, TimeUnit.MILLISECONDS]
448 
449         val fakeVendorExtender =
450             object : VendorExtender {
451                 override fun isExtensionAvailable(
452                     cameraId: String,
453                     characteristicsMap: MutableMap<String, CameraCharacteristics>
454                 ): Boolean {
455                     return true
456                 }
457 
458                 override fun getSupportedYuvAnalysisResolutions(): Array<Size> {
459                     return emptyArray()
460                 }
461             }
462         extensionsManager.setVendorExtenderFactory { _, _ -> fakeVendorExtender }
463 
464         assumeTrue(
465             extensionsManager.extensionsAvailability ==
466                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
467         )
468 
469         assertThat(extensionsManager.isImageAnalysisSupported(baseCameraSelector, extensionMode))
470             .isFalse()
471     }
472 
473     @Test
474     fun isImageAnalysisSupportedReturnsTrue_whenHasAnalysisSizes() {
475         extensionsManager =
476             ExtensionsManager.getInstanceAsync(
477                     context,
478                     cameraProvider,
479                 )[10000, TimeUnit.MILLISECONDS]
480 
481         val fakeVendorExtender =
482             object : VendorExtender {
483                 override fun isExtensionAvailable(
484                     cameraId: String,
485                     characteristicsMap: MutableMap<String, CameraCharacteristics>
486                 ): Boolean {
487                     return true
488                 }
489 
490                 override fun getSupportedYuvAnalysisResolutions(): Array<Size> {
491                     return arrayOf(Size(1920, 1080))
492                 }
493             }
494         extensionsManager.setVendorExtenderFactory { _, _ -> fakeVendorExtender }
495 
496         assumeTrue(
497             extensionsManager.extensionsAvailability ==
498                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
499         )
500 
501         assertThat(extensionsManager.isImageAnalysisSupported(baseCameraSelector, extensionMode))
502             .isTrue()
503     }
504 
505     @Test
506     fun isImageAnalysisSupportedIsFalse_whenExtensionAvailabilityIsNotAvailable() {
507         extensionsManager =
508             ExtensionsManager.getInstanceAsync(context, cameraProvider, ClientVersion("99.0.0"))[
509                     10000, TimeUnit.MILLISECONDS]
510 
511         assumeTrue(
512             extensionsManager.extensionsAvailability !=
513                 ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
514         )
515 
516         assertThat(extensionsManager.isImageAnalysisSupported(baseCameraSelector, extensionMode))
517             .isFalse()
518     }
519 
520     @Test
521     fun isImageAnalysisSupportedIsFalse_whenNoCameraCanBeFound() {
522         checkExtensionAvailabilityAndInit()
523         val emptyCameraSelector =
524             CameraSelector.Builder().addCameraFilter { _ -> ArrayList<CameraInfo>() }.build()
525 
526         assertThat(extensionsManager.isImageAnalysisSupported(emptyCameraSelector, extensionMode))
527             .isFalse()
528     }
529 
530     @Test
531     fun postviewSupportedIsSetCorrectlyOnCameraConfig() = runBlocking {
532         // 1. Arrange
533         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
534         val fakeVendorExtender =
535             object : VendorExtender {
536                 override fun isExtensionAvailable(
537                     cameraId: String,
538                     characteristicsMap: MutableMap<String, CameraCharacteristics>
539                 ): Boolean {
540                     return true
541                 }
542 
543                 override fun isPostviewAvailable(): Boolean {
544                     return true
545                 }
546             }
547         extensionsManager.setVendorExtenderFactory { _, _ -> fakeVendorExtender }
548 
549         // 2. Act
550         val camera =
551             withContext(Dispatchers.Main) {
552                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
553             }
554 
555         // 3. Assert
556         assertThat(camera.extendedConfig.isPostviewSupported).isTrue()
557     }
558 
559     @Test
560     fun captureProcessProgressSupportedIsSetCorrectlyOnCameraConfig() = runBlocking {
561         // 1. Arrange
562         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
563         val fakeVendorExtender =
564             object : VendorExtender {
565                 override fun isExtensionAvailable(
566                     cameraId: String,
567                     characteristicsMap: MutableMap<String, CameraCharacteristics>
568                 ): Boolean {
569                     return true
570                 }
571 
572                 override fun isCaptureProcessProgressAvailable(): Boolean {
573                     return true
574                 }
575             }
576         extensionsManager.setVendorExtenderFactory { _, _ -> fakeVendorExtender }
577 
578         // 2. Act
579         val camera =
580             withContext(Dispatchers.Main) {
581                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
582             }
583 
584         // 3. Assert
585         assertThat(camera.extendedConfig.isCaptureProcessProgressSupported).isTrue()
586     }
587 
588     @Test
589     fun returnsCorrectInitialTypeFromSessionProcessor() = runBlocking {
590         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
591 
592         val camera =
593             withContext(Dispatchers.Main) {
594                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
595             }
596 
597         val sessionProcessor = camera.extendedConfig.sessionProcessor
598         val cameraExtensionsInfo = sessionProcessor as CameraExtensionsInfo
599         val currentType = cameraExtensionsInfo.currentExtensionMode
600         if (cameraExtensionsInfo.isCurrentExtensionModeAvailable) {
601             assertThat(currentType!!.value).isEqualTo(extensionMode)
602         } else {
603             assertThat(currentType).isNull()
604         }
605     }
606 
607     @Test
608     fun returnsCorrectExtensionTypeFromCameraExtensionsInfo() = runBlocking {
609         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
610 
611         val camera =
612             withContext(Dispatchers.Main) {
613                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
614             }
615 
616         val cameraExtensionsInfo = extensionsManager.getCameraExtensionsInfo(camera.cameraInfo)
617 
618         if (cameraExtensionsInfo.isCurrentExtensionModeAvailable) {
619             assertThat(cameraExtensionsInfo.currentExtensionMode!!.value).isEqualTo(extensionMode)
620         } else {
621             assertThat(cameraExtensionsInfo.currentExtensionMode).isNull()
622         }
623     }
624 
625     @Test
626     fun returnsCorrectExtensionStrengthAvailabilityFromCameraExtensionsInfo() = runBlocking {
627         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
628 
629         val camera =
630             withContext(Dispatchers.Main) {
631                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
632             }
633 
634         val cameraExtensionsInfo = extensionsManager.getCameraExtensionsInfo(camera.cameraInfo)
635 
636         assertThat(cameraExtensionsInfo.isExtensionStrengthAvailable)
637             .isEqualTo(
638                 camera.extendedConfig.sessionProcessor.supportedCameraOperations.contains(
639                     AdapterCameraInfo.CAMERA_OPERATION_EXTENSION_STRENGTH
640                 )
641             )
642     }
643 
644     @Test
645     fun returnsCorrectCurrentExtensionTypeAvailabilityFromCameraExtensionsInfo() = runBlocking {
646         assumeTrue(ExtensionVersion.isAdvancedExtenderSupported())
647         assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
648         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
649 
650         // Inject fake VendorExtenderFactory to provide custom VendorExtender
651         extensionsManager.setVendorExtenderFactory { _, _ ->
652             object : VendorExtender {
653                 override fun isExtensionAvailable(
654                     cameraId: String,
655                     characteristicsMap: MutableMap<String, CameraCharacteristics>
656                 ): Boolean {
657                     return true
658                 }
659 
660                 override fun isCurrentExtensionModeAvailable(): Boolean {
661                     return true
662                 }
663 
664                 override fun createSessionProcessor(context: Context): SessionProcessor? {
665                     return AdvancedSessionProcessor(
666                         FakeSessionProcessorImpl(),
667                         Collections.emptyList(),
668                         this,
669                         context,
670                         extensionMode
671                     )
672                 }
673             }
674         }
675 
676         val camera =
677             withContext(Dispatchers.Main) {
678                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
679             }
680         val cameraExtensionsInfo = extensionsManager.getCameraExtensionsInfo(camera.cameraInfo)
681         assertThat(cameraExtensionsInfo.isCurrentExtensionModeAvailable).isTrue()
682     }
683 
684     @Test
685     fun returnsCorrectInitialExtensionStrengthFromCameraExtensionsInfo() = runBlocking {
686         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
687 
688         val camera =
689             withContext(Dispatchers.Main) {
690                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
691             }
692 
693         val cameraExtensionsInfo = extensionsManager.getCameraExtensionsInfo(camera.cameraInfo)
694         if (cameraExtensionsInfo.isExtensionStrengthAvailable) {
695             assertThat(cameraExtensionsInfo.extensionStrength!!.value).isEqualTo(100)
696         } else {
697             assertThat(cameraExtensionsInfo.extensionStrength).isNull()
698         }
699     }
700 
701     private fun checkExtensionAvailabilityAndInit(): CameraSelector {
702         extensionsManager =
703             ExtensionsManager.getInstanceAsync(context, cameraProvider)[
704                     10000, TimeUnit.MILLISECONDS]
705 
706         assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
707 
708         return extensionsManager.getExtensionEnabledCameraSelector(
709             baseCameraSelector,
710             extensionMode
711         )
712     }
713 
714     @Test
715     fun returnsCorrectExtensionStrengthFromCameraExtensionsInfoForNormalMode() = runBlocking {
716         // Runs the test only when the parameterized extension mode is BOKEH to avoid wasting time
717         assumeTrue(extensionMode == ExtensionMode.BOKEH)
718         extensionsManager =
719             ExtensionsManager.getInstanceAsync(context, cameraProvider)[
720                     10000, TimeUnit.MILLISECONDS]
721 
722         val camera =
723             withContext(Dispatchers.Main) {
724                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), baseCameraSelector)
725             }
726 
727         val cameraExtensionsInfo = extensionsManager.getCameraExtensionsInfo(camera.cameraInfo)
728         assertThat(cameraExtensionsInfo.isExtensionStrengthAvailable).isFalse()
729         assertThat(cameraExtensionsInfo.extensionStrength).isNull()
730     }
731 
732     @Test
733     fun retrievesCameraExtensionsControlFromCameraControl(): Unit = runBlocking {
734         val extensionCameraSelector = checkExtensionAvailabilityAndInit()
735 
736         // Retrieves null CameraExtensionsControl from normal mode camera's CameraControl
737         withContext(Dispatchers.Main) {
738                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), baseCameraSelector)
739             }
740             .also {
741                 assertThat(extensionsManager.getCameraExtensionsControl(it.cameraControl)).isNull()
742             }
743 
744         // Retrieves non-null CameraExtensionsControl from extensions-enabled camera's CameraControl
745         withContext(Dispatchers.Main) {
746                 cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector)
747             }
748             .also {
749                 assertThat(extensionsManager.getCameraExtensionsControl(it.cameraControl))
750                     .isNotNull()
751             }
752     }
753 
754     @SdkSuppress(minSdkVersion = 31)
755     @Test
756     fun canProvideCorrectTypeOfSessionProcessor(): Unit = runBlocking {
757         var extensionCameraSelector = checkExtensionAvailabilityAndInit()
758 
759         // Get and check the session processor type is correct
760         (cameraProvider.getCameraInfo(extensionCameraSelector) as AdapterCameraInfo)
761             .sessionProcessor
762             ?.also {
763                 if (shouldUseCamera2Extensions(cameraProvider.configImplType)) {
764                     assertThat(it).isInstanceOf(Camera2ExtensionsSessionProcessor::class.java)
765                 } else if (ExtensionVersion.isAdvancedExtenderSupported()) {
766                     assertThat(it).isInstanceOf(AdvancedSessionProcessor::class.java)
767                 } else {
768                     assertThat(it).isInstanceOf(BasicExtenderSessionProcessor::class.java)
769                 }
770             }
771     }
772 
773     private fun isExtensionAvailableByCameraInfo(cameraInfo: CameraInfo): Boolean {
774         var vendorExtender =
775             ExtensionsTestUtil.createVendorExtender(
776                 context,
777                 extensionMode,
778                 cameraProvider.configImplType
779             )
780         val cameraId = (cameraInfo as CameraInfoInternal).cameraId
781 
782         return vendorExtender.isExtensionAvailable(
783             cameraId,
784             ExtensionsUtils.getCameraCharacteristicsMap(cameraInfo)
785         )
786     }
787 
788     private class FakeSessionProcessorImpl : SessionProcessorImpl {
789         override fun initSession(
790             cameraId: String,
791             cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
792             context: Context,
793             surfaceConfigs: OutputSurfaceConfigurationImpl
794         ): Camera2SessionConfigImpl = FakeCamera2SessionConfigImpl()
795 
796         override fun initSession(
797             cameraId: String,
798             cameraCharacteristicsMap: MutableMap<String, CameraCharacteristics>,
799             context: Context,
800             previewSurfaceConfig: OutputSurfaceImpl,
801             imageCaptureSurfaceConfig: OutputSurfaceImpl,
802             imageAnalysisSurfaceConfig: OutputSurfaceImpl?
803         ): Camera2SessionConfigImpl = FakeCamera2SessionConfigImpl()
804 
805         override fun deInitSession() {}
806 
807         override fun setParameters(parameters: MutableMap<CaptureRequest.Key<*>, Any>) {}
808 
809         override fun startTrigger(
810             triggers: MutableMap<CaptureRequest.Key<*>, Any>,
811             callback: SessionProcessorImpl.CaptureCallback
812         ): Int = 0
813 
814         override fun onCaptureSessionStart(requestProcessor: RequestProcessorImpl) {}
815 
816         override fun onCaptureSessionEnd() {}
817 
818         override fun startRepeating(callback: SessionProcessorImpl.CaptureCallback): Int = 0
819 
820         override fun stopRepeating() {}
821 
822         override fun startCapture(callback: SessionProcessorImpl.CaptureCallback): Int = 0
823 
824         override fun startCaptureWithPostview(callback: SessionProcessorImpl.CaptureCallback): Int =
825             0
826 
827         override fun abortCapture(captureSequenceId: Int) {}
828 
829         override fun getRealtimeCaptureLatency(): Pair<Long, Long>? = null
830     }
831 
832     private class FakeCamera2SessionConfigImpl : Camera2SessionConfigImpl {
833         override fun getOutputConfigs(): MutableList<Camera2OutputConfigImpl> = mutableListOf()
834 
835         override fun getSessionParameters(): MutableMap<CaptureRequest.Key<*>, Any> = mutableMapOf()
836 
837         override fun getSessionTemplateId(): Int = 0
838 
839         override fun getSessionType(): Int = 0
840     }
841 }
842