1 /*
2  * Copyright (C) 2014 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 android.hardware.camera2.cts.testcases;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.*;
20 
21 import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
22 import androidx.test.InstrumentationRegistry;
23 import android.app.UiAutomation;
24 
25 import android.content.Context;
26 import android.graphics.ImageFormat;
27 import android.hardware.camera2.CameraAccessException;
28 import android.hardware.camera2.CameraCaptureSession;
29 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
30 import android.hardware.camera2.CameraCharacteristics;
31 import android.hardware.camera2.CameraDevice;
32 import android.hardware.camera2.CameraManager;
33 import android.hardware.camera2.CameraMetadata;
34 import android.hardware.camera2.CaptureRequest;
35 import android.hardware.camera2.CaptureResult;
36 import android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity;
37 import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
38 import android.hardware.camera2.cts.CameraTestUtils;
39 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
40 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
41 import android.hardware.camera2.cts.helpers.StaticMetadata;
42 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
43 import android.media.ImageReader;
44 import android.os.Handler;
45 import android.os.HandlerThread;
46 import android.os.Looper;
47 import android.util.Log;
48 import android.util.Range;
49 import android.util.Size;
50 import android.view.Surface;
51 import android.view.SurfaceHolder;
52 import android.view.View;
53 import android.view.WindowManager;
54 
55 import androidx.test.rule.ActivityTestRule;
56 
57 import com.android.ex.camera2.blocking.BlockingSessionCallback;
58 import com.android.ex.camera2.blocking.BlockingStateCallback;
59 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
60 
61 import org.junit.After;
62 import org.junit.Before;
63 import org.junit.Ignore;
64 import org.junit.Rule;
65 
66 import java.io.File;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.HashMap;
70 import java.util.List;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.junit.runners.Parameterized;
74 import org.junit.runners.Parameterized.Parameter;
75 import org.junit.runners.Parameterized.Parameters;
76 
77 
78 /**
79  * Camera2 Preview test case base class by using SurfaceView as rendering target.
80  *
81  * <p>This class encapsulates the SurfaceView based preview common functionalities.
82  * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs
83  * and CameraStateCallback are handled in this class. Some basic preview related utility
84  * functions are provided to facilitate the derived preview-based test classes.
85  * </p>
86  */
87 
88 public class Camera2SurfaceViewTestCase extends Camera2ParameterizedTestCase {
89     private static final String TAG = "SurfaceViewTestCase";
90     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
91     private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
92 
93     protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
94     protected static final float FRAME_DURATION_ERROR_MARGIN = 0.01f; // 1 percent error margin.
95     protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
96     protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
97     protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns
98 
99     protected HandlerThread mHandlerThread;
100     protected Handler mHandler;
101     protected BlockingStateCallback mCameraListener;
102     protected BlockingSessionCallback mSessionListener;
103     protected CameraErrorCollector mCollector;
104     protected HashMap<String, StaticMetadata> mAllStaticInfo;
105     // Per device fields:
106     protected StaticMetadata mStaticInfo;
107     protected CameraDevice mCamera;
108     protected CameraCaptureSession mSession;
109     protected ImageReader mReader;
110     protected Surface mReaderSurface;
111     protected Surface mPreviewSurface;
112     protected SurfaceHolder mPreviewHolder;
113     protected Size mPreviewSize;
114     protected List<Size> mOrderedPreviewSizes; // In descending order.
115     protected List<Size> m1080pBoundedOrderedPreviewSizes; // In descending order.
116     protected List<Size> mOrderedVideoSizes; // In descending order.
117     protected List<Size> mOrderedStillSizes; // In descending order.
118     protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
119     protected String mDebugFileNameBase;
120 
121     protected WindowManager mWindowManager;
122 
123     @Rule
124     public ActivityTestRule<Camera2SurfaceViewCtsActivity> mActivityRule =
125             new ActivityTestRule<>(Camera2SurfaceViewCtsActivity.class);
126 
127     @Before
setUp()128     public void setUp() throws Exception {
129         super.setUp();
130         mHandlerThread = new HandlerThread(TAG);
131         mHandlerThread.start();
132         mHandler = new Handler(mHandlerThread.getLooper());
133         mCameraListener = new BlockingStateCallback();
134         mCollector = new CameraErrorCollector();
135 
136         File filesDir = mContext.getPackageManager().isInstantApp()
137                 ? mContext.getFilesDir()
138                 : mContext.getExternalFilesDir(null);
139 
140         mDebugFileNameBase = filesDir.getPath();
141 
142         mAllStaticInfo = new HashMap<String, StaticMetadata>();
143         List<String> hiddenPhysicalIds = new ArrayList<>();
144         for (String cameraId : mCameraIdsUnderTest) {
145             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
146             StaticMetadata staticMetadata = new StaticMetadata(props,
147                     CheckLevel.ASSERT, /*collector*/null);
148             mAllStaticInfo.put(cameraId, staticMetadata);
149 
150             for (String physicalId : props.getPhysicalCameraIds()) {
151                 if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
152                         !hiddenPhysicalIds.contains(physicalId)) {
153                     hiddenPhysicalIds.add(physicalId);
154                     props = mCameraManager.getCameraCharacteristics(physicalId);
155                     staticMetadata = new StaticMetadata(
156                             mCameraManager.getCameraCharacteristics(physicalId),
157                             CheckLevel.ASSERT, /*collector*/null);
158                     mAllStaticInfo.put(physicalId, staticMetadata);
159                 }
160             }
161         }
162 
163         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
164     }
165 
166     @After
tearDown()167     public void tearDown() throws Exception {
168         mHandlerThread.quitSafely();
169         mHandler = null;
170         mCameraListener = null;
171 
172         try {
173             mCollector.verify();
174         } catch (Throwable e) {
175             // When new Exception(e) is used, exception info will be printed twice.
176             throw new Exception(e.getMessage());
177         }
178         super.tearDown();
179     }
180 
181     /**
182      * Start camera preview by using the given request, preview size and capture
183      * listener.
184      * <p>
185      * If preview is already started, calling this function will stop the
186      * current preview stream and start a new preview stream with given
187      * parameters. No need to call stopPreview between two startPreview calls.
188      * </p>
189      *
190      * @param request The request builder used to start the preview.
191      * @param previewSz The size of the camera device output preview stream.
192      * @param listener The callbacks the camera device will notify when preview
193      *            capture is available.
194      */
startPreview(CaptureRequest.Builder request, Size previewSz, CaptureCallback listener)195     protected void startPreview(CaptureRequest.Builder request, Size previewSz,
196             CaptureCallback listener) throws Exception {
197         // Update preview size.
198         updatePreviewSurface(previewSz);
199         if (VERBOSE) {
200             Log.v(TAG, "start preview with size " + mPreviewSize.toString());
201         }
202 
203         configurePreviewOutput(request);
204 
205         mSession.setRepeatingRequest(request.build(), listener, mHandler);
206     }
207 
208     /**
209      * Configure the preview output stream.
210      *
211      * @param request The request to be configured with preview surface
212      */
configurePreviewOutput(CaptureRequest.Builder request)213     protected void configurePreviewOutput(CaptureRequest.Builder request)
214             throws CameraAccessException {
215         List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
216         outputSurfaces.add(mPreviewSurface);
217         mSessionListener = new BlockingSessionCallback();
218         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
219 
220         request.addTarget(mPreviewSurface);
221     }
222 
223     /**
224      * Create a {@link CaptureRequest#Builder} and add the default preview surface.
225      *
226      * @return The {@link CaptureRequest#Builder} to be created
227      * @throws CameraAccessException When create capture request from camera fails
228      */
createRequestForPreview()229     protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException {
230         if (mPreviewSurface == null) {
231             throw new IllegalStateException(
232                     "Preview surface is not set yet, call updatePreviewSurface or startPreview"
233                     + "first to set the preview surface properly.");
234         }
235         CaptureRequest.Builder requestBuilder =
236                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
237         requestBuilder.addTarget(mPreviewSurface);
238         return requestBuilder;
239     }
240 
241     /**
242      * Stop preview for current camera device by closing the session.
243      * Does _not_ wait for the device to go idle
244      */
stopPreview()245     protected void stopPreview() throws Exception {
246         // Stop repeat, wait for captures to complete, and disconnect from surfaces
247         if (mSession != null) {
248             if (VERBOSE) Log.v(TAG, "Stopping preview");
249             mSession.close();
250         }
251     }
252 
253     /**
254      * Stop preview for current camera device by closing the session and waiting for it to close,
255      * resulting in an idle device.
256      */
stopPreviewAndDrain()257     protected void stopPreviewAndDrain() throws Exception {
258         // Stop repeat, wait for captures to complete, and disconnect from surfaces
259         if (mSession != null) {
260             if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
261             mSession.close();
262             mSessionListener.getStateWaiter().waitForState(BlockingSessionCallback.SESSION_CLOSED,
263                     /*timeoutMs*/WAIT_FOR_RESULT_TIMEOUT_MS);
264         }
265     }
266 
267     /**
268      * Setup still (JPEG) capture configuration and start preview.
269      * <p>
270      * The default max number of image is set to image reader.
271      * </p>
272      *
273      * @param previewRequest The capture request to be used for preview
274      * @param stillRequest The capture request to be used for still capture
275      * @param previewSz Preview size
276      * @param stillSz The still capture size
277      * @param resultListener Capture result listener
278      * @param imageListener The still capture image listener
279      */
prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener, boolean isHeic)280     protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
281             CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
282             CaptureCallback resultListener,
283             ImageReader.OnImageAvailableListener imageListener, boolean isHeic) throws Exception {
284         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
285                 isHeic ? ImageFormat.HEIC : ImageFormat.JPEG, resultListener, MAX_READER_IMAGES,
286                 imageListener);
287     }
288 
289     /**
290      * Setup still (JPEG) capture configuration and start preview.
291      *
292      * @param previewRequest The capture request to be used for preview
293      * @param stillRequest The capture request to be used for still capture
294      * @param previewSz Preview size
295      * @param stillSz The still capture size
296      * @param resultListener Capture result listener
297      * @param maxNumImages The max number of images set to the image reader
298      * @param imageListener The still capture image listener
299      */
prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener, boolean isHeic)300     protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
301             CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
302             CaptureCallback resultListener, int maxNumImages,
303             ImageReader.OnImageAvailableListener imageListener, boolean isHeic) throws Exception {
304         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
305                 isHeic ? ImageFormat.HEIC : ImageFormat.JPEG, resultListener, maxNumImages, imageListener);
306     }
307 
308     /**
309      * Setup raw capture configuration and start preview.
310      *
311      * <p>
312      * The default max number of image is set to image reader.
313      * </p>
314      *
315      * @param previewRequest The capture request to be used for preview
316      * @param rawRequest The capture request to be used for raw capture
317      * @param previewSz Preview size
318      * @param rawSz The raw capture size
319      * @param resultListener Capture result listener
320      * @param imageListener The raw capture image listener
321      */
prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)322     protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
323             CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz,
324             CaptureCallback resultListener,
325             ImageReader.OnImageAvailableListener imageListener) throws Exception {
326         prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz,
327                 ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener);
328     }
329 
330     /**
331      * Wait for expected result key value available in a certain number of results.
332      *
333      * <p>
334      * Check the result immediately if numFramesWait is 0.
335      * </p>
336      *
337      * @param listener The capture listener to get capture result
338      * @param resultKey The capture result key associated with the result value
339      * @param expectedValue The result value need to be waited for
340      * @param numResultsWait Number of frame to wait before times out
341      * @throws TimeoutRuntimeException If more than numResultsWait results are
342      * seen before the result matching myRequest arrives, or each individual wait
343      * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
344      */
waitForResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, T expectedValue, int numResultsWait)345     protected static <T> void waitForResultValue(SimpleCaptureCallback listener,
346             CaptureResult.Key<T> resultKey,
347             T expectedValue, int numResultsWait) {
348         CameraTestUtils.waitForResultValue(listener, resultKey, expectedValue,
349                 numResultsWait, WAIT_FOR_RESULT_TIMEOUT_MS);
350     }
351 
352     /**
353      * Wait for any expected result key values available in a certain number of results.
354      *
355      * <p>
356      * Check the result immediately if numFramesWait is 0.
357      * </p>
358      *
359      * @param listener The capture listener to get capture result.
360      * @param resultKey The capture result key associated with the result value.
361      * @param expectedValues The list of result value need to be waited for,
362      * return immediately if the list is empty.
363      * @param numResultsWait Number of frame to wait before times out.
364      * @throws TimeoutRuntimeException If more than numResultsWait results are.
365      * seen before the result matching myRequest arrives, or each individual wait
366      * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
367      */
waitForAnyResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, List<T> expectedValues, int numResultsWait)368     protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener,
369             CaptureResult.Key<T> resultKey,
370             List<T> expectedValues, int numResultsWait) {
371         CameraTestUtils.waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait,
372                 WAIT_FOR_RESULT_TIMEOUT_MS);
373     }
374 
375     /**
376      * Submit a burst of the same capture request, then submit additional captures in order to
377      * ensure that the camera will be synchronized.
378      *
379      * <p>
380      * The additional capture count is determined by android.sync.maxLatency (or
381      * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
382      * </p>
383      *
384      * <p>Returns the number of captures that were submitted (at least 1), which is useful
385      * with {@link #waitForNumResults}.</p>
386      *
387      * @param request capture request to forward to {@link CameraDevice#capture}
388      * @param listener request listener to forward to {@link CameraDevice#capture}
389      * @param handler handler to forward to {@link CameraDevice#capture}
390      *
391      * @return the number of captures that were submitted
392      *
393      * @throws CameraAccessException if capturing failed
394      */
captureRequestsSynchronizedBurst( CaptureRequest request, int count, CaptureCallback listener, Handler handler)395     protected int captureRequestsSynchronizedBurst(
396             CaptureRequest request, int count, CaptureCallback listener, Handler handler)
397                     throws CameraAccessException {
398         return captureRequestsSynchronizedImpl(request, count, listener, handler, true);
399     }
400     /**
401      * Submit a capture once, then submit additional captures in order to ensure that
402      * the camera will be synchronized.
403      *
404      * <p>
405      * The additional capture count is determined by android.sync.maxLatency (or
406      * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
407      * </p>
408      *
409      * <p>Returns the number of captures that were submitted (at least 1), which is useful
410      * with {@link #waitForNumResults}.</p>
411      *
412      * @param request capture request to forward to {@link CameraDevice#capture}
413      * @param listener request listener to forward to {@link CameraDevice#capture}
414      * @param handler handler to forward to {@link CameraDevice#capture}
415      *
416      * @return the number of captures that were submitted
417      *
418      * @throws CameraAccessException if capturing failed
419      */
captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)420     protected int captureRequestsSynchronized(
421             CaptureRequest request, CaptureCallback listener, Handler handler)
422                     throws CameraAccessException {
423         return captureRequestsSynchronizedImpl(request, /*count*/1, listener, handler, false);
424     }
425 
426     /**
427      * Submit a capture {@code count} times, then submit additional captures in order to ensure that
428      * the camera will be synchronized.
429      *
430      * <p>
431      * The additional capture count is determined by android.sync.maxLatency (or
432      * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
433      * </p>
434      *
435      * <p>Returns the number of captures that were submitted (at least 1), which is useful
436      * with {@link #waitForNumResults}.</p>
437      *
438      * @param request capture request to forward to {@link CameraDevice#capture}
439      * @param count the number of times to submit the request (minimally), must be at least 1
440      * @param listener request listener to forward to {@link CameraDevice#capture}
441      * @param handler handler to forward to {@link CameraDevice#capture}
442      *
443      * @return the number of captures that were submitted
444      *
445      * @throws IllegalArgumentException if {@code count} was not at least 1
446      * @throws CameraAccessException if capturing failed
447      */
captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)448     protected int captureRequestsSynchronized(
449             CaptureRequest request, int count, CaptureCallback listener, Handler handler)
450                     throws CameraAccessException {
451         return captureRequestsSynchronizedImpl(request, count, listener, handler, false);
452     }
453 
454     /**
455      * Wait for numResultWait frames
456      *
457      * @param resultListener The capture listener to get capture result back.
458      * @param numResultsWait Number of frame to wait
459      *
460      * @return the last result, or {@code null} if there was none
461      */
waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)462     protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener,
463             int numResultsWait) {
464         return CameraTestUtils.waitForNumResults(resultListener, numResultsWait,
465                 WAIT_FOR_RESULT_TIMEOUT_MS);
466     }
467 
468     /**
469      * Wait for enough results for settings to be applied
470      *
471      * @param resultListener The capture listener to get capture result back.
472      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
473      *                                       unknown.
474      */
waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)475     protected void waitForSettingsApplied(SimpleCaptureCallback resultListener,
476             int numResultWaitForUnknownLatency) {
477         int maxLatency = mStaticInfo.getSyncMaxLatency();
478         if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
479             maxLatency = numResultWaitForUnknownLatency;
480         }
481         // Wait for settings to take effect
482         waitForNumResults(resultListener, maxLatency);
483     }
484 
485     /**
486      * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
487      *
488      * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
489      * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
490      * is unknown.</p>
491      *
492      * <p>This is a no-op for {@code LEGACY} devices since they don't report
493      * the {@code aeState} result.</p>
494      *
495      * @param resultListener The capture listener to get capture result back.
496      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
497      *                                       unknown.
498      */
waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)499     protected void waitForAeStable(SimpleCaptureCallback resultListener,
500             int numResultWaitForUnknownLatency) {
501         CameraTestUtils.waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY,
502                 mStaticInfo, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT);
503     }
504 
505     /**
506      * Wait for AE to be: LOCKED
507      *
508      * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
509      * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
510      * is unknown.</p>
511      *
512      * <p>This is a no-op for {@code LEGACY} devices since they don't report
513      * the {@code aeState} result.</p>
514      *
515      * @param resultListener The capture listener to get capture result back.
516      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
517      *                                       unknown.
518      */
waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)519     protected void waitForAeLocked(SimpleCaptureCallback resultListener,
520             int numResultWaitForUnknownLatency) {
521 
522         waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
523 
524         if (!mStaticInfo.isHardwareLevelAtLeastLimited()) {
525             // No-op for legacy devices
526             return;
527         }
528 
529         List<Integer> expectedAeStates = new ArrayList<Integer>();
530         expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
531         CameraTestUtils.waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
532                 expectedAeStates, NUM_RESULTS_WAIT_TIMEOUT, WAIT_FOR_RESULT_TIMEOUT_MS);
533     }
534 
535     /**
536      * Create an {@link ImageReader} object and get the surface.
537      *
538      * @param size The size of this ImageReader to be created.
539      * @param format The format of this ImageReader to be created
540      * @param maxNumImages The max number of images that can be acquired simultaneously.
541      * @param listener The listener used by this ImageReader to notify callbacks.
542      */
createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)543     protected void createImageReader(Size size, int format, int maxNumImages,
544             ImageReader.OnImageAvailableListener listener) throws Exception {
545         closeImageReader();
546 
547         ImageReader r = makeImageReader(size, format, maxNumImages, listener,
548                 mHandler);
549         mReader = r;
550         mReaderSurface = r.getSurface();
551     }
552 
553     /**
554      * Close the pending images then close current active {@link ImageReader} object.
555      */
closeImageReader()556     protected void closeImageReader() {
557         CameraTestUtils.closeImageReader(mReader);
558         mReader = null;
559         mReaderSurface = null;
560     }
561 
562     /**
563      * Close the pending images then close current active {@link ImageReader} objects.
564      */
closeImageReaders(ImageReader[] readers)565     protected void closeImageReaders(ImageReader[] readers) {
566         CameraTestUtils.closeImageReaders(readers);
567     }
568 
569     /**
570      * Setup still capture configuration and start preview.
571      *
572      * @param previewRequest The capture request to be used for preview
573      * @param stillRequest The capture request to be used for still capture
574      * @param previewSz Preview size
575      * @param captureSizes Still capture sizes
576      * @param formats The single capture image formats
577      * @param resultListener Capture result listener
578      * @param maxNumImages The max number of images set to the image reader
579      * @param imageListeners The single capture capture image listeners
580      * @param isHeic HEIC still capture if true, JPEG still capture if false
581      */
prepareStillCaptureAndStartPreview( CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners, boolean isHeic)582     protected ImageReader[] prepareStillCaptureAndStartPreview(
583             CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest,
584             Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener,
585             int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners,
586             boolean isHeic)
587             throws Exception {
588 
589         if ((captureSizes == null) || (formats == null) || (imageListeners == null) &&
590                 (captureSizes.length != formats.length) ||
591                 (formats.length != imageListeners.length)) {
592             throw new IllegalArgumentException("Invalid capture sizes/formats or image listeners!");
593         }
594 
595         if (VERBOSE) {
596             Log.v(TAG, String.format("Prepare still capture and preview (%s)",
597                     previewSz.toString()));
598         }
599 
600         // Update preview size.
601         updatePreviewSurface(previewSz);
602 
603         ImageReader[] readers = new ImageReader[captureSizes.length];
604         List<Surface> outputSurfaces = new ArrayList<Surface>();
605         outputSurfaces.add(mPreviewSurface);
606         for (int i = 0; i < captureSizes.length; i++) {
607             readers[i] = makeImageReader(captureSizes[i], formats[i], maxNumImages,
608                     imageListeners[i], mHandler);
609             outputSurfaces.add(readers[i].getSurface());
610         }
611 
612         mSessionListener = new BlockingSessionCallback();
613         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
614 
615         // Configure the requests.
616         previewRequest.addTarget(mPreviewSurface);
617         stillRequest.addTarget(mPreviewSurface);
618         for (int i = 0; i < readers.length; i++) {
619             stillRequest.addTarget(readers[i].getSurface());
620         }
621 
622         // Start preview.
623         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
624 
625         return readers;
626     }
627 
628     /**
629      * Open a camera device and get the StaticMetadata for a given camera id.
630      *
631      * @param cameraId The id of the camera device to be opened.
632      */
openDevice(String cameraId)633     protected void openDevice(String cameraId) throws Exception {
634         mCamera = CameraTestUtils.openCamera(
635                 mCameraManager, cameraId, mCameraListener, mHandler);
636         mCollector.setCameraId(cameraId);
637         mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
638                 CheckLevel.ASSERT, /*collector*/null);
639         if (mStaticInfo.isColorOutputSupported()) {
640             mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
641                     getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
642             m1080pBoundedOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
643                     PREVIEW_SIZE_BOUND);
644             mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
645             mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
646             // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
647             // in public API side.
648             mMinPreviewFrameDurationMap =
649                 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
650         }
651     }
652 
653     /**
654      * Close the current actively used camera device.
655      */
closeDevice()656     protected void closeDevice() {
657         if (mCamera != null) {
658             mCamera.close();
659             mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
660             mCamera = null;
661             mSession = null;
662             mSessionListener = null;
663             mStaticInfo = null;
664             mOrderedPreviewSizes = null;
665             mOrderedVideoSizes = null;
666             mOrderedStillSizes = null;
667         }
668     }
669 
670     /**
671      * Update the preview surface size.
672      *
673      * @param size The preview size to be updated.
674      */
updatePreviewSurface(Size size)675     protected void updatePreviewSurface(Size size) {
676         if (size.equals(mPreviewSize) && mPreviewSurface != null) {
677             Log.w(TAG, "Skipping update preview surface size...");
678             return;
679         }
680 
681         mPreviewSize = size;
682         Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
683         final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder();
684         Handler handler = new Handler(Looper.getMainLooper());
685         handler.post(new Runnable() {
686             @Override
687             public void run() {
688                 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
689             }
690         });
691 
692         boolean res = ctsActivity.waitForSurfaceSizeChanged(
693                 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(),
694                 mPreviewSize.getHeight());
695         assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
696         mPreviewHolder = holder;
697         mPreviewSurface = holder.getSurface();
698         assertNotNull("Preview surface is null", mPreviewSurface);
699         assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
700     }
701 
702     /**
703      * Recreate the SurfaceView's Surface
704      *
705      * Hide and unhide the activity's preview SurfaceView, so that its backing Surface is
706      * recreated
707      */
recreatePreviewSurface()708     protected void recreatePreviewSurface() {
709         Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
710         setPreviewVisibility(View.GONE);
711         boolean res = ctsActivity.waitForSurfaceState(
712             WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false);
713         assertTrue("wait for surface destroyed timed out", res);
714         setPreviewVisibility(View.VISIBLE);
715         res = ctsActivity.waitForSurfaceState(
716             WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ true);
717         assertTrue("wait for surface created timed out", res);
718     }
719 
720     /**
721      * Show/hide the preview SurfaceView.
722      *
723      * If set to View.GONE, the surfaceDestroyed callback will fire
724      * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE
725      */
setPreviewVisibility(int visibility)726     protected void setPreviewVisibility(int visibility) {
727         final Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
728         Handler handler = new Handler(Looper.getMainLooper());
729         handler.post(new Runnable() {
730             @Override
731             public void run() {
732                 ctsActivity.getSurfaceView().setVisibility(visibility);
733             }
734         });
735     }
736 
737     /**
738      * Setup single capture configuration and start preview.
739      *
740      * @param previewRequest The capture request to be used for preview
741      * @param stillRequest The capture request to be used for still capture
742      * @param previewSz Preview size
743      * @param captureSz Still capture size
744      * @param format The single capture image format
745      * @param resultListener Capture result listener
746      * @param maxNumImages The max number of images set to the image reader
747      * @param imageListener The single capture capture image listener
748      */
prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)749     protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
750             CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
751             CaptureCallback resultListener, int maxNumImages,
752             ImageReader.OnImageAvailableListener imageListener) throws Exception {
753         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, captureSz,
754             format, resultListener, null, maxNumImages, imageListener);
755     }
756 
757     /**
758      * Setup single capture configuration and start preview.
759      *
760      * @param previewRequest The capture request to be used for preview
761      * @param stillRequest The capture request to be used for still capture
762      * @param previewSz Preview size
763      * @param captureSz Still capture size
764      * @param format The single capture image format
765      * @param resultListener Capture result listener
766      * @param sessionListener Session listener
767      * @param maxNumImages The max number of images set to the image reader
768      * @param imageListener The single capture capture image listener
769      */
prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)770     protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
771             CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
772             CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener,
773             int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception {
774         if (VERBOSE) {
775             Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
776                     captureSz.toString(), previewSz.toString()));
777         }
778 
779         // Update preview size.
780         updatePreviewSurface(previewSz);
781 
782         // Create ImageReader.
783         createImageReader(captureSz, format, maxNumImages, imageListener);
784 
785         // Configure output streams with preview and jpeg streams.
786         List<Surface> outputSurfaces = new ArrayList<Surface>();
787         outputSurfaces.add(mPreviewSurface);
788         outputSurfaces.add(mReaderSurface);
789         if (sessionListener == null) {
790             mSessionListener = new BlockingSessionCallback();
791         } else {
792             mSessionListener = new BlockingSessionCallback(sessionListener);
793         }
794         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
795 
796         // Configure the requests.
797         previewRequest.addTarget(mPreviewSurface);
798         stillRequest.addTarget(mPreviewSurface);
799         stillRequest.addTarget(mReaderSurface);
800 
801         // Start preview.
802         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
803     }
804 
805     /**
806      * Get the max preview size that supports the given fpsRange.
807      *
808      * @param fpsRange The fps range the returned size must support.
809      * @return max size that support the given fps range.
810      */
getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)811     protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) {
812         if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) {
813             throw new IllegalArgumentException("Invalid fps range argument");
814         }
815         if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) {
816             throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap"
817                     + " must be initialized");
818         }
819 
820         long[] frameDurationRange =
821                 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
822         for (Size size : mOrderedPreviewSizes) {
823             Long minDuration = mMinPreviewFrameDurationMap.get(size);
824             if (minDuration == null ||
825                     minDuration == 0) {
826                 if (mStaticInfo.isCapabilitySupported(
827                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
828                     throw new IllegalArgumentException(
829                             "No min frame duration available for the size " + size);
830                 }
831                 continue;
832             }
833             if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) {
834                 return size;
835             }
836         }
837 
838         // Search again for sizes not bounded by display size
839         for (Size size : m1080pBoundedOrderedPreviewSizes) {
840             Long minDuration = mMinPreviewFrameDurationMap.get(size);
841             if (minDuration == null ||
842                     minDuration == 0) {
843                 if (mStaticInfo.isCapabilitySupported(
844                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
845                     throw new IllegalArgumentException(
846                             "No min frame duration available for the size " + size);
847                 }
848                 continue;
849             }
850             if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) {
851                 return size;
852             }
853         }
854         return null;
855     }
856 
isReprocessSupported(String cameraId, int format)857     protected boolean isReprocessSupported(String cameraId, int format)
858             throws CameraAccessException {
859         if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) {
860             throw new IllegalArgumentException(
861                     "format " + format + " is not supported for reprocessing");
862         }
863 
864         StaticMetadata info =
865                 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
866                                    CheckLevel.ASSERT, /*collector*/ null);
867         int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
868         if (format == ImageFormat.PRIVATE) {
869             cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
870         }
871         return info.isCapabilitySupported(cap);
872     }
873 
getSuitableFpsRangeForDuration(String cameraId, long frameDuration)874     protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) {
875         return CameraTestUtils.getSuitableFpsRangeForDuration(cameraId, frameDuration, mStaticInfo);
876     }
877 
captureRequestsSynchronizedImpl( CaptureRequest request, int count, CaptureCallback listener, Handler handler, boolean isBurst)878     private int captureRequestsSynchronizedImpl(
879             CaptureRequest request, int count, CaptureCallback listener, Handler handler,
880             boolean isBurst) throws CameraAccessException {
881         if (count < 1) {
882             throw new IllegalArgumentException("count must be positive");
883         }
884 
885         int maxLatency = mStaticInfo.getSyncMaxLatency();
886         if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
887             maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
888         }
889 
890         assertTrue("maxLatency is non-negative", maxLatency >= 0);
891 
892         int numCaptures = maxLatency + count;
893         ArrayList<CaptureRequest> burstCaptureRequests = new ArrayList<>();
894         for (int i = 0; i < numCaptures; ++i) {
895             if (isBurst) {
896                 burstCaptureRequests.add(request);
897             } else {
898                 mSession.capture(request, listener, handler);
899             }
900         }
901         if (isBurst) {
902             mSession.captureBurst(burstCaptureRequests, listener, handler);
903         }
904 
905         return numCaptures;
906     }
907 }
908