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