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