• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 capture once, then submit additional captures in order to ensure that
389      * 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      */
captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)407     protected int captureRequestsSynchronized(
408             CaptureRequest request, CaptureCallback listener, Handler handler)
409                     throws CameraAccessException {
410         return captureRequestsSynchronized(request, /*count*/1, listener, handler);
411     }
412 
413     /**
414      * Submit a capture {@code count} times, then submit additional captures in order to ensure that
415      * the camera will be synchronized.
416      *
417      * <p>
418      * The additional capture count is determined by android.sync.maxLatency (or
419      * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
420      * </p>
421      *
422      * <p>Returns the number of captures that were submitted (at least 1), which is useful
423      * with {@link #waitForNumResults}.</p>
424      *
425      * @param request capture request to forward to {@link CameraDevice#capture}
426      * @param count the number of times to submit the request (minimally), must be at least 1
427      * @param listener request listener to forward to {@link CameraDevice#capture}
428      * @param handler handler to forward to {@link CameraDevice#capture}
429      *
430      * @return the number of captures that were submitted
431      *
432      * @throws IllegalArgumentException if {@code count} was not at least 1
433      * @throws CameraAccessException if capturing failed
434      */
captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)435     protected int captureRequestsSynchronized(
436             CaptureRequest request, int count, CaptureCallback listener, Handler handler)
437                     throws CameraAccessException {
438         if (count < 1) {
439             throw new IllegalArgumentException("count must be positive");
440         }
441 
442         int maxLatency = mStaticInfo.getSyncMaxLatency();
443         if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
444             maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
445         }
446 
447         assertTrue("maxLatency is non-negative", maxLatency >= 0);
448 
449         int numCaptures = maxLatency + count;
450 
451         for (int i = 0; i < numCaptures; ++i) {
452             mSession.capture(request, listener, handler);
453         }
454 
455         return numCaptures;
456     }
457 
458     /**
459      * Wait for numResultWait frames
460      *
461      * @param resultListener The capture listener to get capture result back.
462      * @param numResultsWait Number of frame to wait
463      *
464      * @return the last result, or {@code null} if there was none
465      */
waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)466     protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener,
467             int numResultsWait) {
468         return CameraTestUtils.waitForNumResults(resultListener, numResultsWait,
469                 WAIT_FOR_RESULT_TIMEOUT_MS);
470     }
471 
472     /**
473      * Wait for enough results for settings to be applied
474      *
475      * @param resultListener The capture listener to get capture result back.
476      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
477      *                                       unknown.
478      */
waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)479     protected void waitForSettingsApplied(SimpleCaptureCallback resultListener,
480             int numResultWaitForUnknownLatency) {
481         int maxLatency = mStaticInfo.getSyncMaxLatency();
482         if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
483             maxLatency = numResultWaitForUnknownLatency;
484         }
485         // Wait for settings to take effect
486         waitForNumResults(resultListener, maxLatency);
487     }
488 
489     /**
490      * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
491      *
492      * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
493      * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
494      * is unknown.</p>
495      *
496      * <p>This is a no-op for {@code LEGACY} devices since they don't report
497      * the {@code aeState} result.</p>
498      *
499      * @param resultListener The capture listener to get capture result back.
500      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
501      *                                       unknown.
502      */
waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)503     protected void waitForAeStable(SimpleCaptureCallback resultListener,
504             int numResultWaitForUnknownLatency) {
505         CameraTestUtils.waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY,
506                 mStaticInfo, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT);
507     }
508 
509     /**
510      * Wait for AE to be: LOCKED
511      *
512      * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
513      * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
514      * is unknown.</p>
515      *
516      * <p>This is a no-op for {@code LEGACY} devices since they don't report
517      * the {@code aeState} result.</p>
518      *
519      * @param resultListener The capture listener to get capture result back.
520      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
521      *                                       unknown.
522      */
waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)523     protected void waitForAeLocked(SimpleCaptureCallback resultListener,
524             int numResultWaitForUnknownLatency) {
525 
526         waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
527 
528         if (!mStaticInfo.isHardwareLevelAtLeastLimited()) {
529             // No-op for legacy devices
530             return;
531         }
532 
533         List<Integer> expectedAeStates = new ArrayList<Integer>();
534         expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
535         CameraTestUtils.waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
536                 expectedAeStates, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT);
537     }
538 
539     /**
540      * Create an {@link ImageReader} object and get the surface.
541      *
542      * @param size The size of this ImageReader to be created.
543      * @param format The format of this ImageReader to be created
544      * @param maxNumImages The max number of images that can be acquired simultaneously.
545      * @param listener The listener used by this ImageReader to notify callbacks.
546      */
createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)547     protected void createImageReader(Size size, int format, int maxNumImages,
548             ImageReader.OnImageAvailableListener listener) throws Exception {
549         closeImageReader();
550 
551         ImageReader r = makeImageReader(size, format, maxNumImages, listener,
552                 mHandler);
553         mReader = r;
554         mReaderSurface = r.getSurface();
555     }
556 
557     /**
558      * Close the pending images then close current active {@link ImageReader} object.
559      */
closeImageReader()560     protected void closeImageReader() {
561         CameraTestUtils.closeImageReader(mReader);
562         mReader = null;
563         mReaderSurface = null;
564     }
565 
566     /**
567      * Close the pending images then close current active {@link ImageReader} objects.
568      */
closeImageReaders(ImageReader[] readers)569     protected void closeImageReaders(ImageReader[] readers) {
570         CameraTestUtils.closeImageReaders(readers);
571     }
572 
573     /**
574      * Setup still capture configuration and start preview.
575      *
576      * @param previewRequest The capture request to be used for preview
577      * @param stillRequest The capture request to be used for still capture
578      * @param previewSz Preview size
579      * @param captureSizes Still capture sizes
580      * @param formats The single capture image formats
581      * @param resultListener Capture result listener
582      * @param maxNumImages The max number of images set to the image reader
583      * @param imageListeners The single capture capture image listeners
584      * @param isHeic HEIC still capture if true, JPEG still capture if false
585      */
prepareStillCaptureAndStartPreview( CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners, boolean isHeic)586     protected ImageReader[] prepareStillCaptureAndStartPreview(
587             CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest,
588             Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener,
589             int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners,
590             boolean isHeic)
591             throws Exception {
592 
593         if ((captureSizes == null) || (formats == null) || (imageListeners == null) &&
594                 (captureSizes.length != formats.length) ||
595                 (formats.length != imageListeners.length)) {
596             throw new IllegalArgumentException("Invalid capture sizes/formats or image listeners!");
597         }
598 
599         if (VERBOSE) {
600             Log.v(TAG, String.format("Prepare still capture and preview (%s)",
601                     previewSz.toString()));
602         }
603 
604         // Update preview size.
605         updatePreviewSurface(previewSz);
606 
607         ImageReader[] readers = new ImageReader[captureSizes.length];
608         List<Surface> outputSurfaces = new ArrayList<Surface>();
609         outputSurfaces.add(mPreviewSurface);
610         for (int i = 0; i < captureSizes.length; i++) {
611             readers[i] = makeImageReader(captureSizes[i], formats[i], maxNumImages,
612                     imageListeners[i], mHandler);
613             outputSurfaces.add(readers[i].getSurface());
614         }
615 
616         mSessionListener = new BlockingSessionCallback();
617         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
618 
619         // Configure the requests.
620         previewRequest.addTarget(mPreviewSurface);
621         stillRequest.addTarget(mPreviewSurface);
622         for (int i = 0; i < readers.length; i++) {
623             stillRequest.addTarget(readers[i].getSurface());
624         }
625 
626         // Start preview.
627         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
628 
629         return readers;
630     }
631 
632     /**
633      * Open a camera device and get the StaticMetadata for a given camera id.
634      *
635      * @param cameraId The id of the camera device to be opened.
636      */
openDevice(String cameraId)637     protected void openDevice(String cameraId) throws Exception {
638         mCamera = CameraTestUtils.openCamera(
639                 mCameraManager, cameraId, mCameraListener, mHandler);
640         mCollector.setCameraId(cameraId);
641         mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
642                 CheckLevel.ASSERT, /*collector*/null);
643         if (mStaticInfo.isColorOutputSupported()) {
644             mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
645                     getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
646             m1080pBoundedOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
647                     PREVIEW_SIZE_BOUND);
648             mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
649             mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
650             // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
651             // in public API side.
652             mMinPreviewFrameDurationMap =
653                 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
654         }
655     }
656 
657     /**
658      * Close the current actively used camera device.
659      */
closeDevice()660     protected void closeDevice() {
661         if (mCamera != null) {
662             mCamera.close();
663             mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
664             mCamera = null;
665             mSession = null;
666             mSessionListener = null;
667             mStaticInfo = null;
668             mOrderedPreviewSizes = null;
669             mOrderedVideoSizes = null;
670             mOrderedStillSizes = null;
671         }
672     }
673 
674     /**
675      * Update the preview surface size.
676      *
677      * @param size The preview size to be updated.
678      */
updatePreviewSurface(Size size)679     protected void updatePreviewSurface(Size size) {
680         if (size.equals(mPreviewSize) && mPreviewSurface != null) {
681             Log.w(TAG, "Skipping update preview surface size...");
682             return;
683         }
684 
685         mPreviewSize = size;
686         Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
687         final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder();
688         Handler handler = new Handler(Looper.getMainLooper());
689         handler.post(new Runnable() {
690             @Override
691             public void run() {
692                 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
693             }
694         });
695 
696         boolean res = ctsActivity.waitForSurfaceSizeChanged(
697                 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(),
698                 mPreviewSize.getHeight());
699         assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
700         mPreviewHolder = holder;
701         mPreviewSurface = holder.getSurface();
702         assertNotNull("Preview surface is null", mPreviewSurface);
703         assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
704     }
705 
706     /**
707      * Recreate the SurfaceView's Surface
708      *
709      * Hide and unhide the activity's preview SurfaceView, so that its backing Surface is
710      * recreated
711      */
recreatePreviewSurface()712     protected void recreatePreviewSurface() {
713         Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
714         setPreviewVisibility(View.GONE);
715         boolean res = ctsActivity.waitForSurfaceState(
716             WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ false);
717         assertTrue("wait for surface destroyed timed out", res);
718         setPreviewVisibility(View.VISIBLE);
719         res = ctsActivity.waitForSurfaceState(
720             WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, /*valid*/ true);
721         assertTrue("wait for surface created timed out", res);
722     }
723 
724     /**
725      * Show/hide the preview SurfaceView.
726      *
727      * If set to View.GONE, the surfaceDestroyed callback will fire
728      * @param visibility the new new visibility to set, one of View.VISIBLE / INVISIBLE / GONE
729      */
setPreviewVisibility(int visibility)730     protected void setPreviewVisibility(int visibility) {
731         final Camera2SurfaceViewCtsActivity ctsActivity = mActivityRule.getActivity();
732         Handler handler = new Handler(Looper.getMainLooper());
733         handler.post(new Runnable() {
734             @Override
735             public void run() {
736                 ctsActivity.getSurfaceView().setVisibility(visibility);
737             }
738         });
739     }
740 
741     /**
742      * Setup single capture configuration and start preview.
743      *
744      * @param previewRequest The capture request to be used for preview
745      * @param stillRequest The capture request to be used for still capture
746      * @param previewSz Preview size
747      * @param captureSz Still capture size
748      * @param format The single capture image format
749      * @param resultListener Capture result listener
750      * @param maxNumImages The max number of images set to the image reader
751      * @param imageListener The single capture capture image listener
752      */
prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)753     protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
754             CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
755             CaptureCallback resultListener, int maxNumImages,
756             ImageReader.OnImageAvailableListener imageListener) throws Exception {
757         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, captureSz,
758             format, resultListener, null, maxNumImages, imageListener);
759     }
760 
761     /**
762      * Setup single capture configuration and start preview.
763      *
764      * @param previewRequest The capture request to be used for preview
765      * @param stillRequest The capture request to be used for still capture
766      * @param previewSz Preview size
767      * @param captureSz Still capture size
768      * @param format The single capture image format
769      * @param resultListener Capture result listener
770      * @param sessionListener Session listener
771      * @param maxNumImages The max number of images set to the image reader
772      * @param imageListener The single capture capture image listener
773      */
prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)774     protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
775             CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
776             CaptureCallback resultListener, CameraCaptureSession.StateCallback sessionListener,
777             int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception {
778         if (VERBOSE) {
779             Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
780                     captureSz.toString(), previewSz.toString()));
781         }
782 
783         // Update preview size.
784         updatePreviewSurface(previewSz);
785 
786         // Create ImageReader.
787         createImageReader(captureSz, format, maxNumImages, imageListener);
788 
789         // Configure output streams with preview and jpeg streams.
790         List<Surface> outputSurfaces = new ArrayList<Surface>();
791         outputSurfaces.add(mPreviewSurface);
792         outputSurfaces.add(mReaderSurface);
793         if (sessionListener == null) {
794             mSessionListener = new BlockingSessionCallback();
795         } else {
796             mSessionListener = new BlockingSessionCallback(sessionListener);
797         }
798         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
799 
800         // Configure the requests.
801         previewRequest.addTarget(mPreviewSurface);
802         stillRequest.addTarget(mPreviewSurface);
803         stillRequest.addTarget(mReaderSurface);
804 
805         // Start preview.
806         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
807     }
808 
809     /**
810      * Get the max preview size that supports the given fpsRange.
811      *
812      * @param fpsRange The fps range the returned size must support.
813      * @return max size that support the given fps range.
814      */
getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)815     protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) {
816         if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) {
817             throw new IllegalArgumentException("Invalid fps range argument");
818         }
819         if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) {
820             throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap"
821                     + " must be initialized");
822         }
823 
824         long[] frameDurationRange =
825                 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
826         for (Size size : mOrderedPreviewSizes) {
827             Long minDuration = mMinPreviewFrameDurationMap.get(size);
828             if (minDuration == null ||
829                     minDuration == 0) {
830                 if (mStaticInfo.isCapabilitySupported(
831                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
832                     throw new IllegalArgumentException(
833                             "No min frame duration available for the size " + size);
834                 }
835                 continue;
836             }
837             if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) {
838                 return size;
839             }
840         }
841 
842         // Search again for sizes not bounded by display size
843         for (Size size : m1080pBoundedOrderedPreviewSizes) {
844             Long minDuration = mMinPreviewFrameDurationMap.get(size);
845             if (minDuration == null ||
846                     minDuration == 0) {
847                 if (mStaticInfo.isCapabilitySupported(
848                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
849                     throw new IllegalArgumentException(
850                             "No min frame duration available for the size " + size);
851                 }
852                 continue;
853             }
854             if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) {
855                 return size;
856             }
857         }
858         return null;
859     }
860 
isReprocessSupported(String cameraId, int format)861     protected boolean isReprocessSupported(String cameraId, int format)
862             throws CameraAccessException {
863         if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) {
864             throw new IllegalArgumentException(
865                     "format " + format + " is not supported for reprocessing");
866         }
867 
868         StaticMetadata info =
869                 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
870                                    CheckLevel.ASSERT, /*collector*/ null);
871         int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
872         if (format == ImageFormat.PRIVATE) {
873             cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
874         }
875         return info.isCapabilitySupported(cap);
876     }
877 
getSuitableFpsRangeForDuration(String cameraId, long frameDuration)878     protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) {
879         return CameraTestUtils.getSuitableFpsRangeForDuration(cameraId, frameDuration, mStaticInfo);
880     }
881 }
882