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