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