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