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.WindowManager;
33 import android.content.Context;
34 import android.graphics.ImageFormat;
35 import android.hardware.camera2.CameraAccessException;
36 import android.hardware.camera2.CameraCaptureSession;
37 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
38 import android.hardware.camera2.CameraCharacteristics;
39 import android.hardware.camera2.CameraDevice;
40 import android.hardware.camera2.CameraManager;
41 import android.hardware.camera2.CameraMetadata;
42 import android.hardware.camera2.CaptureRequest;
43 import android.hardware.camera2.CaptureResult;
44 import android.util.Size;
45 import android.util.Range;
46 import android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity;
47 import android.hardware.camera2.cts.CameraTestUtils;
48 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
49 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
50 import android.hardware.camera2.cts.helpers.StaticMetadata;
51 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
52 
53 import com.android.ex.camera2.blocking.BlockingSessionCallback;
54 import com.android.ex.camera2.blocking.BlockingStateCallback;
55 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
56 
57 import java.util.ArrayList;
58 import java.util.HashMap;
59 import java.util.List;
60 
61 /**
62  * Camera2 Preview test case base class by using SurfaceView as rendering target.
63  *
64  * <p>This class encapsulates the SurfaceView based preview common functionalities.
65  * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs
66  * and CameraStateCallback are handled in this class. Some basic preview related utility
67  * functions are provided to facilitate the derived preview-based test classes.
68  * </p>
69  */
70 
71 public class Camera2SurfaceViewTestCase extends
72         ActivityInstrumentationTestCase2<Camera2SurfaceViewCtsActivity> {
73     private static final String TAG = "SurfaceViewTestCase";
74     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
75     private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
76 
77     // TODO: Use internal storage for this to make sure the file is only visible to test.
78     protected static final String DEBUG_FILE_NAME_BASE =
79             Environment.getExternalStorageDirectory().getPath();
80     protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
81     protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
82     protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
83     protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
84 
85     protected Context mContext;
86     protected CameraManager mCameraManager;
87     protected String[] mCameraIds;
88     protected HandlerThread mHandlerThread;
89     protected Handler mHandler;
90     protected BlockingStateCallback mCameraListener;
91     protected BlockingSessionCallback mSessionListener;
92     protected CameraErrorCollector mCollector;
93     // Per device fields:
94     protected StaticMetadata mStaticInfo;
95     protected CameraDevice mCamera;
96     protected CameraCaptureSession mSession;
97     protected ImageReader mReader;
98     protected Surface mReaderSurface;
99     protected Surface mPreviewSurface;
100     protected Size mPreviewSize;
101     protected List<Size> mOrderedPreviewSizes; // In descending order.
102     protected List<Size> mOrderedVideoSizes; // In descending order.
103     protected List<Size> mOrderedStillSizes; // In descending order.
104     protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
105 
106     protected WindowManager mWindowManager;
107 
Camera2SurfaceViewTestCase()108     public Camera2SurfaceViewTestCase() {
109         super(Camera2SurfaceViewCtsActivity.class);
110     }
111 
112     @Override
setUp()113     protected void setUp() throws Exception {
114         /**
115          * Set up the camera preview required environments, including activity,
116          * CameraManager, HandlerThread, Camera IDs, and CameraStateCallback.
117          */
118         super.setUp();
119         mContext = getActivity();
120         /**
121          * Workaround for mockito and JB-MR2 incompatibility
122          *
123          * Avoid java.lang.IllegalArgumentException: dexcache == null
124          * https://code.google.com/p/dexmaker/issues/detail?id=2
125          */
126         System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
127         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
128         assertNotNull("Unable to get CameraManager", mCameraManager);
129         mCameraIds = mCameraManager.getCameraIdList();
130         assertNotNull("Unable to get camera ids", mCameraIds);
131         mHandlerThread = new HandlerThread(TAG);
132         mHandlerThread.start();
133         mHandler = new Handler(mHandlerThread.getLooper());
134         mCameraListener = new BlockingStateCallback();
135         mCollector = new CameraErrorCollector();
136 
137         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
138     }
139 
140     @Override
tearDown()141     protected void tearDown() throws Exception {
142         // Teardown the camera preview required environments.
143         mHandlerThread.quitSafely();
144         mHandler = null;
145         mCameraListener = null;
146 
147         try {
148             mCollector.verify();
149         } catch (Throwable e) {
150             // When new Exception(e) is used, exception info will be printed twice.
151             throw new Exception(e.getMessage());
152         } finally {
153             super.tearDown();
154         }
155     }
156 
157     /**
158      * Start camera preview by using the given request, preview size and capture
159      * listener.
160      * <p>
161      * If preview is already started, calling this function will stop the
162      * current preview stream and start a new preview stream with given
163      * parameters. No need to call stopPreview between two startPreview calls.
164      * </p>
165      *
166      * @param request The request builder used to start the preview.
167      * @param previewSz The size of the camera device output preview stream.
168      * @param listener The callbacks the camera device will notify when preview
169      *            capture is available.
170      */
startPreview(CaptureRequest.Builder request, Size previewSz, CaptureCallback listener)171     protected void startPreview(CaptureRequest.Builder request, Size previewSz,
172             CaptureCallback listener) throws Exception {
173         // Update preview size.
174         updatePreviewSurface(previewSz);
175         if (VERBOSE) {
176             Log.v(TAG, "start preview with size " + mPreviewSize.toString());
177         }
178 
179         configurePreviewOutput(request);
180 
181         mSession.setRepeatingRequest(request.build(), listener, mHandler);
182     }
183 
184     /**
185      * Configure the preview output stream.
186      *
187      * @param request The request to be configured with preview surface
188      */
configurePreviewOutput(CaptureRequest.Builder request)189     protected void configurePreviewOutput(CaptureRequest.Builder request)
190             throws CameraAccessException {
191         List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
192         outputSurfaces.add(mPreviewSurface);
193         mSessionListener = new BlockingSessionCallback();
194         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
195 
196         request.addTarget(mPreviewSurface);
197     }
198 
199     /**
200      * Create a {@link CaptureRequest#Builder} and add the default preview surface.
201      *
202      * @return The {@link CaptureRequest#Builder} to be created
203      * @throws CameraAccessException When create capture request from camera fails
204      */
createRequestForPreview()205     protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException {
206         if (mPreviewSurface == null) {
207             throw new IllegalStateException(
208                     "Preview surface is not set yet, call updatePreviewSurface or startPreview"
209                     + "first to set the preview surface properly.");
210         }
211         CaptureRequest.Builder requestBuilder =
212                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
213         requestBuilder.addTarget(mPreviewSurface);
214         return requestBuilder;
215     }
216 
217     /**
218      * Stop preview for current camera device.
219      */
stopPreview()220     protected void stopPreview() throws Exception {
221         if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
222         // Stop repeat, wait for captures to complete, and disconnect from surfaces
223         mSession.close();
224     }
225 
226     /**
227      * Setup still (JPEG) capture configuration and start preview.
228      * <p>
229      * The default max number of image is set to image reader.
230      * </p>
231      *
232      * @param previewRequest The capture request to be used for preview
233      * @param stillRequest The capture request to be used for still capture
234      * @param previewSz Preview size
235      * @param stillSz The still capture size
236      * @param resultListener Capture result listener
237      * @param imageListener The still capture image listener
238      */
prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)239     protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
240             CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
241             CaptureCallback resultListener,
242             ImageReader.OnImageAvailableListener imageListener) throws Exception {
243         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
244                 ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener);
245     }
246 
247     /**
248      * Setup still (JPEG) capture configuration and start preview.
249      *
250      * @param previewRequest The capture request to be used for preview
251      * @param stillRequest The capture request to be used for still capture
252      * @param previewSz Preview size
253      * @param stillSz The still capture size
254      * @param resultListener Capture result listener
255      * @param maxNumImages The max number of images set to the image reader
256      * @param imageListener The still capture image listener
257      */
prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)258     protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
259             CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
260             CaptureCallback resultListener, int maxNumImages,
261             ImageReader.OnImageAvailableListener imageListener) throws Exception {
262         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
263                 ImageFormat.JPEG, resultListener, maxNumImages, imageListener);
264     }
265 
266     /**
267      * Setup raw capture configuration and start preview.
268      *
269      * <p>
270      * The default max number of image is set to image reader.
271      * </p>
272      *
273      * @param previewRequest The capture request to be used for preview
274      * @param rawRequest The capture request to be used for raw capture
275      * @param previewSz Preview size
276      * @param rawSz The raw capture size
277      * @param resultListener Capture result listener
278      * @param imageListener The raw capture image listener
279      */
prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener)280     protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
281             CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz,
282             CaptureCallback resultListener,
283             ImageReader.OnImageAvailableListener imageListener) throws Exception {
284         prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz,
285                 ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener);
286     }
287 
288     /**
289      * Wait for expected result key value available in a certain number of results.
290      *
291      * <p>
292      * Check the result immediately if numFramesWait is 0.
293      * </p>
294      *
295      * @param listener The capture listener to get capture result
296      * @param resultKey The capture result key associated with the result value
297      * @param expectedValue The result value need to be waited for
298      * @param numResultsWait Number of frame to wait before times out
299      * @throws TimeoutRuntimeException If more than numResultsWait results are
300      * seen before the result matching myRequest arrives, or each individual wait
301      * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
302      */
waitForResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, T expectedValue, int numResultsWait)303     protected static <T> void waitForResultValue(SimpleCaptureCallback listener,
304             CaptureResult.Key<T> resultKey,
305             T expectedValue, int numResultsWait) {
306         List<T> expectedValues = new ArrayList<T>();
307         expectedValues.add(expectedValue);
308         waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait);
309     }
310 
311     /**
312      * Wait for any expected result key values available in a certain number of results.
313      *
314      * <p>
315      * Check the result immediately if numFramesWait is 0.
316      * </p>
317      *
318      * @param listener The capture listener to get capture result.
319      * @param resultKey The capture result key associated with the result value.
320      * @param expectedValues The list of result value need to be waited for,
321      * return immediately if the list is empty.
322      * @param numResultsWait Number of frame to wait before times out.
323      * @throws TimeoutRuntimeException If more than numResultsWait results are.
324      * seen before the result matching myRequest arrives, or each individual wait
325      * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
326      */
waitForAnyResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, List<T> expectedValues, int numResultsWait)327     protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener,
328             CaptureResult.Key<T> resultKey,
329             List<T> expectedValues, int numResultsWait) {
330         if (numResultsWait < 0 || listener == null || expectedValues == null) {
331             throw new IllegalArgumentException(
332                     "Input must be non-negative number and listener/expectedValues "
333                     + "must be non-null");
334         }
335 
336         int i = 0;
337         CaptureResult result;
338         do {
339             result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
340             T value = result.get(resultKey);
341             for ( T expectedValue : expectedValues) {
342                 if (VERBOSE) {
343                     Log.v(TAG, "Current result value for key " + resultKey.getName() + " is: "
344                             + value.toString());
345                 }
346                 if (value.equals(expectedValue)) {
347                     return;
348                 }
349             }
350         } while (i++ < numResultsWait);
351 
352         throw new TimeoutRuntimeException(
353                 "Unable to get the expected result value " + expectedValues + " for key " +
354                         resultKey.getName() + " after waiting for " + numResultsWait + " results");
355     }
356 
357     /**
358      * Submit a capture once, then submit additional captures in order to ensure that
359      * the camera will be synchronized.
360      *
361      * <p>
362      * The additional capture count is determined by android.sync.maxLatency (or
363      * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
364      * </p>
365      *
366      * <p>Returns the number of captures that were submitted (at least 1), which is useful
367      * with {@link #waitForNumResults}.</p>
368      *
369      * @param request capture request to forward to {@link CameraDevice#capture}
370      * @param listener request listener to forward to {@link CameraDevice#capture}
371      * @param handler handler to forward to {@link CameraDevice#capture}
372      *
373      * @return the number of captures that were submitted
374      *
375      * @throws CameraAccessException if capturing failed
376      */
captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler)377     protected int captureRequestsSynchronized(
378             CaptureRequest request, CaptureCallback listener, Handler handler)
379                     throws CameraAccessException {
380         return captureRequestsSynchronized(request, /*count*/1, listener, handler);
381     }
382 
383     /**
384      * Submit a capture {@code count} times, then submit additional captures in order to ensure that
385      * the camera will be synchronized.
386      *
387      * <p>
388      * The additional capture count is determined by android.sync.maxLatency (or
389      * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
390      * </p>
391      *
392      * <p>Returns the number of captures that were submitted (at least 1), which is useful
393      * with {@link #waitForNumResults}.</p>
394      *
395      * @param request capture request to forward to {@link CameraDevice#capture}
396      * @param count the number of times to submit the request (minimally), must be at least 1
397      * @param listener request listener to forward to {@link CameraDevice#capture}
398      * @param handler handler to forward to {@link CameraDevice#capture}
399      *
400      * @return the number of captures that were submitted
401      *
402      * @throws IllegalArgumentException if {@code count} was not at least 1
403      * @throws CameraAccessException if capturing failed
404      */
captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler)405     protected int captureRequestsSynchronized(
406             CaptureRequest request, int count, CaptureCallback listener, Handler handler)
407                     throws CameraAccessException {
408         if (count < 1) {
409             throw new IllegalArgumentException("count must be positive");
410         }
411 
412         int maxLatency = mStaticInfo.getSyncMaxLatency();
413         if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
414             maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
415         }
416 
417         assertTrue("maxLatency is non-negative", maxLatency >= 0);
418 
419         int numCaptures = maxLatency + count;
420 
421         for (int i = 0; i < numCaptures; ++i) {
422             mSession.capture(request, listener, handler);
423         }
424 
425         return numCaptures;
426     }
427 
428     /**
429      * Wait for numResultWait frames
430      *
431      * @param resultListener The capture listener to get capture result back.
432      * @param numResultsWait Number of frame to wait
433      *
434      * @return the last result, or {@code null} if there was none
435      */
waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait)436     protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener,
437             int numResultsWait) {
438         if (numResultsWait < 0 || resultListener == null) {
439             throw new IllegalArgumentException(
440                     "Input must be positive number and listener must be non-null");
441         }
442 
443         CaptureResult result = null;
444         for (int i = 0; i < numResultsWait; i++) {
445             result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
446         }
447 
448         return result;
449     }
450 
451     /**
452      * Wait for enough results for settings to be applied
453      *
454      * @param resultListener The capture listener to get capture result back.
455      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
456      *                                       unknown.
457      */
waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)458     protected void waitForSettingsApplied(SimpleCaptureCallback resultListener,
459             int numResultWaitForUnknownLatency) {
460         int maxLatency = mStaticInfo.getSyncMaxLatency();
461         if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
462             maxLatency = numResultWaitForUnknownLatency;
463         }
464         // Wait for settings to take effect
465         waitForNumResults(resultListener, maxLatency);
466     }
467 
468 
469     /**
470      * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
471      *
472      * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
473      * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
474      * is unknown.</p>
475      *
476      * <p>This is a no-op for {@code LEGACY} devices since they don't report
477      * the {@code aeState} result.</p>
478      *
479      * @param resultListener The capture listener to get capture result back.
480      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
481      *                                       unknown.
482      */
waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)483     protected void waitForAeStable(SimpleCaptureCallback resultListener,
484             int numResultWaitForUnknownLatency) {
485         waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
486 
487         if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
488             // No-op for metadata
489             return;
490         }
491         List<Integer> expectedAeStates = new ArrayList<Integer>();
492         expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_CONVERGED));
493         expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED));
494         waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates,
495                 NUM_RESULTS_WAIT_TIMEOUT);
496     }
497 
498     /**
499      * Wait for AE to be: LOCKED
500      *
501      * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
502      * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
503      * is unknown.</p>
504      *
505      * <p>This is a no-op for {@code LEGACY} devices since they don't report
506      * the {@code aeState} result.</p>
507      *
508      * @param resultListener The capture listener to get capture result back.
509      * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
510      *                                       unknown.
511      */
waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency)512     protected void waitForAeLocked(SimpleCaptureCallback resultListener,
513             int numResultWaitForUnknownLatency) {
514 
515         waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
516 
517         if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
518             // No-op for legacy devices
519             return;
520         }
521 
522         List<Integer> expectedAeStates = new ArrayList<Integer>();
523         expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
524         waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates,
525                 NUM_RESULTS_WAIT_TIMEOUT);
526     }
527 
528     /**
529      * Create an {@link ImageReader} object and get the surface.
530      *
531      * @param size The size of this ImageReader to be created.
532      * @param format The format of this ImageReader to be created
533      * @param maxNumImages The max number of images that can be acquired simultaneously.
534      * @param listener The listener used by this ImageReader to notify callbacks.
535      */
createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener)536     protected void createImageReader(Size size, int format, int maxNumImages,
537             ImageReader.OnImageAvailableListener listener) throws Exception {
538         closeImageReader();
539 
540         ImageReader r = makeImageReader(size, format, maxNumImages, listener,
541                 mHandler);
542         mReader = r;
543         mReaderSurface = r.getSurface();
544     }
545 
546     /**
547      * Close the pending images then close current active {@link ImageReader} object.
548      */
closeImageReader()549     protected void closeImageReader() {
550         CameraTestUtils.closeImageReader(mReader);
551         mReader = null;
552         mReaderSurface = null;
553     }
554 
555     /**
556      * Open a camera device and get the StaticMetadata for a given camera id.
557      *
558      * @param cameraId The id of the camera device to be opened.
559      */
openDevice(String cameraId)560     protected void openDevice(String cameraId) throws Exception {
561         mCamera = CameraTestUtils.openCamera(
562                 mCameraManager, cameraId, mCameraListener, mHandler);
563         mCollector.setCameraId(cameraId);
564         mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
565                 CheckLevel.ASSERT, /*collector*/null);
566         if (mStaticInfo.isColorOutputSupported()) {
567             mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
568                     getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
569             mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
570             mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
571             // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
572             // in public API side.
573             mMinPreviewFrameDurationMap =
574                 mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
575         }
576     }
577 
578     /**
579      * Close the current actively used camera device.
580      */
closeDevice()581     protected void closeDevice() {
582         if (mCamera != null) {
583             mCamera.close();
584             mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
585             mCamera = null;
586             mSession = null;
587             mSessionListener = null;
588             mStaticInfo = null;
589             mOrderedPreviewSizes = null;
590             mOrderedVideoSizes = null;
591             mOrderedStillSizes = null;
592         }
593     }
594 
595     /**
596      * Update the preview surface size.
597      *
598      * @param size The preview size to be updated.
599      */
updatePreviewSurface(Size size)600     protected void updatePreviewSurface(Size size) {
601         if (size.equals(mPreviewSize) && mPreviewSurface != null) {
602             Log.w(TAG, "Skipping update preview surface size...");
603             return;
604         }
605 
606         mPreviewSize = size;
607         Camera2SurfaceViewCtsActivity ctsActivity = getActivity();
608         final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder();
609         Handler handler = new Handler(Looper.getMainLooper());
610         handler.post(new Runnable() {
611             @Override
612             public void run() {
613                 holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
614             }
615         });
616 
617         boolean res = ctsActivity.waitForSurfaceSizeChanged(
618                 WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(),
619                 mPreviewSize.getHeight());
620         assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
621         mPreviewSurface = holder.getSurface();
622         assertNotNull("Preview surface is null", mPreviewSurface);
623         assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
624     }
625 
626     /**
627      * Setup single capture configuration and start preview.
628      *
629      * @param previewRequest The capture request to be used for preview
630      * @param stillRequest The capture request to be used for still capture
631      * @param previewSz Preview size
632      * @param captureSz Still capture size
633      * @param format The single capture image format
634      * @param resultListener Capture result listener
635      * @param maxNumImages The max number of images set to the image reader
636      * @param imageListener The single capture capture image listener
637      */
prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener)638     protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
639             CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
640             CaptureCallback resultListener, int maxNumImages,
641             ImageReader.OnImageAvailableListener imageListener) throws Exception {
642         if (VERBOSE) {
643             Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
644                     captureSz.toString(), previewSz.toString()));
645         }
646 
647         // Update preview size.
648         updatePreviewSurface(previewSz);
649 
650         // Create ImageReader.
651         createImageReader(captureSz, format, maxNumImages, imageListener);
652 
653         // Configure output streams with preview and jpeg streams.
654         List<Surface> outputSurfaces = new ArrayList<Surface>();
655         outputSurfaces.add(mPreviewSurface);
656         outputSurfaces.add(mReaderSurface);
657         mSessionListener = new BlockingSessionCallback();
658         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
659 
660         // Configure the requests.
661         previewRequest.addTarget(mPreviewSurface);
662         stillRequest.addTarget(mPreviewSurface);
663         stillRequest.addTarget(mReaderSurface);
664 
665         // Start preview.
666         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
667     }
668 
669     /**
670      * Get the max preview size that supports the given fpsRange.
671      *
672      * @param fpsRange The fps range the returned size must support.
673      * @return max size that support the given fps range.
674      */
getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange)675     protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) {
676         if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) {
677             throw new IllegalArgumentException("Invalid fps range argument");
678         }
679         if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) {
680             throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap"
681                     + " must be initialized");
682         }
683 
684         long[] frameDurationRange =
685                 new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
686         for (Size size : mOrderedPreviewSizes) {
687             Long minDuration = mMinPreviewFrameDurationMap.get(size);
688             if (minDuration == null ||
689                     minDuration == 0) {
690                 if (mStaticInfo.isCapabilitySupported(
691                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
692                     throw new IllegalArgumentException(
693                             "No min frame duration available for the size " + size);
694                 }
695                 continue;
696             }
697             if (minDuration <= frameDurationRange[0]) {
698                 return size;
699             }
700         }
701 
702         return null;
703     }
704 
isReprocessSupported(String cameraId, int format)705     protected boolean isReprocessSupported(String cameraId, int format)
706             throws CameraAccessException {
707         if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) {
708             throw new IllegalArgumentException(
709                     "format " + format + " is not supported for reprocessing");
710         }
711 
712         StaticMetadata info =
713                 new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
714                                    CheckLevel.ASSERT, /*collector*/ null);
715         int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
716         if (format == ImageFormat.PRIVATE) {
717             cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
718         }
719         return info.isCapabilitySupported(cap);
720     }
721 }
722