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