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