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