1 /*
2  * Copyright 2013 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;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.*;
20 import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
21 import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
22 import static org.mockito.Mockito.*;
23 import static android.hardware.camera2.CaptureRequest.*;
24 
25 import android.content.Context;
26 import android.graphics.SurfaceTexture;
27 import android.graphics.ImageFormat;
28 import android.hardware.camera2.CameraAccessException;
29 import android.hardware.camera2.CameraCaptureSession;
30 import android.hardware.camera2.CameraCharacteristics;
31 import android.hardware.camera2.CameraDevice;
32 import android.hardware.camera2.CameraMetadata;
33 import android.hardware.camera2.CaptureFailure;
34 import android.hardware.camera2.CaptureRequest;
35 import android.hardware.camera2.CaptureResult;
36 import android.hardware.camera2.TotalCaptureResult;
37 import android.hardware.camera2.cts.helpers.StaticMetadata;
38 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
39 import android.hardware.camera2.params.MeteringRectangle;
40 import android.media.ImageReader;
41 import android.os.Handler;
42 import android.os.SystemClock;
43 import android.util.Log;
44 import android.util.Range;
45 import android.view.Surface;
46 
47 import com.android.ex.camera2.blocking.BlockingSessionCallback;
48 import com.android.ex.camera2.blocking.BlockingStateCallback;
49 import com.android.ex.camera2.utils.StateWaiter;
50 
51 import org.mockito.ArgumentMatcher;
52 
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.concurrent.locks.Condition;
56 import java.util.concurrent.locks.Lock;
57 import java.util.concurrent.locks.ReentrantLock;
58 import java.util.HashSet;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.HashMap;
62 import java.util.Set;
63 import android.util.Size;
64 import java.util.concurrent.LinkedBlockingQueue;
65 import java.util.concurrent.TimeUnit;
66 
67 /**
68  * <p>Basic test for CameraDevice APIs.</p>
69  */
70 public class CameraDeviceTest extends Camera2AndroidTestCase {
71     private static final String TAG = "CameraDeviceTest";
72     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
73     private static final int ERROR_LISTENER_WAIT_TIMEOUT_MS = 1000;
74     private static final int REPEATING_CAPTURE_EXPECTED_RESULT_COUNT = 5;
75     private static final int MAX_NUM_IMAGES = 5;
76     private static final int MIN_FPS_REQUIRED_FOR_STREAMING = 20;
77 
78     private CameraCaptureSession mSession;
79 
80     private BlockingStateCallback mCameraMockListener;
81     private int mLatestDeviceState = STATE_UNINITIALIZED;
82     private BlockingSessionCallback mSessionMockListener;
83     private StateWaiter mSessionWaiter;
84     private int mLatestSessionState = -1; // uninitialized
85 
86     private static int[] sTemplates = new int[] {
87             CameraDevice.TEMPLATE_PREVIEW,
88             CameraDevice.TEMPLATE_RECORD,
89             CameraDevice.TEMPLATE_STILL_CAPTURE,
90             CameraDevice.TEMPLATE_VIDEO_SNAPSHOT
91     };
92 
93     // Request templates that are unsupported by LEGACY mode.
94     private static Set<Integer> sLegacySkipTemplates = new HashSet<>();
95     static {
96         sLegacySkipTemplates.add(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
97         sLegacySkipTemplates.add(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
98         sLegacySkipTemplates.add(CameraDevice.TEMPLATE_MANUAL);
99     }
100 
101     @Override
setContext(Context context)102     public void setContext(Context context) {
103         super.setContext(context);
104 
105         /**
106          * Workaround for mockito and JB-MR2 incompatibility
107          *
108          * Avoid java.lang.IllegalArgumentException: dexcache == null
109          * https://code.google.com/p/dexmaker/issues/detail?id=2
110          */
111         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
112 
113         /**
114          * Create error listener in context scope, to catch asynchronous device error.
115          * Use spy object here since we want to use the SimpleDeviceListener callback
116          * implementation (spy doesn't stub the functions unless we ask it to do so).
117          */
118         mCameraMockListener = spy(new BlockingStateCallback());
119     }
120 
121     @Override
setUp()122     protected void setUp() throws Exception {
123         super.setUp();
124         /**
125          * Due to the asynchronous nature of camera device error callback, we
126          * have to make sure device doesn't run into error state before. If so,
127          * fail the rest of the tests. This is especially needed when error
128          * callback is fired too late.
129          */
130         verify(mCameraMockListener, never())
131                 .onError(
132                     any(CameraDevice.class),
133                     anyInt());
134         verify(mCameraMockListener, never())
135                 .onDisconnected(
136                     any(CameraDevice.class));
137 
138         mCameraListener = mCameraMockListener;
139         createDefaultImageReader(DEFAULT_CAPTURE_SIZE, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
140                 new ImageDropperListener());
141     }
142 
143     @Override
tearDown()144     protected void tearDown() throws Exception {
145         super.tearDown();
146     }
147 
148     /**
149      * <p>
150      * Test camera capture request preview capture template.
151      * </p>
152      *
153      * <p>
154      * The request template returned by the camera device must include a
155      * necessary set of metadata keys, and their values must be set correctly.
156      * It mainly requires below settings:
157      * </p>
158      * <ul>
159      * <li>All 3A settings are auto.</li>
160      * <li>All sensor settings are not null.</li>
161      * <li>All ISP processing settings should be non-manual, and the camera
162      * device should make sure the stable frame rate is guaranteed for the given
163      * settings.</li>
164      * </ul>
165      */
testCameraDevicePreviewTemplate()166     public void testCameraDevicePreviewTemplate() throws Exception {
167         for (int i = 0; i < mCameraIds.length; i++) {
168             captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_PREVIEW);
169         }
170 
171         // TODO: test the frame rate sustainability in preview use case test.
172     }
173 
174     /**
175      * <p>
176      * Test camera capture request still capture template.
177      * </p>
178      *
179      * <p>
180      * The request template returned by the camera device must include a
181      * necessary set of metadata keys, and their values must be set correctly.
182      * It mainly requires below settings:
183      * </p>
184      * <ul>
185      * <li>All 3A settings are auto.</li>
186      * <li>All sensor settings are not null.</li>
187      * <li>All ISP processing settings should be non-manual, and the camera
188      * device should make sure the high quality takes priority to the stable
189      * frame rate for the given settings.</li>
190      * </ul>
191      */
testCameraDeviceStillTemplate()192     public void testCameraDeviceStillTemplate() throws Exception {
193         for (int i = 0; i < mCameraIds.length; i++) {
194             captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
195         }
196     }
197 
198     /**
199      * <p>
200      * Test camera capture video recording template.
201      * </p>
202      *
203      * <p>
204      * The request template returned by the camera device must include a
205      * necessary set of metadata keys, and their values must be set correctly.
206      * It has the similar requirement as preview, with one difference:
207      * </p>
208      * <ul>
209      * <li>Frame rate should be stable, for example, wide fps range like [7, 30]
210      * is a bad setting.</li>
211      */
testCameraDeviceRecordingTemplate()212     public void testCameraDeviceRecordingTemplate() throws Exception {
213         for (int i = 0; i < mCameraIds.length; i++) {
214             captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_RECORD);
215         }
216 
217         // TODO: test the frame rate sustainability in recording use case test.
218     }
219 
220     /**
221      *<p>Test camera capture video snapshot template.</p>
222      *
223      * <p>The request template returned by the camera device must include a necessary set of
224      * metadata keys, and their values must be set correctly. It has the similar requirement
225      * as recording, with an additional requirement: the settings should maximize image quality
226      * without compromising stable frame rate.</p>
227      */
testCameraDeviceVideoSnapShotTemplate()228     public void testCameraDeviceVideoSnapShotTemplate() throws Exception {
229         for (int i = 0; i < mCameraIds.length; i++) {
230             captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
231         }
232 
233         // TODO: test the frame rate sustainability in video snapshot use case test.
234     }
235 
236     /**
237      *<p>Test camera capture request zero shutter lag template.</p>
238      *
239      * <p>The request template returned by the camera device must include a necessary set of
240      * metadata keys, and their values must be set correctly. It has the similar requirement
241      * as preview, with an additional requirement: </p>
242      */
testCameraDeviceZSLTemplate()243     public void testCameraDeviceZSLTemplate() throws Exception {
244         for (int i = 0; i < mCameraIds.length; i++) {
245             captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
246         }
247     }
248 
249     /**
250      * <p>
251      * Test camera capture request manual template.
252      * </p>
253      *
254      * <p>
255      * The request template returned by the camera device must include a
256      * necessary set of metadata keys, and their values must be set correctly. It
257      * mainly requires below settings:
258      * </p>
259      * <ul>
260      * <li>All 3A settings are manual.</li>
261      * <li>ISP processing parameters are set to preview quality.</li>
262      * <li>The manual capture parameters (exposure, sensitivity, and so on) are
263      * set to reasonable defaults.</li>
264      * </ul>
265      */
testCameraDeviceManualTemplate()266     public void testCameraDeviceManualTemplate() throws Exception {
267         for (int i = 0; i < mCameraIds.length; i++) {
268             captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_MANUAL);
269         }
270     }
271 
testCameraDeviceCreateCaptureBuilder()272     public void testCameraDeviceCreateCaptureBuilder() throws Exception {
273         for (int i = 0; i < mCameraIds.length; i++) {
274             try {
275                 openDevice(mCameraIds[i], mCameraMockListener);
276                 /**
277                  * Test: that each template type is supported, and that its required fields are
278                  * present.
279                  */
280                 for (int j = 0; j < sTemplates.length; j++) {
281                     // Skip video snapshots for LEGACY mode
282                     if (mStaticInfo.isHardwareLevelLegacy() &&
283                             sTemplates[j] == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
284                         continue;
285                     }
286                     // Skip non-PREVIEW templates for non-color output
287                     if (!mStaticInfo.isColorOutputSupported() &&
288                             sTemplates[j] != CameraDevice.TEMPLATE_PREVIEW) {
289                         continue;
290                     }
291                     CaptureRequest.Builder capReq = mCamera.createCaptureRequest(sTemplates[j]);
292                     assertNotNull("Failed to create capture request", capReq);
293                     if (mStaticInfo.areKeysAvailable(CaptureRequest.SENSOR_EXPOSURE_TIME)) {
294                         assertNotNull("Missing field: SENSOR_EXPOSURE_TIME",
295                                 capReq.get(CaptureRequest.SENSOR_EXPOSURE_TIME));
296                     }
297                     if (mStaticInfo.areKeysAvailable(CaptureRequest.SENSOR_SENSITIVITY)) {
298                         assertNotNull("Missing field: SENSOR_SENSITIVITY",
299                                 capReq.get(CaptureRequest.SENSOR_SENSITIVITY));
300                     }
301                 }
302             }
303             finally {
304                 try {
305                     closeSession();
306                 } finally {
307                     closeDevice(mCameraIds[i], mCameraMockListener);
308                 }
309             }
310         }
311     }
312 
testCameraDeviceSetErrorListener()313     public void testCameraDeviceSetErrorListener() throws Exception {
314         for (int i = 0; i < mCameraIds.length; i++) {
315             try {
316                 openDevice(mCameraIds[i], mCameraMockListener);
317                 /**
318                  * Test: that the error listener can be set without problems.
319                  * Also, wait some time to check if device doesn't run into error.
320                  */
321                 SystemClock.sleep(ERROR_LISTENER_WAIT_TIMEOUT_MS);
322                 verify(mCameraMockListener, never())
323                         .onError(
324                                 any(CameraDevice.class),
325                                 anyInt());
326             }
327             finally {
328                 try {
329                     closeSession();
330                 } finally {
331                     closeDevice(mCameraIds[i], mCameraMockListener);
332                 }
333             }
334         }
335     }
336 
testCameraDeviceCapture()337     public void testCameraDeviceCapture() throws Exception {
338         runCaptureTest(/*burst*/false, /*repeating*/false, /*abort*/false);
339     }
340 
testCameraDeviceCaptureBurst()341     public void testCameraDeviceCaptureBurst() throws Exception {
342         runCaptureTest(/*burst*/true, /*repeating*/false, /*abort*/false);
343     }
344 
testCameraDeviceRepeatingRequest()345     public void testCameraDeviceRepeatingRequest() throws Exception {
346         runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/false);
347     }
348 
testCameraDeviceRepeatingBurst()349     public void testCameraDeviceRepeatingBurst() throws Exception {
350         runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/false);
351     }
352 
353     /**
354      * Test {@link android.hardware.camera2.CameraCaptureSession#abortCaptures} API.
355      *
356      * <p>Abort is the fastest way to idle the camera device for reconfiguration with
357      * {@link android.hardware.camera2.CameraCaptureSession#abortCaptures}, at the cost of
358      * discarding in-progress work. Once the abort is complete, the idle callback will be called.
359      * </p>
360      */
testCameraDeviceAbort()361     public void testCameraDeviceAbort() throws Exception {
362         runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/true);
363         runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/true);
364         /**
365          * TODO: this is only basic test of abort. we probably should also test below cases:
366          *
367          * 1. Performance. Make sure abort is faster than stopRepeating, we can test each one a
368          * couple of times, then compare the average. Also, for abortCaptures() alone, we should
369          * make sure it doesn't take too long time (e.g. <100ms for full devices, <500ms for limited
370          * devices), after the abort, we should be able to get all results back very quickly.  This
371          * can be done in performance test.
372          *
373          * 2. Make sure all in-flight request comes back after abort, e.g. submit a couple of
374          * long exposure single captures, then abort, then check if we can get the pending
375          * request back quickly.
376          *
377          * 3. Also need check onCaptureSequenceCompleted for repeating burst after abortCaptures().
378          */
379     }
380 
381     /**
382      * Test invalid capture (e.g. null or empty capture request).
383      */
testInvalidCapture()384     public void testInvalidCapture() throws Exception {
385         for (int i = 0; i < mCameraIds.length; i++) {
386             try {
387                 openDevice(mCameraIds[i], mCameraMockListener);
388                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
389 
390                 prepareCapture();
391 
392                 invalidRequestCaptureTestByCamera();
393 
394                 closeSession();
395             }
396             finally {
397                 closeDevice(mCameraIds[i], mCameraMockListener);
398             }
399         }
400     }
401 
402     /**
403      * Test to ensure that we can call camera2 API methods inside callbacks.
404      *
405      * Tests:
406      *  onOpened -> createCaptureSession, createCaptureRequest
407      *  onConfigured -> getDevice, abortCaptures,
408      *     createCaptureRequest, capture, setRepeatingRequest, stopRepeating
409      *  onCaptureCompleted -> createCaptureRequest, getDevice, abortCaptures,
410      *     capture, setRepeatingRequest, stopRepeating, session+device.close
411      */
testChainedOperation()412     public void testChainedOperation() throws Throwable {
413 
414         final ArrayList<Surface> outputs = new ArrayList<>();
415         outputs.add(mReaderSurface);
416 
417         // A queue for the chained listeners to push results to
418         // A success Throwable indicates no errors; other Throwables detail a test failure;
419         // nulls indicate timeouts.
420         final Throwable success = new Throwable("Success");
421         final LinkedBlockingQueue<Throwable> results = new LinkedBlockingQueue<>();
422 
423         // Define listeners
424         // A cascade of Device->Session->Capture listeners, each of which invokes at least one
425         // method on the camera device or session.
426 
427         class ChainedCaptureCallback extends CameraCaptureSession.CaptureCallback {
428             public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
429                     TotalCaptureResult result) {
430                 try {
431                     CaptureRequest.Builder request2 =
432                             session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
433                     request2.addTarget(mReaderSurface);
434 
435                     // Some calls to the camera for coverage
436                     session.abortCaptures();
437                     session.capture(request2.build(),
438                             /*listener*/ null, /*handler*/ null);
439                     session.setRepeatingRequest(request2.build(),
440                             /*listener*/ null, /*handler*/ null);
441                     session.stopRepeating();
442 
443                     CameraDevice camera = session.getDevice();
444                     session.close();
445                     camera.close();
446 
447                     results.offer(success);
448                 } catch (Throwable t) {
449                     results.offer(t);
450                 }
451             }
452 
453             public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
454                     CaptureFailure failure) {
455                 try {
456                     CameraDevice camera = session.getDevice();
457                     session.close();
458                     camera.close();
459                     fail("onCaptureFailed invoked with failure reason: " + failure.getReason());
460                 } catch (Throwable t) {
461                     results.offer(t);
462                 }
463             }
464         }
465 
466         class ChainedSessionListener extends CameraCaptureSession.StateCallback {
467             private final ChainedCaptureCallback mCaptureCallback = new ChainedCaptureCallback();
468 
469             public void onConfigured(CameraCaptureSession session) {
470                 try {
471                     CaptureRequest.Builder request =
472                             session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
473                     request.addTarget(mReaderSurface);
474                     // Some calls to the camera for coverage
475                     session.getDevice();
476                     session.abortCaptures();
477                     // The important call for the next level of chaining
478                     session.capture(request.build(), mCaptureCallback, mHandler);
479                     // Some more calls
480                     session.setRepeatingRequest(request.build(),
481                             /*listener*/ null, /*handler*/ null);
482                     session.stopRepeating();
483                     results.offer(success);
484                 } catch (Throwable t) {
485                     results.offer(t);
486                 }
487             }
488 
489             public void onConfigureFailed(CameraCaptureSession session) {
490                 try {
491                     CameraDevice camera = session.getDevice();
492                     session.close();
493                     camera.close();
494                     fail("onConfigureFailed was invoked");
495                 } catch (Throwable t) {
496                     results.offer(t);
497                 }
498             }
499         }
500 
501         class ChainedCameraListener extends CameraDevice.StateCallback {
502             private final ChainedSessionListener mSessionListener = new ChainedSessionListener();
503 
504             public CameraDevice cameraDevice;
505 
506             public void onOpened(CameraDevice camera) {
507 
508                 cameraDevice = camera;
509                 try {
510                     // Some calls for coverage
511                     camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
512                     // The important call for next level of chaining
513                     camera.createCaptureSession(outputs, mSessionListener, mHandler);
514                     results.offer(success);
515                 } catch (Throwable t) {
516                     try {
517                         camera.close();
518                         results.offer(t);
519                     } catch (Throwable t2) {
520                         Log.e(TAG,
521                                 "Second failure reached; discarding first exception with trace " +
522                                 Log.getStackTraceString(t));
523                         results.offer(t2);
524                     }
525                 }
526             }
527 
528             public void onDisconnected(CameraDevice camera) {
529                 try {
530                     camera.close();
531                     fail("onDisconnected invoked");
532                 } catch (Throwable t) {
533                     results.offer(t);
534                 }
535             }
536 
537             public void onError(CameraDevice camera, int error) {
538                 try {
539                     camera.close();
540                     fail("onError invoked with error code: " + error);
541                 } catch (Throwable t) {
542                     results.offer(t);
543                 }
544             }
545         }
546 
547         // Actual test code
548 
549         for (int i = 0; i < mCameraIds.length; i++) {
550             Throwable result;
551 
552             if (!(new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraIds[i]))).
553                     isColorOutputSupported()) {
554                 Log.i(TAG, "Camera " + mCameraIds[i] + " does not support color outputs, skipping");
555                 continue;
556             }
557 
558             // Start chained cascade
559             ChainedCameraListener cameraListener = new ChainedCameraListener();
560             mCameraManager.openCamera(mCameraIds[i], cameraListener, mHandler);
561 
562             // Check if open succeeded
563             result = results.poll(CAMERA_OPEN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
564             if (result != success) {
565                 if (cameraListener.cameraDevice != null) cameraListener.cameraDevice.close();
566                 if (result == null) {
567                     fail("Timeout waiting for camera open");
568                 } else {
569                     throw result;
570                 }
571             }
572 
573             // Check if configure succeeded
574             result = results.poll(SESSION_CONFIGURE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
575             if (result != success) {
576                 if (cameraListener.cameraDevice != null) cameraListener.cameraDevice.close();
577                 if (result == null) {
578                     fail("Timeout waiting for session configure");
579                 } else {
580                     throw result;
581                 }
582             }
583 
584             // Check if capture succeeded
585             result = results.poll(CAPTURE_RESULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
586             if (result != success) {
587                 if (cameraListener.cameraDevice != null) cameraListener.cameraDevice.close();
588                 if (result == null) {
589                     fail("Timeout waiting for capture completion");
590                 } else {
591                     throw result;
592                 }
593             }
594         }
595     }
596 
597     /**
598      * Verify basic semantics and error conditions of the prepare call.
599      *
600      */
testPrepare()601     public void testPrepare() throws Exception {
602         for (int i = 0; i < mCameraIds.length; i++) {
603             try {
604                 openDevice(mCameraIds[i], mCameraMockListener);
605                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
606                 if (!mStaticInfo.isColorOutputSupported()) {
607                     Log.i(TAG, "Camera " + mCameraIds[i] +
608                             " does not support color outputs, skipping");
609                     continue;
610                 }
611 
612                 prepareTestByCamera();
613             }
614             finally {
615                 closeDevice(mCameraIds[i], mCameraMockListener);
616             }
617         }
618     }
619 
620     /**
621      * Verify creating sessions back to back.
622      */
testCreateSessions()623     public void testCreateSessions() throws Exception {
624         for (int i = 0; i < mCameraIds.length; i++) {
625             try {
626                 openDevice(mCameraIds[i], mCameraMockListener);
627                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
628                 if (!mStaticInfo.isColorOutputSupported()) {
629                     Log.i(TAG, "Camera " + mCameraIds[i] +
630                             " does not support color outputs, skipping");
631                     continue;
632                 }
633 
634                 testCreateSessionsByCamera(mCameraIds[i]);
635             }
636             finally {
637                 closeDevice(mCameraIds[i], mCameraMockListener);
638             }
639         }
640     }
641 
642     /**
643      * Verify creating sessions back to back and only the last one is valid for
644      * submitting requests.
645      */
testCreateSessionsByCamera(String cameraId)646     private void testCreateSessionsByCamera(String cameraId) throws Exception {
647         final int NUM_SESSIONS = 3;
648         final int SESSION_TIMEOUT_MS = 1000;
649         final int CAPTURE_TIMEOUT_MS = 3000;
650 
651         if (VERBOSE) {
652             Log.v(TAG, "Testing creating sessions for camera " + cameraId);
653         }
654 
655         Size yuvSize = getSortedSizesForFormat(cameraId, mCameraManager, ImageFormat.YUV_420_888,
656                 /*bound*/null).get(0);
657         Size jpegSize = getSortedSizesForFormat(cameraId, mCameraManager, ImageFormat.JPEG,
658                 /*bound*/null).get(0);
659 
660         // Create a list of image readers. JPEG for last one and YUV for the rest.
661         List<ImageReader> imageReaders = new ArrayList<>();
662         List<CameraCaptureSession> allSessions = new ArrayList<>();
663 
664         try {
665             for (int i = 0; i < NUM_SESSIONS - 1; i++) {
666                 imageReaders.add(ImageReader.newInstance(yuvSize.getWidth(), yuvSize.getHeight(),
667                         ImageFormat.YUV_420_888, /*maxImages*/1));
668             }
669             imageReaders.add(ImageReader.newInstance(jpegSize.getWidth(), jpegSize.getHeight(),
670                     ImageFormat.JPEG, /*maxImages*/1));
671 
672             // Create multiple sessions back to back.
673             MultipleSessionCallback sessionListener =
674                     new MultipleSessionCallback(/*failOnConfigureFailed*/true);
675             for (int i = 0; i < NUM_SESSIONS; i++) {
676                 List<Surface> outputs = new ArrayList<>();
677                 outputs.add(imageReaders.get(i).getSurface());
678                 mCamera.createCaptureSession(outputs, sessionListener, mHandler);
679             }
680 
681             // Verify we get onConfigured() for all sessions.
682             allSessions = sessionListener.getAllSessions(NUM_SESSIONS,
683                     SESSION_TIMEOUT_MS * NUM_SESSIONS);
684             assertEquals(String.format("Got %d sessions but configured %d sessions",
685                     allSessions.size(), NUM_SESSIONS), allSessions.size(), NUM_SESSIONS);
686 
687             // Verify all sessions except the last one are closed.
688             for (int i = 0; i < NUM_SESSIONS - 1; i++) {
689                 sessionListener.waitForSessionClose(allSessions.get(i), SESSION_TIMEOUT_MS);
690             }
691 
692             // Verify we can capture a frame with the last session.
693             CameraCaptureSession session = allSessions.get(allSessions.size() - 1);
694             SimpleCaptureCallback captureListener = new SimpleCaptureCallback();
695             ImageReader reader = imageReaders.get(imageReaders.size() - 1);
696             SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
697             reader.setOnImageAvailableListener(imageListener, mHandler);
698 
699             CaptureRequest.Builder builder =
700                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
701             builder.addTarget(reader.getSurface());
702             CaptureRequest request = builder.build();
703 
704             session.capture(request, captureListener, mHandler);
705             captureListener.getCaptureResultForRequest(request, CAPTURE_TIMEOUT_MS);
706             imageListener.getImage(CAPTURE_TIMEOUT_MS).close();
707         } finally {
708             for (ImageReader reader : imageReaders) {
709                 reader.close();
710             }
711             for (CameraCaptureSession session : allSessions) {
712                 session.close();
713             }
714         }
715     }
716 
prepareTestByCamera()717     private void prepareTestByCamera() throws Exception {
718         final int PREPARE_TIMEOUT_MS = 10000;
719 
720         mSessionMockListener = spy(new BlockingSessionCallback());
721 
722         SurfaceTexture output1 = new SurfaceTexture(1);
723         Surface output1Surface = new Surface(output1);
724         SurfaceTexture output2 = new SurfaceTexture(2);
725         Surface output2Surface = new Surface(output2);
726 
727         List<Surface> outputSurfaces = new ArrayList<>(
728             Arrays.asList(output1Surface, output2Surface));
729         mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
730 
731         mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
732 
733         // Try basic prepare
734 
735         mSession.prepare(output1Surface);
736 
737         verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(1))
738                 .onSurfacePrepared(eq(mSession), eq(output1Surface));
739 
740         // Should not complain if preparing already prepared stream
741 
742         mSession.prepare(output1Surface);
743 
744         verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(2))
745                 .onSurfacePrepared(eq(mSession), eq(output1Surface));
746 
747         // Check surface not included in session
748 
749         SurfaceTexture output3 = new SurfaceTexture(3);
750         Surface output3Surface = new Surface(output3);
751         try {
752             mSession.prepare(output3Surface);
753             // Legacy camera prepare always succeed
754             if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
755                 fail("Preparing surface not part of session must throw IllegalArgumentException");
756             }
757         } catch (IllegalArgumentException e) {
758             // expected
759         }
760 
761         // Ensure second prepare also works
762 
763         mSession.prepare(output2Surface);
764 
765         verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(1))
766                 .onSurfacePrepared(eq(mSession), eq(output2Surface));
767 
768         // Use output1
769 
770         CaptureRequest.Builder r = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
771         r.addTarget(output1Surface);
772 
773         mSession.capture(r.build(), null, null);
774 
775         try {
776             mSession.prepare(output1Surface);
777             // Legacy camera prepare always succeed
778             if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
779                 fail("Preparing already-used surface must throw IllegalArgumentException");
780             }
781         } catch (IllegalArgumentException e) {
782             // expected
783         }
784 
785         // Create new session with outputs 1 and 3, ensure output1Surface still can't be prepared
786         // again
787 
788         mSessionMockListener = spy(new BlockingSessionCallback());
789 
790         outputSurfaces = new ArrayList<>(
791             Arrays.asList(output1Surface, output3Surface));
792         mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
793 
794         mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
795 
796         try {
797             mSession.prepare(output1Surface);
798             // Legacy camera prepare always succeed
799             if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
800                 fail("Preparing surface used in previous session must throw " +
801                         "IllegalArgumentException");
802             }
803         } catch (IllegalArgumentException e) {
804             // expected
805         }
806 
807         // Use output3, wait for result, then make sure prepare still doesn't work
808 
809         r = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
810         r.addTarget(output3Surface);
811 
812         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
813         mSession.capture(r.build(), resultListener, mHandler);
814 
815         resultListener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
816 
817         try {
818             mSession.prepare(output3Surface);
819             // Legacy camera prepare always succeed
820             if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
821                 fail("Preparing already-used surface must throw IllegalArgumentException");
822             }
823         } catch (IllegalArgumentException e) {
824             // expected
825         }
826 
827         // Create new session with outputs 1 and 2, ensure output2Surface can be prepared again
828 
829         mSessionMockListener = spy(new BlockingSessionCallback());
830 
831         outputSurfaces = new ArrayList<>(
832             Arrays.asList(output1Surface, output2Surface));
833         mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
834 
835         mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
836 
837         mSession.prepare(output2Surface);
838 
839         verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(1))
840                 .onSurfacePrepared(eq(mSession), eq(output2Surface));
841 
842         try {
843             mSession.prepare(output1Surface);
844             // Legacy camera prepare always succeed
845             if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
846                 fail("Preparing surface used in previous session must throw " +
847                         "IllegalArgumentException");
848             }
849         } catch (IllegalArgumentException e) {
850             // expected
851         }
852 
853     }
854 
855 
invalidRequestCaptureTestByCamera()856     private void invalidRequestCaptureTestByCamera() throws Exception {
857         if (VERBOSE) Log.v(TAG, "invalidRequestCaptureTestByCamera");
858 
859         List<CaptureRequest> emptyRequests = new ArrayList<CaptureRequest>();
860         CaptureRequest.Builder requestBuilder =
861                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
862         CaptureRequest unConfiguredRequest = requestBuilder.build();
863         List<CaptureRequest> unConfiguredRequests = new ArrayList<CaptureRequest>();
864         unConfiguredRequests.add(unConfiguredRequest);
865 
866         try {
867             // Test: CameraCaptureSession capture should throw IAE for null request.
868             mSession.capture(/*request*/null, /*listener*/null, mHandler);
869             mCollector.addMessage(
870                     "Session capture should throw IllegalArgumentException for null request");
871         } catch (IllegalArgumentException e) {
872             // Pass.
873         }
874 
875         try {
876             // Test: CameraCaptureSession capture should throw IAE for request
877             // without surface configured.
878             mSession.capture(unConfiguredRequest, /*listener*/null, mHandler);
879             mCollector.addMessage("Session capture should throw " +
880                     "IllegalArgumentException for request without surface configured");
881         } catch (IllegalArgumentException e) {
882             // Pass.
883         }
884 
885         try {
886             // Test: CameraCaptureSession setRepeatingRequest should throw IAE for null request.
887             mSession.setRepeatingRequest(/*request*/null, /*listener*/null, mHandler);
888             mCollector.addMessage("Session setRepeatingRequest should throw " +
889                     "IllegalArgumentException for null request");
890         } catch (IllegalArgumentException e) {
891             // Pass.
892         }
893 
894         try {
895             // Test: CameraCaptureSession setRepeatingRequest should throw IAE for for request
896             // without surface configured.
897             mSession.setRepeatingRequest(unConfiguredRequest, /*listener*/null, mHandler);
898             mCollector.addMessage("Capture zero burst should throw IllegalArgumentException " +
899                     "for request without surface configured");
900         } catch (IllegalArgumentException e) {
901             // Pass.
902         }
903 
904         try {
905             // Test: CameraCaptureSession captureBurst should throw IAE for null request list.
906             mSession.captureBurst(/*requests*/null, /*listener*/null, mHandler);
907             mCollector.addMessage("Session captureBurst should throw " +
908                     "IllegalArgumentException for null request list");
909         } catch (IllegalArgumentException e) {
910             // Pass.
911         }
912 
913         try {
914             // Test: CameraCaptureSession captureBurst should throw IAE for empty request list.
915             mSession.captureBurst(emptyRequests, /*listener*/null, mHandler);
916             mCollector.addMessage("Session captureBurst should throw " +
917                     " IllegalArgumentException for empty request list");
918         } catch (IllegalArgumentException e) {
919             // Pass.
920         }
921 
922         try {
923             // Test: CameraCaptureSession captureBurst should throw IAE for request
924             // without surface configured.
925             mSession.captureBurst(unConfiguredRequests, /*listener*/null, mHandler);
926             fail("Session captureBurst should throw IllegalArgumentException " +
927                     "for null request list");
928         } catch (IllegalArgumentException e) {
929             // Pass.
930         }
931 
932         try {
933             // Test: CameraCaptureSession setRepeatingBurst should throw IAE for null request list.
934             mSession.setRepeatingBurst(/*requests*/null, /*listener*/null, mHandler);
935             mCollector.addMessage("Session setRepeatingBurst should throw " +
936                     "IllegalArgumentException for null request list");
937         } catch (IllegalArgumentException e) {
938             // Pass.
939         }
940 
941         try {
942             // Test: CameraCaptureSession setRepeatingBurst should throw IAE for empty request list.
943             mSession.setRepeatingBurst(emptyRequests, /*listener*/null, mHandler);
944             mCollector.addMessage("Session setRepeatingBurst should throw " +
945                     "IllegalArgumentException for empty request list");
946         } catch (IllegalArgumentException e) {
947             // Pass.
948         }
949 
950         try {
951             // Test: CameraCaptureSession setRepeatingBurst should throw IAE for request
952             // without surface configured.
953             mSession.setRepeatingBurst(unConfiguredRequests, /*listener*/null, mHandler);
954             mCollector.addMessage("Session setRepeatingBurst should throw " +
955                     "IllegalArgumentException for request without surface configured");
956         } catch (IllegalArgumentException e) {
957             // Pass.
958         }
959     }
960 
961     private class IsCaptureResultNotEmpty
962             extends ArgumentMatcher<TotalCaptureResult> {
963         @Override
matches(Object obj)964         public boolean matches(Object obj) {
965             /**
966              * Do the simple verification here. Only verify the timestamp for now.
967              * TODO: verify more required capture result metadata fields.
968              */
969             TotalCaptureResult result = (TotalCaptureResult) obj;
970             Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
971             if (timeStamp != null && timeStamp.longValue() > 0L) {
972                 return true;
973             }
974             return false;
975         }
976     }
977 
978     /**
979      * Run capture test with different test configurations.
980      *
981      * @param burst If the test uses {@link CameraCaptureSession#captureBurst} or
982      * {@link CameraCaptureSession#setRepeatingBurst} to capture the burst.
983      * @param repeating If the test uses {@link CameraCaptureSession#setRepeatingBurst} or
984      * {@link CameraCaptureSession#setRepeatingRequest} for repeating capture.
985      * @param abort If the test uses {@link CameraCaptureSession#abortCaptures} to stop the
986      * repeating capture.  It has no effect if repeating is false.
987      */
runCaptureTest(boolean burst, boolean repeating, boolean abort)988     private void runCaptureTest(boolean burst, boolean repeating, boolean abort) throws Exception {
989         for (int i = 0; i < mCameraIds.length; i++) {
990             try {
991                 openDevice(mCameraIds[i], mCameraMockListener);
992                 waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
993 
994                 prepareCapture();
995 
996                 if (!burst) {
997                     // Test: that a single capture of each template type succeeds.
998                     for (int j = 0; j < sTemplates.length; j++) {
999                         // Skip video snapshots for LEGACY mode
1000                         if (mStaticInfo.isHardwareLevelLegacy() &&
1001                                 sTemplates[j] == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
1002                             continue;
1003                         }
1004                         // Skip non-PREVIEW templates for non-color output
1005                         if (!mStaticInfo.isColorOutputSupported() &&
1006                                 sTemplates[j] != CameraDevice.TEMPLATE_PREVIEW) {
1007                             continue;
1008                         }
1009                         captureSingleShot(mCameraIds[i], sTemplates[j], repeating, abort);
1010                     }
1011                 }
1012                 else {
1013                     // Test: burst of one shot
1014                     captureBurstShot(mCameraIds[i], sTemplates, 1, repeating, abort);
1015 
1016                     int template = mStaticInfo.isColorOutputSupported() ?
1017                         CameraDevice.TEMPLATE_STILL_CAPTURE :
1018                         CameraDevice.TEMPLATE_PREVIEW;
1019                     int[] templates = new int[] {
1020                         template,
1021                         template,
1022                         template,
1023                         template,
1024                         template
1025                     };
1026 
1027                     // Test: burst of 5 shots of the same template type
1028                     captureBurstShot(mCameraIds[i], templates, templates.length, repeating, abort);
1029 
1030                     // Test: burst of 5 shots of different template types
1031                     captureBurstShot(
1032                             mCameraIds[i], sTemplates, sTemplates.length, repeating, abort);
1033                 }
1034                 verify(mCameraMockListener, never())
1035                         .onError(
1036                                 any(CameraDevice.class),
1037                                 anyInt());
1038             } catch (Exception e) {
1039                 mCollector.addError(e);
1040             } finally {
1041                 try {
1042                     closeSession();
1043                 } catch (Exception e) {
1044                     mCollector.addError(e);
1045                 }finally {
1046                     closeDevice(mCameraIds[i], mCameraMockListener);
1047                 }
1048             }
1049         }
1050     }
1051 
captureSingleShot( String id, int template, boolean repeating, boolean abort)1052     private void captureSingleShot(
1053             String id,
1054             int template,
1055             boolean repeating, boolean abort) throws Exception {
1056 
1057         assertEquals("Bad initial state for preparing to capture",
1058                 mLatestSessionState, SESSION_READY);
1059 
1060         CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(template);
1061         assertNotNull("Failed to create capture request", requestBuilder);
1062         requestBuilder.addTarget(mReaderSurface);
1063         CameraCaptureSession.CaptureCallback mockCaptureCallback =
1064                 mock(CameraCaptureSession.CaptureCallback.class);
1065 
1066         if (VERBOSE) {
1067             Log.v(TAG, String.format("Capturing shot for device %s, template %d",
1068                     id, template));
1069         }
1070 
1071         startCapture(requestBuilder.build(), repeating, mockCaptureCallback, mHandler);
1072         waitForSessionState(SESSION_ACTIVE, SESSION_ACTIVE_TIMEOUT_MS);
1073 
1074         int expectedCaptureResultCount = repeating ? REPEATING_CAPTURE_EXPECTED_RESULT_COUNT : 1;
1075         verifyCaptureResults(mockCaptureCallback, expectedCaptureResultCount);
1076 
1077         if (repeating) {
1078             if (abort) {
1079                 mSession.abortCaptures();
1080             } else {
1081                 mSession.stopRepeating();
1082             }
1083         }
1084         waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
1085     }
1086 
captureBurstShot( String id, int[] templates, int len, boolean repeating, boolean abort)1087     private void captureBurstShot(
1088             String id,
1089             int[] templates,
1090             int len,
1091             boolean repeating,
1092             boolean abort) throws Exception {
1093 
1094         assertEquals("Bad initial state for preparing to capture",
1095                 mLatestSessionState, SESSION_READY);
1096 
1097         assertTrue("Invalid args to capture function", len <= templates.length);
1098         List<CaptureRequest> requests = new ArrayList<CaptureRequest>();
1099         for (int i = 0; i < len; i++) {
1100             // Skip video snapshots for LEGACY mode
1101             if (mStaticInfo.isHardwareLevelLegacy() &&
1102                     templates[i] == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
1103                 continue;
1104             }
1105             // Skip non-PREVIEW templates for non-color outpu
1106             if (!mStaticInfo.isColorOutputSupported() &&
1107                     templates[i] != CameraDevice.TEMPLATE_PREVIEW) {
1108                 continue;
1109             }
1110             CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(templates[i]);
1111             assertNotNull("Failed to create capture request", requestBuilder);
1112             requestBuilder.addTarget(mReaderSurface);
1113             requests.add(requestBuilder.build());
1114         }
1115         CameraCaptureSession.CaptureCallback mockCaptureCallback =
1116                 mock(CameraCaptureSession.CaptureCallback.class);
1117 
1118         if (VERBOSE) {
1119             Log.v(TAG, String.format("Capturing burst shot for device %s", id));
1120         }
1121 
1122         if (!repeating) {
1123             mSession.captureBurst(requests, mockCaptureCallback, mHandler);
1124         }
1125         else {
1126             mSession.setRepeatingBurst(requests, mockCaptureCallback, mHandler);
1127         }
1128         waitForSessionState(SESSION_ACTIVE, SESSION_READY_TIMEOUT_MS);
1129 
1130         int expectedResultCount = requests.size();
1131         if (repeating) {
1132             expectedResultCount *= REPEATING_CAPTURE_EXPECTED_RESULT_COUNT;
1133         }
1134 
1135         verifyCaptureResults(mockCaptureCallback, expectedResultCount);
1136 
1137         if (repeating) {
1138             if (abort) {
1139                 mSession.abortCaptures();
1140             } else {
1141                 mSession.stopRepeating();
1142             }
1143         }
1144         waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
1145     }
1146 
1147     /**
1148      * Precondition: Device must be in known OPENED state (has been waited for).
1149      *
1150      * <p>Creates a new capture session and waits until it is in the {@code SESSION_READY} state.
1151      * </p>
1152      *
1153      * <p>Any existing capture session will be closed as a result of calling this.</p>
1154      * */
prepareCapture()1155     private void prepareCapture() throws Exception {
1156         if (VERBOSE) Log.v(TAG, "prepareCapture");
1157 
1158         assertTrue("Bad initial state for preparing to capture",
1159                 mLatestDeviceState == STATE_OPENED);
1160 
1161         if (mSession != null) {
1162             if (VERBOSE) Log.v(TAG, "prepareCapture - closing existing session");
1163             closeSession();
1164         }
1165 
1166         // Create a new session listener each time, it's not reusable across cameras
1167         mSessionMockListener = spy(new BlockingSessionCallback());
1168         mSessionWaiter = mSessionMockListener.getStateWaiter();
1169 
1170         if (!mStaticInfo.isColorOutputSupported()) {
1171             createDefaultImageReader(getMaxDepthSize(mCamera.getId(), mCameraManager),
1172                     ImageFormat.DEPTH16, MAX_NUM_IMAGES, new ImageDropperListener());
1173         }
1174 
1175         List<Surface> outputSurfaces = new ArrayList<>(Arrays.asList(mReaderSurface));
1176         mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
1177 
1178         mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
1179         waitForSessionState(SESSION_CONFIGURED, SESSION_CONFIGURE_TIMEOUT_MS);
1180         waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
1181     }
1182 
waitForDeviceState(int state, long timeoutMs)1183     private void waitForDeviceState(int state, long timeoutMs) {
1184         mCameraMockListener.waitForState(state, timeoutMs);
1185         mLatestDeviceState = state;
1186     }
1187 
waitForSessionState(int state, long timeoutMs)1188     private void waitForSessionState(int state, long timeoutMs) {
1189         mSessionWaiter.waitForState(state, timeoutMs);
1190         mLatestSessionState = state;
1191     }
1192 
verifyCaptureResults( CameraCaptureSession.CaptureCallback mockListener, int expectResultCount)1193     private void verifyCaptureResults(
1194             CameraCaptureSession.CaptureCallback mockListener,
1195             int expectResultCount) {
1196         final int TIMEOUT_PER_RESULT_MS = 2000;
1197         // Should receive expected number of capture results.
1198         verify(mockListener,
1199                 timeout(TIMEOUT_PER_RESULT_MS * expectResultCount).atLeast(expectResultCount))
1200                         .onCaptureCompleted(
1201                                 eq(mSession),
1202                                 isA(CaptureRequest.class),
1203                                 argThat(new IsCaptureResultNotEmpty()));
1204         // Should not receive any capture failed callbacks.
1205         verify(mockListener, never())
1206                         .onCaptureFailed(
1207                                 eq(mSession),
1208                                 isA(CaptureRequest.class),
1209                                 isA(CaptureFailure.class));
1210         // Should receive expected number of capture shutter calls
1211         verify(mockListener,
1212                 atLeast(expectResultCount))
1213                         .onCaptureStarted(
1214                                eq(mSession),
1215                                isA(CaptureRequest.class),
1216                                anyLong(),
1217                                anyLong());
1218     }
1219 
checkFpsRange(CaptureRequest.Builder request, int template, CameraCharacteristics props)1220     private void checkFpsRange(CaptureRequest.Builder request, int template,
1221             CameraCharacteristics props) {
1222         CaptureRequest.Key<Range<Integer>> fpsRangeKey = CONTROL_AE_TARGET_FPS_RANGE;
1223         Range<Integer> fpsRange;
1224         if ((fpsRange = mCollector.expectKeyValueNotNull(request, fpsRangeKey)) == null) {
1225             return;
1226         }
1227 
1228         int minFps = fpsRange.getLower();
1229         int maxFps = fpsRange.getUpper();
1230         Range<Integer>[] availableFpsRange = props
1231                 .get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
1232         boolean foundRange = false;
1233         for (int i = 0; i < availableFpsRange.length; i += 1) {
1234             if (minFps == availableFpsRange[i].getLower()
1235                     && maxFps == availableFpsRange[i].getUpper()) {
1236                 foundRange = true;
1237                 break;
1238             }
1239         }
1240         if (!foundRange) {
1241             mCollector.addMessage(String.format("Unable to find the fps range (%d, %d)",
1242                     minFps, maxFps));
1243             return;
1244         }
1245 
1246 
1247         if (template != CameraDevice.TEMPLATE_MANUAL &&
1248                 template != CameraDevice.TEMPLATE_STILL_CAPTURE) {
1249             if (maxFps < MIN_FPS_REQUIRED_FOR_STREAMING) {
1250                 mCollector.addMessage("Max fps should be at least "
1251                         + MIN_FPS_REQUIRED_FOR_STREAMING);
1252                 return;
1253             }
1254 
1255             // Relax framerate constraints on legacy mode
1256             if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
1257                 // Need give fixed frame rate for video recording template.
1258                 if (template == CameraDevice.TEMPLATE_RECORD) {
1259                     if (maxFps != minFps) {
1260                         mCollector.addMessage("Video recording frame rate should be fixed");
1261                     }
1262                 }
1263             }
1264         }
1265     }
1266 
checkAfMode(CaptureRequest.Builder request, int template, CameraCharacteristics props)1267     private void checkAfMode(CaptureRequest.Builder request, int template,
1268             CameraCharacteristics props) {
1269         boolean hasFocuser = props.getKeys().contains(CameraCharacteristics.
1270                 LENS_INFO_MINIMUM_FOCUS_DISTANCE) &&
1271                 (props.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE) > 0f);
1272 
1273         if (!hasFocuser) {
1274             return;
1275         }
1276 
1277         int targetAfMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
1278         int[] availableAfMode = props.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
1279         if (template == CameraDevice.TEMPLATE_PREVIEW ||
1280                 template == CameraDevice.TEMPLATE_STILL_CAPTURE ||
1281                 template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG) {
1282             // Default to CONTINUOUS_PICTURE if it is available, otherwise AUTO.
1283             for (int i = 0; i < availableAfMode.length; i++) {
1284                 if (availableAfMode[i] == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
1285                     targetAfMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
1286                     break;
1287                 }
1288             }
1289         } else if (template == CameraDevice.TEMPLATE_RECORD ||
1290                 template == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
1291             // Default to CONTINUOUS_VIDEO if it is available, otherwise AUTO.
1292             for (int i = 0; i < availableAfMode.length; i++) {
1293                 if (availableAfMode[i] == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO) {
1294                     targetAfMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
1295                     break;
1296                 }
1297             }
1298         } else if (template == CameraDevice.TEMPLATE_MANUAL) {
1299             targetAfMode = CaptureRequest.CONTROL_AF_MODE_OFF;
1300         }
1301 
1302         mCollector.expectKeyValueEquals(request, CONTROL_AF_MODE, targetAfMode);
1303         if (mStaticInfo.areKeysAvailable(CaptureRequest.LENS_FOCUS_DISTANCE)) {
1304             mCollector.expectKeyValueNotNull(request, LENS_FOCUS_DISTANCE);
1305         }
1306     }
1307 
checkAntiBandingMode(CaptureRequest.Builder request, int template)1308     private void checkAntiBandingMode(CaptureRequest.Builder request, int template) {
1309         if (template == CameraDevice.TEMPLATE_MANUAL) {
1310             return;
1311         }
1312 
1313         if (!mStaticInfo.isColorOutputSupported()) return;
1314 
1315         List<Integer> availableAntiBandingModes =
1316                 Arrays.asList(toObject(mStaticInfo.getAeAvailableAntiBandingModesChecked()));
1317 
1318         if (availableAntiBandingModes.contains(CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO)) {
1319             mCollector.expectKeyValueEquals(request, CONTROL_AE_ANTIBANDING_MODE,
1320                     CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
1321         } else {
1322             mCollector.expectKeyValueIsIn(request, CONTROL_AE_ANTIBANDING_MODE,
1323                     CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ,
1324                     CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ);
1325         }
1326     }
1327 
1328     /**
1329      * <p>Check if 3A metering settings are "up to HAL" in request template</p>
1330      *
1331      * <p>This function doesn't fail the test immediately, it updates the
1332      * test pass/fail status and appends the failure message to the error collector each key.</p>
1333      *
1334      * @param regions The metering rectangles to be checked
1335      */
checkMeteringRect(MeteringRectangle[] regions)1336     private void checkMeteringRect(MeteringRectangle[] regions) {
1337         if (regions == null) {
1338             return;
1339         }
1340         mCollector.expectNotEquals("Number of metering region should not be 0", 0, regions.length);
1341         for (int i = 0; i < regions.length; i++) {
1342             mCollector.expectEquals("Default metering regions should have all zero weight",
1343                     0, regions[i].getMeteringWeight());
1344         }
1345     }
1346 
1347     /**
1348      * <p>Check if the request settings are suitable for a given request template.</p>
1349      *
1350      * <p>This function doesn't fail the test immediately, it updates the
1351      * test pass/fail status and appends the failure message to the error collector each key.</p>
1352      *
1353      * @param request The request to be checked.
1354      * @param template The capture template targeted by this request.
1355      * @param props The CameraCharacteristics this request is checked against with.
1356      */
checkRequestForTemplate(CaptureRequest.Builder request, int template, CameraCharacteristics props)1357     private void checkRequestForTemplate(CaptureRequest.Builder request, int template,
1358             CameraCharacteristics props) {
1359         // 3A settings--control.mode.
1360         if (template != CameraDevice.TEMPLATE_MANUAL) {
1361             mCollector.expectKeyValueEquals(request, CONTROL_MODE,
1362                     CaptureRequest.CONTROL_MODE_AUTO);
1363         }
1364 
1365         // 3A settings--AE/AWB/AF.
1366         Integer maxRegionsAeVal = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
1367         int maxRegionsAe = maxRegionsAeVal != null ? maxRegionsAeVal : 0;
1368         Integer maxRegionsAwbVal = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
1369         int maxRegionsAwb = maxRegionsAwbVal != null ? maxRegionsAwbVal : 0;
1370         Integer maxRegionsAfVal = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
1371         int maxRegionsAf = maxRegionsAfVal != null ? maxRegionsAfVal : 0;
1372 
1373         checkFpsRange(request, template, props);
1374 
1375         checkAfMode(request, template, props);
1376         checkAntiBandingMode(request, template);
1377 
1378         if (template == CameraDevice.TEMPLATE_MANUAL) {
1379             mCollector.expectKeyValueEquals(request, CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
1380             mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
1381                     CaptureRequest.CONTROL_AE_MODE_OFF);
1382             mCollector.expectKeyValueEquals(request, CONTROL_AWB_MODE,
1383                     CaptureRequest.CONTROL_AWB_MODE_OFF);
1384         } else {
1385             if (mStaticInfo.isColorOutputSupported()) {
1386                 mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
1387                         CaptureRequest.CONTROL_AE_MODE_ON);
1388                 mCollector.expectKeyValueEquals(request, CONTROL_AE_EXPOSURE_COMPENSATION, 0);
1389                 mCollector.expectKeyValueEquals(request, CONTROL_AE_PRECAPTURE_TRIGGER,
1390                         CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
1391                 // if AE lock is not supported, expect the control key to be non-exist or false
1392                 if (mStaticInfo.isAeLockSupported() || request.get(CONTROL_AE_LOCK) != null) {
1393                     mCollector.expectKeyValueEquals(request, CONTROL_AE_LOCK, false);
1394                 }
1395 
1396                 mCollector.expectKeyValueEquals(request, CONTROL_AF_TRIGGER,
1397                         CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
1398 
1399                 mCollector.expectKeyValueEquals(request, CONTROL_AWB_MODE,
1400                         CaptureRequest.CONTROL_AWB_MODE_AUTO);
1401                 // if AWB lock is not supported, expect the control key to be non-exist or false
1402                 if (mStaticInfo.isAwbLockSupported() || request.get(CONTROL_AWB_LOCK) != null) {
1403                     mCollector.expectKeyValueEquals(request, CONTROL_AWB_LOCK, false);
1404                 }
1405 
1406                 // Check 3A regions.
1407                 if (VERBOSE) {
1408                     Log.v(TAG, String.format("maxRegions is: {AE: %s, AWB: %s, AF: %s}",
1409                                     maxRegionsAe, maxRegionsAwb, maxRegionsAf));
1410                 }
1411                 if (maxRegionsAe > 0) {
1412                     mCollector.expectKeyValueNotNull(request, CONTROL_AE_REGIONS);
1413                     MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
1414                     checkMeteringRect(aeRegions);
1415                 }
1416                 if (maxRegionsAwb > 0) {
1417                     mCollector.expectKeyValueNotNull(request, CONTROL_AWB_REGIONS);
1418                     MeteringRectangle[] awbRegions = request.get(CONTROL_AWB_REGIONS);
1419                     checkMeteringRect(awbRegions);
1420                 }
1421                 if (maxRegionsAf > 0) {
1422                     mCollector.expectKeyValueNotNull(request, CONTROL_AF_REGIONS);
1423                     MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
1424                     checkMeteringRect(afRegions);
1425                 }
1426             }
1427         }
1428 
1429         // Sensor settings.
1430 
1431         mCollector.expectEquals("Lens aperture must be present in request if available apertures " +
1432                         "are present in metadata, and vice-versa.",
1433                 mStaticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES),
1434                 mStaticInfo.areKeysAvailable(CaptureRequest.LENS_APERTURE));
1435         if (mStaticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)) {
1436             float[] availableApertures =
1437                     props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
1438             if (availableApertures.length > 1) {
1439                 mCollector.expectKeyValueNotNull(request, LENS_APERTURE);
1440             }
1441         }
1442 
1443         mCollector.expectEquals("Lens filter density must be present in request if available " +
1444                         "filter densities are present in metadata, and vice-versa.",
1445                 mStaticInfo.areKeysAvailable(CameraCharacteristics.
1446                         LENS_INFO_AVAILABLE_FILTER_DENSITIES),
1447                 mStaticInfo.areKeysAvailable(CaptureRequest.LENS_FILTER_DENSITY));
1448         if (mStaticInfo.areKeysAvailable(CameraCharacteristics.
1449                 LENS_INFO_AVAILABLE_FILTER_DENSITIES)) {
1450             float[] availableFilters =
1451                     props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES);
1452             if (availableFilters.length > 1) {
1453                 mCollector.expectKeyValueNotNull(request, LENS_FILTER_DENSITY);
1454             }
1455         }
1456 
1457         float[] availableFocalLen =
1458                 props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
1459         if (availableFocalLen.length > 1) {
1460             mCollector.expectKeyValueNotNull(request, LENS_FOCAL_LENGTH);
1461         }
1462 
1463         mCollector.expectEquals("Lens optical stabilization must be present in request if " +
1464                         "available optical stabilizations are present in metadata, and vice-versa.",
1465                 mStaticInfo.areKeysAvailable(CameraCharacteristics.
1466                         LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION),
1467                 mStaticInfo.areKeysAvailable(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE));
1468         if (mStaticInfo.areKeysAvailable(CameraCharacteristics.
1469                 LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION)) {
1470             int[] availableOIS =
1471                     props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION);
1472             if (availableOIS.length > 1) {
1473                 mCollector.expectKeyValueNotNull(request, LENS_OPTICAL_STABILIZATION_MODE);
1474             }
1475         }
1476 
1477         if (mStaticInfo.areKeysAvailable(BLACK_LEVEL_LOCK)) {
1478             mCollector.expectKeyValueEquals(request, BLACK_LEVEL_LOCK, false);
1479         }
1480 
1481         if (mStaticInfo.areKeysAvailable(SENSOR_FRAME_DURATION)) {
1482             mCollector.expectKeyValueNotNull(request, SENSOR_FRAME_DURATION);
1483         }
1484 
1485         if (mStaticInfo.areKeysAvailable(SENSOR_EXPOSURE_TIME)) {
1486             mCollector.expectKeyValueNotNull(request, SENSOR_EXPOSURE_TIME);
1487         }
1488 
1489         if (mStaticInfo.areKeysAvailable(SENSOR_SENSITIVITY)) {
1490             mCollector.expectKeyValueNotNull(request, SENSOR_SENSITIVITY);
1491         }
1492 
1493         // ISP-processing settings.
1494         if (mStaticInfo.isColorOutputSupported()) {
1495             mCollector.expectKeyValueEquals(
1496                     request, STATISTICS_FACE_DETECT_MODE,
1497                     CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF);
1498             mCollector.expectKeyValueEquals(request, FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
1499         }
1500 
1501         List<Integer> availableCaps = mStaticInfo.getAvailableCapabilitiesChecked();
1502         if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE)) {
1503             // If the device doesn't support RAW, all template should have OFF as default.
1504             if (!availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1505                 mCollector.expectKeyValueEquals(
1506                         request, STATISTICS_LENS_SHADING_MAP_MODE,
1507                         CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_OFF);
1508             }
1509         }
1510 
1511         // Edge enhancement and noise reduction modes
1512         boolean supportReprocessing =
1513                 availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING) ||
1514                 availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1515 
1516         if (template == CameraDevice.TEMPLATE_STILL_CAPTURE) {
1517             // Not enforce high quality here, as some devices may not effectively have high quality
1518             // mode.
1519             if (mStaticInfo.areKeysAvailable(COLOR_CORRECTION_MODE)) {
1520                 mCollector.expectKeyValueNotEquals(
1521                         request, COLOR_CORRECTION_MODE,
1522                         CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
1523             }
1524 
1525             mCollector.expectEquals("Edge mode must be present in request if " +
1526                             "available edge modes are present in metadata, and vice-versa.",
1527                     mStaticInfo.areKeysAvailable(CameraCharacteristics.
1528                             EDGE_AVAILABLE_EDGE_MODES),
1529                     mStaticInfo.areKeysAvailable(CaptureRequest.EDGE_MODE));
1530             if (mStaticInfo.areKeysAvailable(EDGE_MODE)) {
1531                 List<Integer> availableEdgeModes =
1532                         Arrays.asList(toObject(mStaticInfo.getAvailableEdgeModesChecked()));
1533                 if (availableEdgeModes.contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY)) {
1534                     mCollector.expectKeyValueEquals(request, EDGE_MODE,
1535                             CaptureRequest.EDGE_MODE_HIGH_QUALITY);
1536                 } else if (availableEdgeModes.contains(CaptureRequest.EDGE_MODE_FAST)) {
1537                     mCollector.expectKeyValueEquals(request, EDGE_MODE,
1538                             CaptureRequest.EDGE_MODE_FAST);
1539                 } else {
1540                     mCollector.expectKeyValueEquals(request, EDGE_MODE,
1541                             CaptureRequest.EDGE_MODE_OFF);
1542                 }
1543             }
1544 
1545             mCollector.expectEquals("Noise reduction mode must be present in request if " +
1546                             "available noise reductions are present in metadata, and vice-versa.",
1547                     mStaticInfo.areKeysAvailable(CameraCharacteristics.
1548                             NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES),
1549                     mStaticInfo.areKeysAvailable(CaptureRequest.NOISE_REDUCTION_MODE));
1550             if (mStaticInfo.areKeysAvailable(
1551                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES)) {
1552                 List<Integer> availableNoiseReductionModes =
1553                         Arrays.asList(toObject(mStaticInfo.getAvailableNoiseReductionModesChecked()));
1554                 if (availableNoiseReductionModes
1555                         .contains(CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY)) {
1556                     mCollector.expectKeyValueEquals(
1557                             request, NOISE_REDUCTION_MODE,
1558                             CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
1559                 } else if (availableNoiseReductionModes
1560                         .contains(CaptureRequest.NOISE_REDUCTION_MODE_FAST)) {
1561                     mCollector.expectKeyValueEquals(
1562                             request, NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_FAST);
1563                 } else {
1564                     mCollector.expectKeyValueEquals(
1565                             request, NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
1566                 }
1567             }
1568         } else if (template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG && supportReprocessing) {
1569             mCollector.expectKeyValueEquals(request, EDGE_MODE,
1570                     CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
1571             mCollector.expectKeyValueEquals(request, NOISE_REDUCTION_MODE,
1572                     CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
1573         } else {
1574             if (mStaticInfo.areKeysAvailable(EDGE_MODE)) {
1575                 mCollector.expectKeyValueNotNull(request, EDGE_MODE);
1576             }
1577 
1578             if (mStaticInfo.areKeysAvailable(NOISE_REDUCTION_MODE)) {
1579                 mCollector.expectKeyValueNotNull(request, NOISE_REDUCTION_MODE);
1580             }
1581         }
1582 
1583         // Tone map and lens shading modes.
1584         if (template == CameraDevice.TEMPLATE_STILL_CAPTURE) {
1585             mCollector.expectEquals("Tonemap mode must be present in request if " +
1586                             "available tonemap modes are present in metadata, and vice-versa.",
1587                     mStaticInfo.areKeysAvailable(CameraCharacteristics.
1588                             TONEMAP_AVAILABLE_TONE_MAP_MODES),
1589                     mStaticInfo.areKeysAvailable(CaptureRequest.TONEMAP_MODE));
1590             if (mStaticInfo.areKeysAvailable(
1591                     CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES)) {
1592                 List<Integer> availableToneMapModes =
1593                         Arrays.asList(toObject(mStaticInfo.getAvailableToneMapModesChecked()));
1594                 if (availableToneMapModes.contains(CaptureRequest.TONEMAP_MODE_HIGH_QUALITY)) {
1595                     mCollector.expectKeyValueEquals(request, TONEMAP_MODE,
1596                             CaptureRequest.TONEMAP_MODE_HIGH_QUALITY);
1597                 } else {
1598                     mCollector.expectKeyValueEquals(request, TONEMAP_MODE,
1599                             CaptureRequest.TONEMAP_MODE_FAST);
1600                 }
1601             }
1602 
1603             // Still capture template should have android.statistics.lensShadingMapMode ON when
1604             // RAW capability is supported.
1605             if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE) &&
1606                     availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1607                     mCollector.expectKeyValueEquals(request, STATISTICS_LENS_SHADING_MAP_MODE,
1608                             STATISTICS_LENS_SHADING_MAP_MODE_ON);
1609             }
1610         } else {
1611             if (mStaticInfo.areKeysAvailable(TONEMAP_MODE)) {
1612                 mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
1613                         CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE);
1614                 mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
1615                         CaptureRequest.TONEMAP_MODE_GAMMA_VALUE);
1616                 mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
1617                         CaptureRequest.TONEMAP_MODE_PRESET_CURVE);
1618             }
1619             if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE)) {
1620                 mCollector.expectKeyValueNotNull(request, STATISTICS_LENS_SHADING_MAP_MODE);
1621             }
1622         }
1623 
1624         mCollector.expectKeyValueEquals(request, CONTROL_CAPTURE_INTENT, template);
1625 
1626         // TODO: use the list of keys from CameraCharacteristics to avoid expecting
1627         //       keys which are not available by this CameraDevice.
1628     }
1629 
captureTemplateTestByCamera(String cameraId, int template)1630     private void captureTemplateTestByCamera(String cameraId, int template) throws Exception {
1631         try {
1632             openDevice(cameraId, mCameraMockListener);
1633 
1634             assertTrue("Camera template " + template + " is out of range!",
1635                     template >= CameraDevice.TEMPLATE_PREVIEW
1636                             && template <= CameraDevice.TEMPLATE_MANUAL);
1637 
1638             mCollector.setCameraId(cameraId);
1639 
1640             try {
1641                 CaptureRequest.Builder request = mCamera.createCaptureRequest(template);
1642                 assertNotNull("Failed to create capture request for template " + template, request);
1643 
1644                 CameraCharacteristics props = mStaticInfo.getCharacteristics();
1645                 checkRequestForTemplate(request, template, props);
1646             } catch (IllegalArgumentException e) {
1647                 if (template == CameraDevice.TEMPLATE_MANUAL &&
1648                         !mStaticInfo.isCapabilitySupported(CameraCharacteristics.
1649                                 REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1650                     // OK
1651                 } else if (template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG &&
1652                         !mStaticInfo.isCapabilitySupported(CameraCharacteristics.
1653                                 REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING)) {
1654                     // OK.
1655                 } else if (sLegacySkipTemplates.contains(template) &&
1656                         mStaticInfo.isHardwareLevelLegacy()) {
1657                     // OK
1658                 } else if (template != CameraDevice.TEMPLATE_PREVIEW &&
1659                         mStaticInfo.isDepthOutputSupported() &&
1660                         !mStaticInfo.isColorOutputSupported()) {
1661                     // OK, depth-only devices need only support PREVIEW template
1662                 } else {
1663                     throw e; // rethrow
1664                 }
1665             }
1666         }
1667         finally {
1668             try {
1669                 closeSession();
1670             } finally {
1671                 closeDevice(cameraId, mCameraMockListener);
1672             }
1673         }
1674     }
1675 
1676     /**
1677      * Start capture with given {@link #CaptureRequest}.
1678      *
1679      * @param request The {@link #CaptureRequest} to be captured.
1680      * @param repeating If the capture is single capture or repeating.
1681      * @param listener The {@link #CaptureCallback} camera device used to notify callbacks.
1682      * @param handler The handler camera device used to post callbacks.
1683      */
startCapture(CaptureRequest request, boolean repeating, CameraCaptureSession.CaptureCallback listener, Handler handler)1684     protected void startCapture(CaptureRequest request, boolean repeating,
1685             CameraCaptureSession.CaptureCallback listener, Handler handler)
1686                     throws CameraAccessException {
1687         if (VERBOSE) Log.v(TAG, "Starting capture from session");
1688 
1689         if (repeating) {
1690             mSession.setRepeatingRequest(request, listener, handler);
1691         } else {
1692             mSession.capture(request, listener, handler);
1693         }
1694     }
1695 
1696     /**
1697      * Close a {@link #CameraCaptureSession capture session}; blocking until
1698      * the close finishes with a transition to {@link CameraCaptureSession.StateCallback#onClosed}.
1699      */
closeSession()1700     protected void closeSession() {
1701         if (mSession == null) {
1702             return;
1703         }
1704 
1705         mSession.close();
1706         waitForSessionState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
1707         mSession = null;
1708 
1709         mSessionMockListener = null;
1710         mSessionWaiter = null;
1711     }
1712 
1713     /**
1714      * A camera capture session listener that keeps all the configured and closed sessions.
1715      */
1716     private class MultipleSessionCallback extends CameraCaptureSession.StateCallback {
1717         public static final int SESSION_CONFIGURED = 0;
1718         public static final int SESSION_CLOSED = 1;
1719 
1720         final List<CameraCaptureSession> mSessions = new ArrayList<>();
1721         final Map<CameraCaptureSession, Integer> mSessionStates = new HashMap<>();
1722         CameraCaptureSession mCurrentConfiguredSession = null;
1723 
1724         final ReentrantLock mLock = new ReentrantLock();
1725         final Condition mNewStateCond = mLock.newCondition();
1726 
1727         final boolean mFailOnConfigureFailed;
1728 
1729         /**
1730          * If failOnConfigureFailed is true, it calls fail() when onConfigureFailed() is invoked
1731          * for any session.
1732          */
MultipleSessionCallback(boolean failOnConfigureFailed)1733         public MultipleSessionCallback(boolean failOnConfigureFailed) {
1734             mFailOnConfigureFailed = failOnConfigureFailed;
1735         }
1736 
1737         @Override
onClosed(CameraCaptureSession session)1738         public void onClosed(CameraCaptureSession session) {
1739             mLock.lock();
1740             mSessionStates.put(session, SESSION_CLOSED);
1741             mNewStateCond.signal();
1742             mLock.unlock();
1743         }
1744 
1745         @Override
onConfigured(CameraCaptureSession session)1746         public void onConfigured(CameraCaptureSession session) {
1747             mLock.lock();
1748             mSessions.add(session);
1749             mSessionStates.put(session, SESSION_CONFIGURED);
1750             mNewStateCond.signal();
1751             mLock.unlock();
1752         }
1753 
1754         @Override
onConfigureFailed(CameraCaptureSession session)1755         public void onConfigureFailed(CameraCaptureSession session) {
1756             if (mFailOnConfigureFailed) {
1757                 fail("Configuring a session failed");
1758             }
1759         }
1760 
1761         /**
1762          * Get a number of sessions that have been configured.
1763          */
getAllSessions(int numSessions, int timeoutMs)1764         public List<CameraCaptureSession> getAllSessions(int numSessions, int timeoutMs)
1765                 throws Exception {
1766             long remainingTime = timeoutMs;
1767             mLock.lock();
1768             try {
1769                 while (mSessions.size() < numSessions) {
1770                     long startTime = SystemClock.elapsedRealtime();
1771                     boolean ret = mNewStateCond.await(remainingTime, TimeUnit.MILLISECONDS);
1772                     remainingTime -= (SystemClock.elapsedRealtime() - startTime);
1773                     ret &= remainingTime > 0;
1774 
1775                     assertTrue("Get " + numSessions + " sessions timed out after " + timeoutMs +
1776                             "ms", ret);
1777                 }
1778 
1779                 return mSessions;
1780             } finally {
1781                 mLock.unlock();
1782             }
1783         }
1784 
1785         /**
1786          * Wait until a previously-configured sessoin is closed or it times out.
1787          */
waitForSessionClose(CameraCaptureSession session, int timeoutMs)1788         public void waitForSessionClose(CameraCaptureSession session, int timeoutMs) throws Exception {
1789             long remainingTime = timeoutMs;
1790             mLock.lock();
1791             try {
1792                 while (mSessionStates.get(session).equals(SESSION_CLOSED) == false) {
1793                     long startTime = SystemClock.elapsedRealtime();
1794                     boolean ret = mNewStateCond.await(remainingTime, TimeUnit.MILLISECONDS);
1795                     remainingTime -= (SystemClock.elapsedRealtime() - startTime);
1796                     ret &= remainingTime > 0;
1797 
1798                     assertTrue("Wait for session close timed out after " + timeoutMs + "ms", ret);
1799                 }
1800             } finally {
1801                 mLock.unlock();
1802             }
1803         }
1804     }
1805 }
1806