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