1 /*
2  * Copyright 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;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.*;
20 import static android.hardware.camera2.CameraCharacteristics.*;
21 
22 import android.graphics.Point;
23 import android.graphics.PointF;
24 import android.graphics.Rect;
25 import android.hardware.camera2.CameraCharacteristics;
26 import android.hardware.camera2.CameraDevice;
27 import android.hardware.camera2.CameraMetadata;
28 import android.hardware.camera2.CaptureRequest;
29 import android.hardware.camera2.CaptureResult;
30 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
31 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
32 import android.hardware.camera2.params.ColorSpaceTransform;
33 import android.hardware.camera2.params.Face;
34 import android.hardware.camera2.params.LensShadingMap;
35 import android.hardware.camera2.params.MeteringRectangle;
36 import android.hardware.camera2.params.RggbChannelVector;
37 import android.hardware.camera2.params.TonemapCurve;
38 
39 import android.util.Log;
40 import android.util.Range;
41 import android.util.Rational;
42 import android.util.Size;
43 
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.List;
47 
48 /**
49  * <p>
50  * Basic test for camera CaptureRequest key controls.
51  * </p>
52  * <p>
53  * Several test categories are covered: manual sensor control, 3A control,
54  * manual ISP control and other per-frame control and synchronization.
55  * </p>
56  */
57 public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
58     private static final String TAG = "CaptureRequestTest";
59     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
60     private static final int NUM_FRAMES_VERIFIED = 15;
61     private static final int NUM_FACE_DETECTION_FRAMES_VERIFIED = 60;
62     /** 30ms exposure time must be supported by full capability devices. */
63     private static final long DEFAULT_EXP_TIME_NS = 30000000L;
64     private static final int DEFAULT_SENSITIVITY = 100;
65     private static final int RGGB_COLOR_CHANNEL_COUNT = 4;
66     private static final int MAX_SHADING_MAP_SIZE = 64 * 64 * RGGB_COLOR_CHANNEL_COUNT;
67     private static final int MIN_SHADING_MAP_SIZE = 1 * 1 * RGGB_COLOR_CHANNEL_COUNT;
68     private static final long IGNORE_REQUESTED_EXPOSURE_TIME_CHECK = -1L;
69     private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
70     private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8333333L; // 8.3ms, Approximation.
71     private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
72     private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
73     private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
74     private static final int DEFAULT_NUM_SENSITIVITY_STEPS = 16;
75     private static final int DEFAULT_SENSITIVITY_STEP_SIZE = 100;
76     private static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
77     private static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
78     private static final int NUM_TEST_FOCUS_DISTANCES = 10;
79     // 5 percent error margin for calibrated device
80     private static final float FOCUS_DISTANCE_ERROR_PERCENT_CALIBRATED = 0.05f;
81     // 25 percent error margin for uncalibrated device
82     private static final float FOCUS_DISTANCE_ERROR_PERCENT_UNCALIBRATED = 0.25f;
83     // 10 percent error margin for approximate device
84     private static final float FOCUS_DISTANCE_ERROR_PERCENT_APPROXIMATE = 0.10f;
85     private static final int ANTI_FLICKERING_50HZ = 1;
86     private static final int ANTI_FLICKERING_60HZ = 2;
87 
88     // 5 percent error margin for resulting crop regions
89     private static final float CROP_REGION_ERROR_PERCENT_DELTA = 0.05f;
90     // 1 percent error margin for centering the crop region
91     private static final float CROP_REGION_ERROR_PERCENT_CENTERED = 0.01f;
92 
93     // Linear tone mapping curve example.
94     private static final float[] TONEMAP_CURVE_LINEAR = {0, 0, 1.0f, 1.0f};
95     // Standard sRGB tone mapping, per IEC 61966-2-1:1999, with 16 control points.
96     private static final float[] TONEMAP_CURVE_SRGB = {
97             0.0000f, 0.0000f, 0.0667f, 0.2864f, 0.1333f, 0.4007f, 0.2000f, 0.4845f,
98             0.2667f, 0.5532f, 0.3333f, 0.6125f, 0.4000f, 0.6652f, 0.4667f, 0.7130f,
99             0.5333f, 0.7569f, 0.6000f, 0.7977f, 0.6667f, 0.8360f, 0.7333f, 0.8721f,
100             0.8000f, 0.9063f, 0.8667f, 0.9389f, 0.9333f, 0.9701f, 1.0000f, 1.0000f
101     };
102     private final Rational ZERO_R = new Rational(0, 1);
103     private final Rational ONE_R = new Rational(1, 1);
104 
105     private final int NUM_ALGORITHMS = 3; // AE, AWB and AF
106     private final int INDEX_ALGORITHM_AE = 0;
107     private final int INDEX_ALGORITHM_AWB = 1;
108     private final int INDEX_ALGORITHM_AF = 2;
109 
110     private enum TorchSeqState {
111         RAMPING_UP,
112         FIRED,
113         RAMPING_DOWN
114     }
115 
116     @Override
setUp()117     protected void setUp() throws Exception {
118         super.setUp();
119     }
120 
121     @Override
tearDown()122     protected void tearDown() throws Exception {
123         super.tearDown();
124     }
125 
126     /**
127      * Test black level lock when exposure value change.
128      * <p>
129      * When {@link CaptureRequest#BLACK_LEVEL_LOCK} is true in a request, the
130      * camera device should lock the black level. When the exposure values are changed,
131      * the camera may require reset black level Since changes to certain capture
132      * parameters (such as exposure time) may require resetting of black level
133      * compensation. However, the black level must remain locked after exposure
134      * value changes (when requests have lock ON).
135      * </p>
136      */
testBlackLevelLock()137     public void testBlackLevelLock() throws Exception {
138         for (int i = 0; i < mCameraIds.length; i++) {
139             try {
140                 openDevice(mCameraIds[i]);
141 
142                 if (!mStaticInfo.isCapabilitySupported(
143                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
144                     continue;
145                 }
146 
147                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
148                 CaptureRequest.Builder requestBuilder =
149                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
150 
151                 // Start with default manual exposure time, with black level being locked.
152                 requestBuilder.set(CaptureRequest.BLACK_LEVEL_LOCK, true);
153                 changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
154 
155                 Size previewSz =
156                         getMaxPreviewSize(mCamera.getId(), mCameraManager,
157                         getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
158 
159                 startPreview(requestBuilder, previewSz, listener);
160                 waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
161                 // No lock OFF state is allowed as the exposure is not changed.
162                 verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/0);
163 
164                 // Double the exposure time and gain, with black level still being locked.
165                 changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS * 2, DEFAULT_SENSITIVITY * 2);
166                 listener = new SimpleCaptureCallback();
167                 startPreview(requestBuilder, previewSz, listener);
168                 waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
169                 // Allow at most one lock OFF state as the exposure is changed once.
170                 verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/1);
171 
172                 stopPreview();
173             } finally {
174                 closeDevice();
175             }
176         }
177     }
178 
179     /**
180      * Basic lens shading map request test.
181      * <p>
182      * When {@link CaptureRequest#SHADING_MODE} is set to OFF, no lens shading correction will
183      * be applied by the camera device, and an identity lens shading map data
184      * will be provided if {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} is ON.
185      * </p>
186      * <p>
187      * When {@link CaptureRequest#SHADING_MODE} is set to other modes, lens shading correction
188      * will be applied by the camera device. The lens shading map data can be
189      * requested by setting {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} to ON.
190      * </p>
191      */
testLensShadingMap()192     public void testLensShadingMap() throws Exception {
193         for (int i = 0; i < mCameraIds.length; i++) {
194             try {
195                 openDevice(mCameraIds[i]);
196 
197                 if (!mStaticInfo.isManualLensShadingMapSupported()) {
198                     Log.i(TAG, "Camera " + mCameraIds[i] +
199                             " doesn't support lens shading controls, skipping test");
200                     continue;
201                 }
202 
203                 List<Integer> lensShadingMapModes = Arrays.asList(CameraTestUtils.toObject(
204                         mStaticInfo.getAvailableLensShadingMapModesChecked()));
205 
206                 if (!lensShadingMapModes.contains(STATISTICS_LENS_SHADING_MAP_MODE_ON)) {
207                     continue;
208                 }
209 
210                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
211                 CaptureRequest.Builder requestBuilder =
212                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
213                 requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
214                         STATISTICS_LENS_SHADING_MAP_MODE_ON);
215 
216                 Size previewSz =
217                         getMaxPreviewSize(mCamera.getId(), mCameraManager,
218                         getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
219                 List<Integer> lensShadingModes = Arrays.asList(CameraTestUtils.toObject(
220                         mStaticInfo.getAvailableLensShadingModesChecked()));
221 
222                 // Shading map mode OFF, lensShadingMapMode ON, camera device
223                 // should output unity maps.
224                 if (lensShadingModes.contains(SHADING_MODE_OFF)) {
225                     requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_OFF);
226                     listener = new SimpleCaptureCallback();
227                     startPreview(requestBuilder, previewSz, listener);
228                     waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
229                     verifyShadingMap(listener, NUM_FRAMES_VERIFIED, SHADING_MODE_OFF);
230                 }
231 
232                 // Shading map mode FAST, lensShadingMapMode ON, camera device
233                 // should output valid maps.
234                 if (lensShadingModes.contains(SHADING_MODE_FAST)) {
235                     requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_FAST);
236 
237                     listener = new SimpleCaptureCallback();
238                     startPreview(requestBuilder, previewSz, listener);
239                     waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
240                     // Allow at most one lock OFF state as the exposure is changed once.
241                     verifyShadingMap(listener, NUM_FRAMES_VERIFIED, SHADING_MODE_FAST);
242                 }
243 
244                 // Shading map mode HIGH_QUALITY, lensShadingMapMode ON, camera device
245                 // should output valid maps.
246                 if (lensShadingModes.contains(SHADING_MODE_HIGH_QUALITY)) {
247                     requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_HIGH_QUALITY);
248 
249                     listener = new SimpleCaptureCallback();
250                     startPreview(requestBuilder, previewSz, listener);
251                     waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
252                     verifyShadingMap(listener, NUM_FRAMES_VERIFIED, SHADING_MODE_HIGH_QUALITY);
253                 }
254 
255                 stopPreview();
256             } finally {
257                 closeDevice();
258             }
259         }
260     }
261 
262     /**
263      * Test {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE} control.
264      * <p>
265      * Test all available anti-banding modes, check if the exposure time adjustment is
266      * correct.
267      * </p>
268      */
testAntiBandingModes()269     public void testAntiBandingModes() throws Exception {
270         for (int i = 0; i < mCameraIds.length; i++) {
271             try {
272                 openDevice(mCameraIds[i]);
273 
274                 // Without manual sensor control, exposure time cannot be verified
275                 if (!mStaticInfo.isCapabilitySupported(
276                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
277                     continue;
278                 }
279 
280                 int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
281 
282                 Size previewSz =
283                         getMaxPreviewSize(mCamera.getId(), mCameraManager,
284                         getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
285 
286                 for (int mode : modes) {
287                     antiBandingTestByMode(previewSz, mode);
288                 }
289             } finally {
290                 closeDevice();
291             }
292         }
293 
294     }
295 
296     /**
297      * Test AE mode and lock.
298      *
299      * <p>
300      * For AE lock, when it is locked, exposure parameters shouldn't be changed.
301      * For AE modes, each mode should satisfy the per frame controls defined in
302      * API specifications.
303      * </p>
304      */
testAeModeAndLock()305     public void testAeModeAndLock() throws Exception {
306         for (int i = 0; i < mCameraIds.length; i++) {
307             try {
308                 openDevice(mCameraIds[i]);
309                 if (!mStaticInfo.isColorOutputSupported()) {
310                     Log.i(TAG, "Camera " + mCameraIds[i] +
311                             " does not support color outputs, skipping");
312                     continue;
313                 }
314 
315                 Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
316 
317                 // Update preview surface with given size for all sub-tests.
318                 updatePreviewSurface(maxPreviewSz);
319 
320                 // Test aeMode and lock
321                 int[] aeModes = mStaticInfo.getAeAvailableModesChecked();
322                 for (int mode : aeModes) {
323                     aeModeAndLockTestByMode(mode);
324                 }
325             } finally {
326                 closeDevice();
327             }
328         }
329     }
330 
331     /** Test {@link CaptureRequest#FLASH_MODE} control.
332      * <p>
333      * For each {@link CaptureRequest#FLASH_MODE} mode, test the flash control
334      * and {@link CaptureResult#FLASH_STATE} result.
335      * </p>
336      */
testFlashControl()337     public void testFlashControl() throws Exception {
338         for (int i = 0; i < mCameraIds.length; i++) {
339             try {
340                 openDevice(mCameraIds[i]);
341                 if (!mStaticInfo.isColorOutputSupported()) {
342                     Log.i(TAG, "Camera " + mCameraIds[i] +
343                             " does not support color outputs, skipping");
344                     continue;
345                 }
346 
347                 SimpleCaptureCallback listener = new SimpleCaptureCallback();
348                 CaptureRequest.Builder requestBuilder =
349                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
350 
351                 Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
352 
353                 startPreview(requestBuilder, maxPreviewSz, listener);
354 
355                 // Flash control can only be used when the AE mode is ON or OFF.
356                 flashTestByAeMode(listener, CaptureRequest.CONTROL_AE_MODE_ON);
357 
358                 // LEGACY won't support AE mode OFF
359                 boolean aeOffModeSupported = false;
360                 for (int aeMode : mStaticInfo.getAeAvailableModesChecked()) {
361                     if (aeMode == CaptureRequest.CONTROL_AE_MODE_OFF) {
362                         aeOffModeSupported = true;
363                     }
364                 }
365                 if (aeOffModeSupported) {
366                     flashTestByAeMode(listener, CaptureRequest.CONTROL_AE_MODE_OFF);
367                 }
368 
369                 stopPreview();
370             } finally {
371                 closeDevice();
372             }
373         }
374     }
375 
376     /**
377      * Test face detection modes and results.
378      */
testFaceDetection()379     public void testFaceDetection() throws Exception {
380         for (int i = 0; i < mCameraIds.length; i++) {
381             try {
382                 openDevice(mCameraIds[i]);
383                 if (!mStaticInfo.isColorOutputSupported()) {
384                     Log.i(TAG, "Camera " + mCameraIds[i] +
385                             " does not support color outputs, skipping");
386                     continue;
387                 }
388                 faceDetectionTestByCamera();
389             } finally {
390                 closeDevice();
391             }
392         }
393     }
394 
395     /**
396      * Test tone map modes and controls.
397      */
testToneMapControl()398     public void testToneMapControl() throws Exception {
399         for (String id : mCameraIds) {
400             try {
401                 openDevice(id);
402                 if (!mStaticInfo.isManualToneMapSupported()) {
403                     Log.i(TAG, "Camera " + id +
404                             " doesn't support tone mapping controls, skipping test");
405                     continue;
406                 }
407                 toneMapTestByCamera();
408             } finally {
409                 closeDevice();
410             }
411         }
412     }
413 
414     /**
415      * Test color correction modes and controls.
416      */
testColorCorrectionControl()417     public void testColorCorrectionControl() throws Exception {
418         for (String id : mCameraIds) {
419             try {
420                 openDevice(id);
421                 if (!mStaticInfo.isColorCorrectionSupported()) {
422                     Log.i(TAG, "Camera " + id +
423                             " doesn't support color correction controls, skipping test");
424                     continue;
425                 }
426                 colorCorrectionTestByCamera();
427             } finally {
428                 closeDevice();
429             }
430         }
431     }
432 
testEdgeModeControl()433     public void testEdgeModeControl() throws Exception {
434         for (String id : mCameraIds) {
435             try {
436                 openDevice(id);
437                 if (!mStaticInfo.isEdgeModeControlSupported()) {
438                     Log.i(TAG, "Camera " + id +
439                             " doesn't support EDGE_MODE controls, skipping test");
440                     continue;
441                 }
442 
443                 edgeModesTestByCamera();
444             } finally {
445                 closeDevice();
446             }
447         }
448     }
449 
450     /**
451      * Test focus distance control.
452      */
testFocusDistanceControl()453     public void testFocusDistanceControl() throws Exception {
454         for (String id : mCameraIds) {
455             try {
456                 openDevice(id);
457                 if (!mStaticInfo.hasFocuser()) {
458                     Log.i(TAG, "Camera " + id + " has no focuser, skipping test");
459                     continue;
460                 }
461 
462                 if (!mStaticInfo.isCapabilitySupported(
463                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
464                     Log.i(TAG, "Camera " + id +
465                             " does not support MANUAL_SENSOR, skipping test");
466                     continue;
467                 }
468 
469                 focusDistanceTestByCamera();
470             } finally {
471                 closeDevice();
472             }
473         }
474     }
475 
testNoiseReductionModeControl()476     public void testNoiseReductionModeControl() throws Exception {
477         for (String id : mCameraIds) {
478             try {
479                 openDevice(id);
480                 if (!mStaticInfo.isNoiseReductionModeControlSupported()) {
481                     Log.i(TAG, "Camera " + id +
482                             " doesn't support noise reduction mode, skipping test");
483                     continue;
484                 }
485 
486                 noiseReductionModeTestByCamera();
487             } finally {
488                 closeDevice();
489             }
490         }
491     }
492 
493     /**
494      * Test AWB lock control.
495      *
496      * <p>The color correction gain and transform shouldn't be changed when AWB is locked.</p>
497      */
testAwbModeAndLock()498     public void testAwbModeAndLock() throws Exception {
499         for (String id : mCameraIds) {
500             try {
501                 openDevice(id);
502                 if (!mStaticInfo.isColorOutputSupported()) {
503                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
504                     continue;
505                 }
506                 awbModeAndLockTestByCamera();
507             } finally {
508                 closeDevice();
509             }
510         }
511     }
512 
513     /**
514      * Test different AF modes.
515      */
testAfModes()516     public void testAfModes() throws Exception {
517         for (String id : mCameraIds) {
518             try {
519                 openDevice(id);
520                 if (!mStaticInfo.isColorOutputSupported()) {
521                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
522                     continue;
523                 }
524                 afModeTestByCamera();
525             } finally {
526                 closeDevice();
527             }
528         }
529     }
530 
531     /**
532      * Test video and optical stabilizations.
533      */
testCameraStabilizations()534     public void testCameraStabilizations() throws Exception {
535         for (String id : mCameraIds) {
536             try {
537                 openDevice(id);
538                 List<Key<?>> keys = mStaticInfo.getCharacteristics().getKeys();
539                 if (!(keys.contains(
540                         CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES) ||
541                         keys.contains(
542                                 CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION))) {
543                     Log.i(TAG, "Camera " + id + " doesn't support any stabilization modes");
544                     continue;
545                 }
546                 if (!mStaticInfo.isColorOutputSupported()) {
547                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
548                     continue;
549                 }
550                 stabilizationTestByCamera();
551             } finally {
552                 closeDevice();
553             }
554         }
555     }
556 
557     /**
558      * Test digitalZoom (center wise and non-center wise), validate the returned crop regions.
559      * The max preview size is used for each camera.
560      */
testDigitalZoom()561     public void testDigitalZoom() throws Exception {
562         for (String id : mCameraIds) {
563             try {
564                 openDevice(id);
565                 if (!mStaticInfo.isColorOutputSupported()) {
566                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
567                     continue;
568                 }
569                 Size maxPreviewSize = mOrderedPreviewSizes.get(0);
570                 digitalZoomTestByCamera(maxPreviewSize);
571             } finally {
572                 closeDevice();
573             }
574         }
575     }
576 
577     /**
578      * Test digital zoom and all preview size combinations.
579      * TODO: this and above test should all be moved to preview test class.
580      */
testDigitalZoomPreviewCombinations()581     public void testDigitalZoomPreviewCombinations() throws Exception {
582         for (String id : mCameraIds) {
583             try {
584                 openDevice(id);
585                 if (!mStaticInfo.isColorOutputSupported()) {
586                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
587                     continue;
588                 }
589                 digitalZoomPreviewCombinationTestByCamera();
590             } finally {
591                 closeDevice();
592             }
593         }
594     }
595 
596     /**
597      * Test scene mode controls.
598      */
testSceneModes()599     public void testSceneModes() throws Exception {
600         for (String id : mCameraIds) {
601             try {
602                 openDevice(id);
603                 if (mStaticInfo.isSceneModeSupported()) {
604                     sceneModeTestByCamera();
605                 }
606             } finally {
607                 closeDevice();
608             }
609         }
610     }
611 
612     /**
613      * Test effect mode controls.
614      */
testEffectModes()615     public void testEffectModes() throws Exception {
616         for (String id : mCameraIds) {
617             try {
618                 openDevice(id);
619                 if (!mStaticInfo.isColorOutputSupported()) {
620                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
621                     continue;
622                 }
623                 effectModeTestByCamera();
624             } finally {
625                 closeDevice();
626             }
627         }
628     }
629 
630     // TODO: add 3A state machine test.
631 
noiseReductionModeTestByCamera()632     private void noiseReductionModeTestByCamera() throws Exception {
633         Size maxPrevSize = mOrderedPreviewSizes.get(0);
634         CaptureRequest.Builder requestBuilder =
635                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
636         int[] availableModes = mStaticInfo.getAvailableNoiseReductionModesChecked();
637         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
638         startPreview(requestBuilder, maxPrevSize, resultListener);
639 
640         for (int mode : availableModes) {
641             requestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, mode);
642             resultListener = new SimpleCaptureCallback();
643             mSession.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
644             waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
645 
646             verifyCaptureResultForKey(CaptureResult.NOISE_REDUCTION_MODE, mode,
647                     resultListener, NUM_FRAMES_VERIFIED);
648 
649             // Test that OFF and FAST mode should not slow down the frame rate.
650             if (mode == CaptureRequest.NOISE_REDUCTION_MODE_OFF ||
651                     mode == CaptureRequest.NOISE_REDUCTION_MODE_FAST) {
652                 verifyFpsNotSlowDown(requestBuilder, NUM_FRAMES_VERIFIED);
653             }
654         }
655 
656         stopPreview();
657     }
658 
focusDistanceTestByCamera()659     private void focusDistanceTestByCamera() throws Exception {
660         Size maxPrevSize = mOrderedPreviewSizes.get(0);
661         float[] testDistances = getFocusDistanceTestValuesInOrder();
662         CaptureRequest.Builder requestBuilder =
663                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
664         requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
665         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
666         startPreview(requestBuilder, maxPrevSize, resultListener);
667 
668         CaptureRequest request;
669         float[] resultDistances = new float[testDistances.length];
670         for (int i = 0; i < testDistances.length; i++) {
671             requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, testDistances[i]);
672             request = requestBuilder.build();
673             resultListener = new SimpleCaptureCallback();
674             mSession.setRepeatingRequest(request, resultListener, mHandler);
675             waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
676             resultDistances[i] = verifyFocusDistanceControl(testDistances[i], request,
677                     resultListener);
678             if (VERBOSE) {
679                 Log.v(TAG, "Capture request focus distance: " + testDistances[i] + " result: "
680                         + resultDistances[i]);
681             }
682         }
683 
684         // Verify the monotonicity
685         mCollector.checkArrayMonotonicityAndNotAllEqual(CameraTestUtils.toObject(resultDistances),
686                 /*ascendingOrder*/true);
687 
688         if (mStaticInfo.getCharacteristics().getKeys().
689                 contains(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE)) {
690 
691             // Test hyperfocal distance optionally
692             float hyperFocalDistance = mStaticInfo.getHyperfocalDistanceChecked();
693             if (hyperFocalDistance > 0) {
694                 requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, hyperFocalDistance);
695                 request = requestBuilder.build();
696                 resultListener = new SimpleCaptureCallback();
697                 mSession.setRepeatingRequest(request, resultListener, mHandler);
698                 waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
699 
700                 // Then wait for the lens.state to be stationary.
701                 waitForResultValue(resultListener, CaptureResult.LENS_STATE,
702                         CaptureResult.LENS_STATE_STATIONARY, NUM_RESULTS_WAIT_TIMEOUT);
703                 // Need get reasonably accurate value.
704                 CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
705                 Float focusDistance = getValueNotNull(result, CaptureResult.LENS_FOCUS_DISTANCE);
706                 float errorMargin = FOCUS_DISTANCE_ERROR_PERCENT_UNCALIBRATED;
707                 int calibrationStatus = mStaticInfo.getFocusDistanceCalibrationChecked();
708                 if (calibrationStatus ==
709                         CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED) {
710                     errorMargin = FOCUS_DISTANCE_ERROR_PERCENT_CALIBRATED;
711                 } else if (calibrationStatus ==
712                         CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE) {
713                     errorMargin = FOCUS_DISTANCE_ERROR_PERCENT_APPROXIMATE;
714                 }
715                 mCollector.expectInRange("Focus distance for hyper focal should be close enough to" +
716                                 "requested value", focusDistance,
717                         hyperFocalDistance * (1.0f - errorMargin),
718                         hyperFocalDistance * (1.0f + errorMargin)
719                 );
720             }
721         }
722     }
723 
724     /**
725      * Verify focus distance control.
726      *
727      * @param distance The focus distance requested
728      * @param request The capture request to control the manual focus distance
729      * @param resultListener The capture listener to recieve capture result callbacks
730      * @return the result focus distance
731      */
verifyFocusDistanceControl(float distance, CaptureRequest request, SimpleCaptureCallback resultListener)732     private float verifyFocusDistanceControl(float distance, CaptureRequest request,
733             SimpleCaptureCallback resultListener) {
734         // Need make sure the result corresponding to the request is back, then check.
735         CaptureResult result =
736                 resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
737         // Then wait for the lens.state to be stationary.
738         waitForResultValue(resultListener, CaptureResult.LENS_STATE,
739                 CaptureResult.LENS_STATE_STATIONARY, NUM_RESULTS_WAIT_TIMEOUT);
740         // Then check the focus distance.
741         result = resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
742         Float resultDistance = getValueNotNull(result, CaptureResult.LENS_FOCUS_DISTANCE);
743         if (mStaticInfo.getFocusDistanceCalibrationChecked() ==
744                 CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED) {
745             // TODO: what's more to test for CALIBRATED devices?
746         }
747 
748         float minValue = 0;
749         float maxValue = mStaticInfo.getMinimumFocusDistanceChecked();
750         mCollector.expectInRange("Result focus distance is out of range",
751                 resultDistance, minValue, maxValue);
752 
753         return resultDistance;
754     }
755 
756     /**
757      * Verify edge mode control results.
758      */
edgeModesTestByCamera()759     private void edgeModesTestByCamera() throws Exception {
760         Size maxPrevSize = mOrderedPreviewSizes.get(0);
761         int[] edgeModes = mStaticInfo.getAvailableEdgeModesChecked();
762         CaptureRequest.Builder requestBuilder =
763                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
764         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
765         startPreview(requestBuilder, maxPrevSize, resultListener);
766 
767         for (int mode : edgeModes) {
768             requestBuilder.set(CaptureRequest.EDGE_MODE, mode);
769             resultListener = new SimpleCaptureCallback();
770             mSession.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
771             waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
772 
773             verifyCaptureResultForKey(CaptureResult.EDGE_MODE, mode, resultListener,
774                     NUM_FRAMES_VERIFIED);
775 
776             // Test that OFF and FAST mode should not slow down the frame rate.
777             if (mode == CaptureRequest.EDGE_MODE_OFF ||
778                     mode == CaptureRequest.EDGE_MODE_FAST) {
779                 verifyFpsNotSlowDown(requestBuilder, NUM_FRAMES_VERIFIED);
780             }
781         }
782 
783         stopPreview();
784     }
785 
786     /**
787      * Test color correction controls.
788      *
789      * <p>Test different color correction modes. For TRANSFORM_MATRIX, only test
790      * the unit gain and identity transform.</p>
791      */
colorCorrectionTestByCamera()792     private void colorCorrectionTestByCamera() throws Exception {
793         CaptureRequest request;
794         CaptureResult result;
795         Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
796         updatePreviewSurface(maxPreviewSz);
797         CaptureRequest.Builder manualRequestBuilder = createRequestForPreview();
798         CaptureRequest.Builder previewRequestBuilder = createRequestForPreview();
799         SimpleCaptureCallback listener = new SimpleCaptureCallback();
800 
801         startPreview(previewRequestBuilder, maxPreviewSz, listener);
802 
803         // Default preview result should give valid color correction metadata.
804         result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
805         validateColorCorrectionResult(result,
806                 previewRequestBuilder.get(CaptureRequest.COLOR_CORRECTION_MODE));
807         int colorCorrectionMode = CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX;
808         // TRANSFORM_MATRIX mode
809         // Only test unit gain and identity transform
810         List<Integer> availableControlModes = Arrays.asList(
811                 CameraTestUtils.toObject(mStaticInfo.getAvailableControlModesChecked()));
812         List<Integer> availableAwbModes = Arrays.asList(
813                 CameraTestUtils.toObject(mStaticInfo.getAwbAvailableModesChecked()));
814         boolean isManualCCSupported =
815                 availableControlModes.contains(CaptureRequest.CONTROL_MODE_OFF) ||
816                 availableAwbModes.contains(CaptureRequest.CONTROL_AWB_MODE_OFF);
817         if (isManualCCSupported) {
818             if (!availableControlModes.contains(CaptureRequest.CONTROL_MODE_OFF)) {
819                 // Only manual AWB mode is supported
820                 manualRequestBuilder.set(CaptureRequest.CONTROL_MODE,
821                         CaptureRequest.CONTROL_MODE_AUTO);
822                 manualRequestBuilder.set(CaptureRequest.CONTROL_AWB_MODE,
823                         CaptureRequest.CONTROL_AWB_MODE_OFF);
824             } else {
825                 // All 3A manual controls are supported, it doesn't matter what we set for AWB mode.
826                 manualRequestBuilder.set(CaptureRequest.CONTROL_MODE,
827                         CaptureRequest.CONTROL_MODE_OFF);
828             }
829 
830             RggbChannelVector UNIT_GAIN = new RggbChannelVector(1.0f, 1.0f, 1.0f, 1.0f);
831 
832             ColorSpaceTransform IDENTITY_TRANSFORM = new ColorSpaceTransform(
833                 new Rational[] {
834                     ONE_R, ZERO_R, ZERO_R,
835                     ZERO_R, ONE_R, ZERO_R,
836                     ZERO_R, ZERO_R, ONE_R
837                 });
838 
839             manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, colorCorrectionMode);
840             manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_GAINS, UNIT_GAIN);
841             manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_TRANSFORM, IDENTITY_TRANSFORM);
842             request = manualRequestBuilder.build();
843             mSession.capture(request, listener, mHandler);
844             result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
845             RggbChannelVector gains = result.get(CaptureResult.COLOR_CORRECTION_GAINS);
846             ColorSpaceTransform transform = result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM);
847             validateColorCorrectionResult(result, colorCorrectionMode);
848             mCollector.expectEquals("control mode result/request mismatch",
849                     CaptureResult.CONTROL_MODE_OFF, result.get(CaptureResult.CONTROL_MODE));
850             mCollector.expectEquals("Color correction gain result/request mismatch",
851                     UNIT_GAIN, gains);
852             mCollector.expectEquals("Color correction gain result/request mismatch",
853                     IDENTITY_TRANSFORM, transform);
854 
855         }
856 
857         // FAST mode
858         colorCorrectionMode = CaptureRequest.COLOR_CORRECTION_MODE_FAST;
859         manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
860         manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, colorCorrectionMode);
861         request = manualRequestBuilder.build();
862         mSession.capture(request, listener, mHandler);
863         result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
864         validateColorCorrectionResult(result, colorCorrectionMode);
865         mCollector.expectEquals("control mode result/request mismatch",
866                 CaptureResult.CONTROL_MODE_AUTO, result.get(CaptureResult.CONTROL_MODE));
867 
868         // HIGH_QUALITY mode
869         colorCorrectionMode = CaptureRequest.COLOR_CORRECTION_MODE_HIGH_QUALITY;
870         manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
871         manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, colorCorrectionMode);
872         request = manualRequestBuilder.build();
873         mSession.capture(request, listener, mHandler);
874         result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
875         validateColorCorrectionResult(result, colorCorrectionMode);
876         mCollector.expectEquals("control mode result/request mismatch",
877                 CaptureResult.CONTROL_MODE_AUTO, result.get(CaptureResult.CONTROL_MODE));
878     }
879 
validateColorCorrectionResult(CaptureResult result, int colorCorrectionMode)880     private void validateColorCorrectionResult(CaptureResult result, int colorCorrectionMode) {
881         final RggbChannelVector ZERO_GAINS = new RggbChannelVector(0, 0, 0, 0);
882         final int TRANSFORM_SIZE = 9;
883         Rational[] zeroTransform = new Rational[TRANSFORM_SIZE];
884         Arrays.fill(zeroTransform, ZERO_R);
885         final ColorSpaceTransform ZERO_TRANSFORM = new ColorSpaceTransform(zeroTransform);
886 
887         RggbChannelVector resultGain;
888         if ((resultGain = mCollector.expectKeyValueNotNull(result,
889                 CaptureResult.COLOR_CORRECTION_GAINS)) != null) {
890             mCollector.expectKeyValueNotEquals(result,
891                     CaptureResult.COLOR_CORRECTION_GAINS, ZERO_GAINS);
892         }
893 
894         ColorSpaceTransform resultTransform;
895         if ((resultTransform = mCollector.expectKeyValueNotNull(result,
896                 CaptureResult.COLOR_CORRECTION_TRANSFORM)) != null) {
897             mCollector.expectKeyValueNotEquals(result,
898                     CaptureResult.COLOR_CORRECTION_TRANSFORM, ZERO_TRANSFORM);
899         }
900 
901         mCollector.expectEquals("color correction mode result/request mismatch",
902                 colorCorrectionMode, result.get(CaptureResult.COLOR_CORRECTION_MODE));
903     }
904 
905     /**
906      * Test flash mode control by AE mode.
907      * <p>
908      * Only allow AE mode ON or OFF, because other AE mode could run into conflict with
909      * flash manual control. This function expects the camera to already have an active
910      * repeating request and be sending results to the listener.
911      * </p>
912      *
913      * @param listener The Capture listener that is used to wait for capture result
914      * @param aeMode The AE mode for flash to test with
915      */
flashTestByAeMode(SimpleCaptureCallback listener, int aeMode)916     private void flashTestByAeMode(SimpleCaptureCallback listener, int aeMode) throws Exception {
917         CaptureResult result;
918         final int NUM_FLASH_REQUESTS_TESTED = 10;
919         CaptureRequest.Builder requestBuilder = createRequestForPreview();
920 
921         if (aeMode == CaptureRequest.CONTROL_AE_MODE_ON) {
922             requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, aeMode);
923         } else if (aeMode == CaptureRequest.CONTROL_AE_MODE_OFF) {
924             changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
925         } else {
926             throw new IllegalArgumentException("This test only works when AE mode is ON or OFF");
927         }
928 
929         mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
930         waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
931 
932         // For camera that doesn't have flash unit, flash state should always be UNAVAILABLE.
933         if (mStaticInfo.getFlashInfoChecked() == false) {
934             for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
935                 result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
936                 mCollector.expectEquals("No flash unit available, flash state must be UNAVAILABLE"
937                         + "for AE mode " + aeMode, CaptureResult.FLASH_STATE_UNAVAILABLE,
938                         result.get(CaptureResult.FLASH_STATE));
939             }
940 
941             return;
942         }
943 
944         // Test flash SINGLE mode control. Wait for flash state to be READY first.
945         if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
946             waitForResultValue(listener, CaptureResult.FLASH_STATE, CaptureResult.FLASH_STATE_READY,
947                     NUM_RESULTS_WAIT_TIMEOUT);
948         } // else the settings were already waited on earlier
949 
950         requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
951         CaptureRequest flashSinglerequest = requestBuilder.build();
952 
953         int flashModeSingleRequests = captureRequestsSynchronized(
954                 flashSinglerequest, listener, mHandler);
955         waitForNumResults(listener, flashModeSingleRequests - 1);
956         result = listener.getCaptureResultForRequest(flashSinglerequest, NUM_RESULTS_WAIT_TIMEOUT);
957         // Result mode must be SINGLE, state must be FIRED.
958         mCollector.expectEquals("Flash mode result must be SINGLE",
959                 CaptureResult.FLASH_MODE_SINGLE, result.get(CaptureResult.FLASH_MODE));
960         mCollector.expectEquals("Flash state result must be FIRED",
961                 CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
962 
963         // Test flash TORCH mode control.
964         requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
965         CaptureRequest torchRequest = requestBuilder.build();
966 
967         int flashModeTorchRequests = captureRequestsSynchronized(torchRequest,
968                 NUM_FLASH_REQUESTS_TESTED, listener, mHandler);
969         waitForNumResults(listener, flashModeTorchRequests - NUM_FLASH_REQUESTS_TESTED);
970 
971         // Verify the results
972         TorchSeqState state = TorchSeqState.RAMPING_UP;
973         for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
974             result = listener.getCaptureResultForRequest(torchRequest,
975                     NUM_RESULTS_WAIT_TIMEOUT);
976             int flashMode = result.get(CaptureResult.FLASH_MODE);
977             int flashState = result.get(CaptureResult.FLASH_STATE);
978             // Result mode must be TORCH
979             mCollector.expectEquals("Flash mode result " + i + " must be TORCH",
980                     CaptureResult.FLASH_MODE_TORCH, result.get(CaptureResult.FLASH_MODE));
981             if (state == TorchSeqState.RAMPING_UP &&
982                     flashState == CaptureResult.FLASH_STATE_FIRED) {
983                 state = TorchSeqState.FIRED;
984             } else if (state == TorchSeqState.FIRED &&
985                     flashState == CaptureResult.FLASH_STATE_PARTIAL) {
986                 state = TorchSeqState.RAMPING_DOWN;
987             }
988 
989             if (i == 0 && mStaticInfo.isPerFrameControlSupported()) {
990                 mCollector.expectTrue(
991                         "Per frame control device must enter FIRED state on first torch request",
992                         state == TorchSeqState.FIRED);
993             }
994 
995             if (state == TorchSeqState.FIRED) {
996                 mCollector.expectEquals("Flash state result " + i + " must be FIRED",
997                         CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
998             } else {
999                 mCollector.expectEquals("Flash state result " + i + " must be PARTIAL",
1000                         CaptureResult.FLASH_STATE_PARTIAL, result.get(CaptureResult.FLASH_STATE));
1001             }
1002         }
1003         mCollector.expectTrue("Torch state FIRED never seen",
1004                 state == TorchSeqState.FIRED || state == TorchSeqState.RAMPING_DOWN);
1005 
1006         // Test flash OFF mode control
1007         requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
1008         CaptureRequest flashOffrequest = requestBuilder.build();
1009 
1010         int flashModeOffRequests = captureRequestsSynchronized(flashOffrequest, listener, mHandler);
1011         waitForNumResults(listener, flashModeOffRequests - 1);
1012         result = listener.getCaptureResultForRequest(flashOffrequest, NUM_RESULTS_WAIT_TIMEOUT);
1013         mCollector.expectEquals("Flash mode result must be OFF", CaptureResult.FLASH_MODE_OFF,
1014                 result.get(CaptureResult.FLASH_MODE));
1015     }
1016 
verifyAntiBandingMode(SimpleCaptureCallback listener, int numFramesVerified, int mode, boolean isAeManual, long requestExpTime)1017     private void verifyAntiBandingMode(SimpleCaptureCallback listener, int numFramesVerified,
1018             int mode, boolean isAeManual, long requestExpTime) throws Exception {
1019         // Skip the first a couple of frames as antibanding may not be fully up yet.
1020         final int NUM_FRAMES_SKIPPED = 5;
1021         for (int i = 0; i < NUM_FRAMES_SKIPPED; i++) {
1022             listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1023         }
1024 
1025         for (int i = 0; i < numFramesVerified; i++) {
1026             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1027             Long resultExpTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
1028             assertNotNull("Exposure time shouldn't be null", resultExpTime);
1029             Integer flicker = result.get(CaptureResult.STATISTICS_SCENE_FLICKER);
1030             // Scene flicker result should be always available.
1031             assertNotNull("Scene flicker must not be null", flicker);
1032             assertTrue("Scene flicker is invalid", flicker >= STATISTICS_SCENE_FLICKER_NONE &&
1033                     flicker <= STATISTICS_SCENE_FLICKER_60HZ);
1034 
1035             if (isAeManual) {
1036                 // First, round down not up, second, need close enough.
1037                 validateExposureTime(requestExpTime, resultExpTime);
1038                 return;
1039             }
1040 
1041             long expectedExpTime = resultExpTime; // Default, no exposure adjustment.
1042             if (mode == CONTROL_AE_ANTIBANDING_MODE_50HZ) {
1043                 // result exposure time must be adjusted by 50Hz illuminant source.
1044                 expectedExpTime =
1045                         getAntiFlickeringExposureTime(ANTI_FLICKERING_50HZ, resultExpTime);
1046             } else if (mode == CONTROL_AE_ANTIBANDING_MODE_60HZ) {
1047                 // result exposure time must be adjusted by 60Hz illuminant source.
1048                 expectedExpTime =
1049                         getAntiFlickeringExposureTime(ANTI_FLICKERING_60HZ, resultExpTime);
1050             } else if (mode == CONTROL_AE_ANTIBANDING_MODE_AUTO){
1051                 /**
1052                  * Use STATISTICS_SCENE_FLICKER to tell the illuminant source
1053                  * and do the exposure adjustment.
1054                  */
1055                 expectedExpTime = resultExpTime;
1056                 if (flicker == STATISTICS_SCENE_FLICKER_60HZ) {
1057                     expectedExpTime =
1058                             getAntiFlickeringExposureTime(ANTI_FLICKERING_60HZ, resultExpTime);
1059                 } else if (flicker == STATISTICS_SCENE_FLICKER_50HZ) {
1060                     expectedExpTime =
1061                             getAntiFlickeringExposureTime(ANTI_FLICKERING_50HZ, resultExpTime);
1062                 }
1063             }
1064 
1065             if (Math.abs(resultExpTime - expectedExpTime) > EXPOSURE_TIME_ERROR_MARGIN_NS) {
1066                 mCollector.addMessage(String.format("Result exposure time %dns diverges too much"
1067                         + " from expected exposure time %dns for mode %d when AE is auto",
1068                         resultExpTime, expectedExpTime, mode));
1069             }
1070         }
1071     }
1072 
antiBandingTestByMode(Size size, int mode)1073     private void antiBandingTestByMode(Size size, int mode)
1074             throws Exception {
1075         if(VERBOSE) {
1076             Log.v(TAG, "Anti-banding test for mode " + mode + " for camera " + mCamera.getId());
1077         }
1078         CaptureRequest.Builder requestBuilder =
1079                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1080 
1081         requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, mode);
1082 
1083         // Test auto AE mode anti-banding behavior
1084         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
1085         startPreview(requestBuilder, size, resultListener);
1086         waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1087         verifyAntiBandingMode(resultListener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/false,
1088                 IGNORE_REQUESTED_EXPOSURE_TIME_CHECK);
1089 
1090         // Test manual AE mode anti-banding behavior
1091         // 65ms, must be supported by full capability devices.
1092         final long TEST_MANUAL_EXP_TIME_NS = 65000000L;
1093         long manualExpTime = mStaticInfo.getExposureClampToRange(TEST_MANUAL_EXP_TIME_NS);
1094         changeExposure(requestBuilder, manualExpTime);
1095         resultListener = new SimpleCaptureCallback();
1096         startPreview(requestBuilder, size, resultListener);
1097         waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1098         verifyAntiBandingMode(resultListener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/true,
1099                 manualExpTime);
1100 
1101         stopPreview();
1102     }
1103 
1104     /**
1105      * Test the all available AE modes and AE lock.
1106      * <p>
1107      * For manual AE mode, test iterates through different sensitivities and
1108      * exposure times, validate the result exposure time correctness. For
1109      * CONTROL_AE_MODE_ON_ALWAYS_FLASH mode, the AE lock and flash are tested.
1110      * For the rest of the AUTO mode, AE lock is tested.
1111      * </p>
1112      *
1113      * @param mode
1114      */
aeModeAndLockTestByMode(int mode)1115     private void aeModeAndLockTestByMode(int mode)
1116             throws Exception {
1117         switch (mode) {
1118             case CONTROL_AE_MODE_OFF:
1119                 if (mStaticInfo.isCapabilitySupported(
1120                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
1121                     // Test manual exposure control.
1122                     aeManualControlTest();
1123                 } else {
1124                     Log.w(TAG,
1125                             "aeModeAndLockTestByMode - can't test AE mode OFF without " +
1126                             "manual sensor control");
1127                 }
1128                 break;
1129             case CONTROL_AE_MODE_ON:
1130             case CONTROL_AE_MODE_ON_AUTO_FLASH:
1131             case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
1132             case CONTROL_AE_MODE_ON_ALWAYS_FLASH:
1133                 // Test AE lock for above AUTO modes.
1134                 aeAutoModeTestLock(mode);
1135                 break;
1136             default:
1137                 throw new UnsupportedOperationException("Unhandled AE mode " + mode);
1138         }
1139     }
1140 
1141     /**
1142      * Test AE auto modes.
1143      * <p>
1144      * Use single request rather than repeating request to test AE lock per frame control.
1145      * </p>
1146      */
aeAutoModeTestLock(int mode)1147     private void aeAutoModeTestLock(int mode) throws Exception {
1148         CaptureRequest.Builder requestBuilder =
1149                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1150         if (mStaticInfo.isAeLockSupported()) {
1151             requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
1152         }
1153         requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mode);
1154         configurePreviewOutput(requestBuilder);
1155 
1156         final int MAX_NUM_CAPTURES_DURING_LOCK = 5;
1157         for (int i = 1; i <= MAX_NUM_CAPTURES_DURING_LOCK; i++) {
1158             autoAeMultipleCapturesThenTestLock(requestBuilder, mode, i);
1159         }
1160     }
1161 
1162     /**
1163      * Issue multiple auto AE captures, then lock AE, validate the AE lock vs.
1164      * the first capture result after the AE lock. The right AE lock behavior is:
1165      * When it is locked, it locks to the current exposure value, and all subsequent
1166      * request with lock ON will have the same exposure value locked.
1167      */
autoAeMultipleCapturesThenTestLock( CaptureRequest.Builder requestBuilder, int aeMode, int numCapturesDuringLock)1168     private void autoAeMultipleCapturesThenTestLock(
1169             CaptureRequest.Builder requestBuilder, int aeMode, int numCapturesDuringLock)
1170             throws Exception {
1171         if (numCapturesDuringLock < 1) {
1172             throw new IllegalArgumentException("numCapturesBeforeLock must be no less than 1");
1173         }
1174         if (VERBOSE) {
1175             Log.v(TAG, "Camera " + mCamera.getId() + ": Testing auto AE mode and lock for mode "
1176                     + aeMode + " with " + numCapturesDuringLock + " captures before lock");
1177         }
1178 
1179         final int NUM_CAPTURES_BEFORE_LOCK = 2;
1180         SimpleCaptureCallback listener =  new SimpleCaptureCallback();
1181 
1182         CaptureResult[] resultsDuringLock = new CaptureResult[numCapturesDuringLock];
1183         boolean canSetAeLock = mStaticInfo.isAeLockSupported();
1184 
1185         // Reset the AE lock to OFF, since we are reusing this builder many times
1186         if (canSetAeLock) {
1187             requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
1188         }
1189 
1190         // Just send several captures with auto AE, lock off.
1191         CaptureRequest request = requestBuilder.build();
1192         for (int i = 0; i < NUM_CAPTURES_BEFORE_LOCK; i++) {
1193             mSession.capture(request, listener, mHandler);
1194         }
1195         waitForNumResults(listener, NUM_CAPTURES_BEFORE_LOCK);
1196 
1197         if (!canSetAeLock) {
1198             // Without AE lock, the remaining tests items won't work
1199             return;
1200         }
1201 
1202         // Then fire several capture to lock the AE.
1203         requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
1204 
1205         int requestCount = captureRequestsSynchronized(
1206                 requestBuilder.build(), numCapturesDuringLock, listener, mHandler);
1207 
1208         int[] sensitivities = new int[numCapturesDuringLock];
1209         long[] expTimes = new long[numCapturesDuringLock];
1210         Arrays.fill(sensitivities, -1);
1211         Arrays.fill(expTimes, -1L);
1212 
1213         // Get the AE lock on result and validate the exposure values.
1214         waitForNumResults(listener, requestCount - numCapturesDuringLock);
1215         for (int i = 0; i < resultsDuringLock.length; i++) {
1216             resultsDuringLock[i] = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1217         }
1218 
1219         for (int i = 0; i < numCapturesDuringLock; i++) {
1220             mCollector.expectKeyValueEquals(
1221                     resultsDuringLock[i], CaptureResult.CONTROL_AE_LOCK, true);
1222         }
1223 
1224         // Can't read manual sensor/exposure settings without manual sensor
1225         if (mStaticInfo.isCapabilitySupported(
1226                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
1227             int sensitivityLocked =
1228                     getValueNotNull(resultsDuringLock[0], CaptureResult.SENSOR_SENSITIVITY);
1229             long expTimeLocked =
1230                     getValueNotNull(resultsDuringLock[0], CaptureResult.SENSOR_EXPOSURE_TIME);
1231             for (int i = 1; i < resultsDuringLock.length; i++) {
1232                 mCollector.expectKeyValueEquals(
1233                         resultsDuringLock[i], CaptureResult.SENSOR_EXPOSURE_TIME, expTimeLocked);
1234                 mCollector.expectKeyValueEquals(
1235                         resultsDuringLock[i], CaptureResult.SENSOR_SENSITIVITY, sensitivityLocked);
1236             }
1237         }
1238     }
1239 
1240     /**
1241      * Iterate through exposure times and sensitivities for manual AE control.
1242      * <p>
1243      * Use single request rather than repeating request to test manual exposure
1244      * value change per frame control.
1245      * </p>
1246      */
aeManualControlTest()1247     private void aeManualControlTest()
1248             throws Exception {
1249         CaptureRequest.Builder requestBuilder =
1250                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1251 
1252         requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
1253         configurePreviewOutput(requestBuilder);
1254         SimpleCaptureCallback listener =  new SimpleCaptureCallback();
1255 
1256         long[] expTimes = getExposureTimeTestValues();
1257         int[] sensitivities = getSensitivityTestValues();
1258         // Submit single request at a time, then verify the result.
1259         for (int i = 0; i < expTimes.length; i++) {
1260             for (int j = 0; j < sensitivities.length; j++) {
1261                 if (VERBOSE) {
1262                     Log.v(TAG, "Camera " + mCamera.getId() + ": Testing sensitivity "
1263                             + sensitivities[j] + ", exposure time " + expTimes[i] + "ns");
1264                 }
1265 
1266                 changeExposure(requestBuilder, expTimes[i], sensitivities[j]);
1267                 mSession.capture(requestBuilder.build(), listener, mHandler);
1268 
1269                 // make sure timeout is long enough for long exposure time
1270                 long timeout = WAIT_FOR_RESULT_TIMEOUT_MS + expTimes[i];
1271                 CaptureResult result = listener.getCaptureResult(timeout);
1272                 long resultExpTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
1273                 int resultSensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
1274                 validateExposureTime(expTimes[i], resultExpTime);
1275                 validateSensitivity(sensitivities[j], resultSensitivity);
1276                 validateFrameDurationForCapture(result);
1277             }
1278         }
1279         // TODO: Add another case to test where we can submit all requests, then wait for
1280         // results, which will hide the pipeline latency. this is not only faster, but also
1281         // test high speed per frame control and synchronization.
1282     }
1283 
1284 
1285     /**
1286      * Verify black level lock control.
1287      */
verifyBlackLevelLockResults(SimpleCaptureCallback listener, int numFramesVerified, int maxLockOffCnt)1288     private void verifyBlackLevelLockResults(SimpleCaptureCallback listener, int numFramesVerified,
1289             int maxLockOffCnt) throws Exception {
1290         int noLockCnt = 0;
1291         for (int i = 0; i < numFramesVerified; i++) {
1292             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1293             Boolean blackLevelLock = result.get(CaptureResult.BLACK_LEVEL_LOCK);
1294             assertNotNull("Black level lock result shouldn't be null", blackLevelLock);
1295 
1296             // Count the lock == false result, which could possibly occur at most once.
1297             if (blackLevelLock == false) {
1298                 noLockCnt++;
1299             }
1300 
1301             if(VERBOSE) {
1302                 Log.v(TAG, "Black level lock result: " + blackLevelLock);
1303             }
1304         }
1305         assertTrue("Black level lock OFF occurs " + noLockCnt + " times,  expect at most "
1306                 + maxLockOffCnt + " for camera " + mCamera.getId(), noLockCnt <= maxLockOffCnt);
1307     }
1308 
1309     /**
1310      * Verify shading map for different shading modes.
1311      */
verifyShadingMap(SimpleCaptureCallback listener, int numFramesVerified, int shadingMode)1312     private void verifyShadingMap(SimpleCaptureCallback listener, int numFramesVerified,
1313             int shadingMode) throws Exception {
1314 
1315         for (int i = 0; i < numFramesVerified; i++) {
1316             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1317             mCollector.expectEquals("Shading mode result doesn't match request",
1318                     shadingMode, result.get(CaptureResult.SHADING_MODE));
1319             LensShadingMap mapObj = result.get(
1320                     CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
1321             assertNotNull("Map object must not be null", mapObj);
1322             int numElementsInMap = mapObj.getGainFactorCount();
1323             float[] map = new float[numElementsInMap];
1324             mapObj.copyGainFactors(map, /*offset*/0);
1325             assertNotNull("Map must not be null", map);
1326             assertFalse(String.format(
1327                     "Map size %d should be less than %d", numElementsInMap, MAX_SHADING_MAP_SIZE),
1328                     numElementsInMap >= MAX_SHADING_MAP_SIZE);
1329             assertFalse(String.format("Map size %d should be no less than %d", numElementsInMap,
1330                     MIN_SHADING_MAP_SIZE), numElementsInMap < MIN_SHADING_MAP_SIZE);
1331 
1332             if (shadingMode == CaptureRequest.SHADING_MODE_FAST ||
1333                     shadingMode == CaptureRequest.SHADING_MODE_HIGH_QUALITY) {
1334                 // shading mode is FAST or HIGH_QUALITY, expect to receive a map with all
1335                 // elements >= 1.0f
1336 
1337                 int badValueCnt = 0;
1338                 // Detect the bad values of the map data.
1339                 for (int j = 0; j < numElementsInMap; j++) {
1340                     if (Float.isNaN(map[j]) || map[j] < 1.0f) {
1341                         badValueCnt++;
1342                     }
1343                 }
1344                 assertEquals("Number of value in the map is " + badValueCnt + " out of "
1345                         + numElementsInMap, /*expected*/0, /*actual*/badValueCnt);
1346             } else if (shadingMode == CaptureRequest.SHADING_MODE_OFF) {
1347                 float[] unityMap = new float[numElementsInMap];
1348                 Arrays.fill(unityMap, 1.0f);
1349                 // shading mode is OFF, expect to receive a unity map.
1350                 assertTrue("Result map " + Arrays.toString(map) + " must be an unity map",
1351                         Arrays.equals(unityMap, map));
1352             }
1353         }
1354     }
1355 
1356     /**
1357      * Test face detection for a camera.
1358      */
1359     private void faceDetectionTestByCamera() throws Exception {
1360         int[] faceDetectModes = mStaticInfo.getAvailableFaceDetectModesChecked();
1361 
1362         SimpleCaptureCallback listener;
1363         CaptureRequest.Builder requestBuilder =
1364                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1365 
1366         Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
1367         for (int mode : faceDetectModes) {
1368             requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, mode);
1369             if (VERBOSE) {
1370                 Log.v(TAG, "Start testing face detection mode " + mode);
1371             }
1372 
1373             // Create a new listener for each run to avoid the results from one run spill
1374             // into another run.
1375             listener = new SimpleCaptureCallback();
1376             startPreview(requestBuilder, maxPreviewSz, listener);
1377             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1378             verifyFaceDetectionResults(listener, NUM_FACE_DETECTION_FRAMES_VERIFIED, mode);
1379         }
1380 
1381         stopPreview();
1382     }
1383 
1384     /**
1385      * Verify face detection results for different face detection modes.
1386      *
1387      * @param listener The listener to get capture result
1388      * @param numFramesVerified Number of results to be verified
1389      * @param faceDetectionMode Face detection mode to be verified against
1390      */
1391     private void verifyFaceDetectionResults(SimpleCaptureCallback listener, int numFramesVerified,
1392             int faceDetectionMode) {
1393         for (int i = 0; i < numFramesVerified; i++) {
1394             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1395             mCollector.expectEquals("Result face detection mode should match the request",
1396                     faceDetectionMode, result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE));
1397 
1398             Face[] faces = result.get(CaptureResult.STATISTICS_FACES);
1399             List<Integer> faceIds = new ArrayList<Integer>(faces.length);
1400             List<Integer> faceScores = new ArrayList<Integer>(faces.length);
1401             if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
1402                 mCollector.expectEquals("Number of detection faces should always 0 for OFF mode",
1403                         0, faces.length);
1404             } else if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
1405                 for (Face face : faces) {
1406                     mCollector.expectNotNull("Face rectangle shouldn't be null", face.getBounds());
1407                     faceScores.add(face.getScore());
1408                     mCollector.expectTrue("Face id is expected to be -1 for SIMPLE mode",
1409                             face.getId() == Face.ID_UNSUPPORTED);
1410                 }
1411             } else if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
1412                 if (VERBOSE) {
1413                     Log.v(TAG, "Number of faces detected: " + faces.length);
1414                 }
1415 
1416                 for (Face face : faces) {
1417                     Rect faceBound;
1418                     boolean faceRectAvailable =  mCollector.expectTrue("Face rectangle "
1419                             + "shouldn't be null", face.getBounds() != null);
1420                     if (!faceRectAvailable) {
1421                         continue;
1422                     }
1423                     faceBound = face.getBounds();
1424 
1425                     faceScores.add(face.getScore());
1426                     faceIds.add(face.getId());
1427 
1428                     mCollector.expectTrue("Face id is shouldn't be -1 for FULL mode",
1429                             face.getId() != Face.ID_UNSUPPORTED);
1430                     boolean leftEyeAvailable =
1431                             mCollector.expectTrue("Left eye position shouldn't be null",
1432                                     face.getLeftEyePosition() != null);
1433                     boolean rightEyeAvailable =
1434                             mCollector.expectTrue("Right eye position shouldn't be null",
1435                                     face.getRightEyePosition() != null);
1436                     boolean mouthAvailable =
1437                             mCollector.expectTrue("Mouth position shouldn't be null",
1438                             face.getMouthPosition() != null);
1439                     // Eyes/mouth position should be inside of the face rect.
1440                     if (leftEyeAvailable) {
1441                         Point leftEye = face.getLeftEyePosition();
1442                         mCollector.expectTrue("Left eye " + leftEye + "should be"
1443                                 + "inside of face rect " + faceBound,
1444                                 faceBound.contains(leftEye.x, leftEye.y));
1445                     }
1446                     if (rightEyeAvailable) {
1447                         Point rightEye = face.getRightEyePosition();
1448                         mCollector.expectTrue("Right eye " + rightEye + "should be"
1449                                 + "inside of face rect " + faceBound,
1450                                 faceBound.contains(rightEye.x, rightEye.y));
1451                     }
1452                     if (mouthAvailable) {
1453                         Point mouth = face.getMouthPosition();
1454                         mCollector.expectTrue("Mouth " + mouth +  " should be inside of"
1455                                 + " face rect " + faceBound,
1456                                 faceBound.contains(mouth.x, mouth.y));
1457                     }
1458                 }
1459             }
1460             mCollector.expectValuesInRange("Face scores are invalid", faceScores,
1461                     Face.SCORE_MIN, Face.SCORE_MAX);
1462             mCollector.expectValuesUnique("Face ids are invalid", faceIds);
1463         }
1464     }
1465 
1466     /**
1467      * Test tone map mode and result by camera
1468      */
1469     private void toneMapTestByCamera() throws Exception {
1470         if (!mStaticInfo.isManualToneMapSupported()) {
1471             return;
1472         }
1473 
1474         CaptureRequest.Builder requestBuilder =
1475                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1476         int[] toneMapModes = mStaticInfo.getAvailableToneMapModesChecked();
1477         for (int mode : toneMapModes) {
1478             if (VERBOSE) {
1479                 Log.v(TAG, "Testing tonemap mode " + mode);
1480             }
1481 
1482             requestBuilder.set(CaptureRequest.TONEMAP_MODE, mode);
1483             switch (mode) {
1484                 case CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE:
1485                     TonemapCurve toneCurve = new TonemapCurve(TONEMAP_CURVE_LINEAR,
1486                             TONEMAP_CURVE_LINEAR, TONEMAP_CURVE_LINEAR);
1487                     requestBuilder.set(CaptureRequest.TONEMAP_CURVE, toneCurve);
1488                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1489 
1490                     toneCurve = new TonemapCurve(TONEMAP_CURVE_SRGB,
1491                             TONEMAP_CURVE_SRGB, TONEMAP_CURVE_SRGB);
1492                     requestBuilder.set(CaptureRequest.TONEMAP_CURVE, toneCurve);
1493                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1494                     break;
1495                 case CaptureRequest.TONEMAP_MODE_GAMMA_VALUE:
1496                     requestBuilder.set(CaptureRequest.TONEMAP_GAMMA, 1.0f);
1497                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1498                     requestBuilder.set(CaptureRequest.TONEMAP_GAMMA, 2.2f);
1499                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1500                     requestBuilder.set(CaptureRequest.TONEMAP_GAMMA, 5.0f);
1501                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1502                     break;
1503                 case CaptureRequest.TONEMAP_MODE_PRESET_CURVE:
1504                     requestBuilder.set(CaptureRequest.TONEMAP_PRESET_CURVE,
1505                             CaptureRequest.TONEMAP_PRESET_CURVE_REC709);
1506                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1507                     requestBuilder.set(CaptureRequest.TONEMAP_PRESET_CURVE,
1508                             CaptureRequest.TONEMAP_PRESET_CURVE_SRGB);
1509                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1510                     break;
1511                 default:
1512                     testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
1513                     break;
1514             }
1515         }
1516 
1517 
1518     }
1519 
1520     /**
1521      * Test tonemap mode with speficied request settings
1522      *
1523      * @param numFramesVerified Number of results to be verified
1524      * @param requestBuilder the request builder of settings to be tested
1525      */
1526     private void testToneMapMode (int numFramesVerified,
1527             CaptureRequest.Builder requestBuilder)  throws Exception  {
1528         final int MIN_TONEMAP_CURVE_POINTS = 2;
1529         final Float ZERO = new Float(0);
1530         final Float ONE = new Float(1.0f);
1531 
1532         SimpleCaptureCallback listener = new SimpleCaptureCallback();
1533         int tonemapMode = requestBuilder.get(CaptureRequest.TONEMAP_MODE);
1534         Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
1535         startPreview(requestBuilder, maxPreviewSz, listener);
1536         waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1537 
1538         int maxCurvePoints = mStaticInfo.getMaxTonemapCurvePointChecked();
1539         for (int i = 0; i < numFramesVerified; i++) {
1540             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1541             mCollector.expectEquals("Capture result tonemap mode should match request", tonemapMode,
1542                     result.get(CaptureResult.TONEMAP_MODE));
1543             TonemapCurve tc = getValueNotNull(result, CaptureResult.TONEMAP_CURVE);
1544             int pointCount = tc.getPointCount(TonemapCurve.CHANNEL_RED);
1545             float[] mapRed = new float[pointCount * TonemapCurve.POINT_SIZE];
1546             pointCount = tc.getPointCount(TonemapCurve.CHANNEL_GREEN);
1547             float[] mapGreen = new float[pointCount * TonemapCurve.POINT_SIZE];
1548             pointCount = tc.getPointCount(TonemapCurve.CHANNEL_BLUE);
1549             float[] mapBlue = new float[pointCount * TonemapCurve.POINT_SIZE];
1550             tc.copyColorCurve(TonemapCurve.CHANNEL_RED, mapRed, 0);
1551             tc.copyColorCurve(TonemapCurve.CHANNEL_GREEN, mapGreen, 0);
1552             tc.copyColorCurve(TonemapCurve.CHANNEL_BLUE, mapBlue, 0);
1553             if (tonemapMode == CaptureResult.TONEMAP_MODE_CONTRAST_CURVE) {
1554                 /**
1555                  * TODO: need figure out a good way to measure the difference
1556                  * between request and result, as they may have different array
1557                  * size.
1558                  */
1559             } else if (tonemapMode == CaptureResult.TONEMAP_MODE_GAMMA_VALUE) {
1560                 mCollector.expectEquals("Capture result gamma value should match request",
1561                         requestBuilder.get(CaptureRequest.TONEMAP_GAMMA),
1562                         result.get(CaptureResult.TONEMAP_GAMMA));
1563             } else if (tonemapMode == CaptureResult.TONEMAP_MODE_PRESET_CURVE) {
1564                 mCollector.expectEquals("Capture result preset curve should match request",
1565                         requestBuilder.get(CaptureRequest.TONEMAP_PRESET_CURVE),
1566                         result.get(CaptureResult.TONEMAP_PRESET_CURVE));
1567             }
1568 
1569             // Tonemap curve result availability and basic sanity check for all modes.
1570             mCollector.expectValuesInRange("Tonemap curve red values are out of range",
1571                     CameraTestUtils.toObject(mapRed), /*min*/ZERO, /*max*/ONE);
1572             mCollector.expectInRange("Tonemap curve red length is out of range",
1573                     mapRed.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
1574             mCollector.expectValuesInRange("Tonemap curve green values are out of range",
1575                     CameraTestUtils.toObject(mapGreen), /*min*/ZERO, /*max*/ONE);
1576             mCollector.expectInRange("Tonemap curve green length is out of range",
1577                     mapGreen.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
1578             mCollector.expectValuesInRange("Tonemap curve blue values are out of range",
1579                     CameraTestUtils.toObject(mapBlue), /*min*/ZERO, /*max*/ONE);
1580             mCollector.expectInRange("Tonemap curve blue length is out of range",
1581                     mapBlue.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
1582         }
1583         stopPreview();
1584     }
1585 
1586     /**
1587      * Test awb mode control.
1588      * <p>
1589      * Test each supported AWB mode, verify the AWB mode in capture result
1590      * matches request. When AWB is locked, the color correction gains and
1591      * transform should remain unchanged.
1592      * </p>
1593      */
1594     private void awbModeAndLockTestByCamera() throws Exception {
1595         int[] awbModes = mStaticInfo.getAwbAvailableModesChecked();
1596         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
1597         boolean canSetAwbLock = mStaticInfo.isAwbLockSupported();
1598         CaptureRequest.Builder requestBuilder =
1599                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1600         startPreview(requestBuilder, maxPreviewSize, /*listener*/null);
1601 
1602         for (int mode : awbModes) {
1603             SimpleCaptureCallback listener;
1604             requestBuilder.set(CaptureRequest.CONTROL_AWB_MODE, mode);
1605             listener = new SimpleCaptureCallback();
1606             mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1607             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1608 
1609             // Verify AWB mode in capture result.
1610             verifyCaptureResultForKey(CaptureResult.CONTROL_AWB_MODE, mode, listener,
1611                     NUM_FRAMES_VERIFIED);
1612 
1613             if (mode == CameraMetadata.CONTROL_AWB_MODE_AUTO && canSetAwbLock) {
1614                 // Verify color correction transform and gains stay unchanged after a lock.
1615                 requestBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
1616                 listener = new SimpleCaptureCallback();
1617                 mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1618                 waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1619 
1620                 if (mStaticInfo.areKeysAvailable(CaptureResult.CONTROL_AWB_STATE)) {
1621                     waitForResultValue(listener, CaptureResult.CONTROL_AWB_STATE,
1622                             CaptureResult.CONTROL_AWB_STATE_LOCKED, NUM_RESULTS_WAIT_TIMEOUT);
1623                 }
1624 
1625             }
1626             // Don't verify auto mode result if AWB lock is not supported
1627             if (mode != CameraMetadata.CONTROL_AWB_MODE_AUTO || canSetAwbLock) {
1628                 verifyAwbCaptureResultUnchanged(listener, NUM_FRAMES_VERIFIED);
1629             }
1630         }
1631     }
1632 
1633     private void verifyAwbCaptureResultUnchanged(SimpleCaptureCallback listener,
1634             int numFramesVerified) {
1635         // Skip check if cc gains/transform/mode are not available
1636         if (!mStaticInfo.areKeysAvailable(
1637                 CaptureResult.COLOR_CORRECTION_GAINS,
1638                 CaptureResult.COLOR_CORRECTION_TRANSFORM,
1639                 CaptureResult.COLOR_CORRECTION_MODE)) {
1640             return;
1641         }
1642 
1643         CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1644         RggbChannelVector lockedGains =
1645                 getValueNotNull(result, CaptureResult.COLOR_CORRECTION_GAINS);
1646         ColorSpaceTransform lockedTransform =
1647                 getValueNotNull(result, CaptureResult.COLOR_CORRECTION_TRANSFORM);
1648 
1649         for (int i = 0; i < numFramesVerified; i++) {
1650             result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1651             // Color correction mode check is skipped here, as it is checked in colorCorrectionTest.
1652             validateColorCorrectionResult(result, result.get(CaptureResult.COLOR_CORRECTION_MODE));
1653 
1654             RggbChannelVector gains = getValueNotNull(result, CaptureResult.COLOR_CORRECTION_GAINS);
1655             ColorSpaceTransform transform =
1656                     getValueNotNull(result, CaptureResult.COLOR_CORRECTION_TRANSFORM);
1657             mCollector.expectEquals("Color correction gains should remain unchanged after awb lock",
1658                     lockedGains, gains);
1659             mCollector.expectEquals("Color correction transform should remain unchanged after"
1660                     + " awb lock", lockedTransform, transform);
1661         }
1662     }
1663 
1664     /**
1665      * Test AF mode control.
1666      * <p>
1667      * Test all supported AF modes, verify the AF mode in capture result matches
1668      * request. When AF mode is one of the CONTROL_AF_MODE_CONTINUOUS_* mode,
1669      * verify if the AF can converge to PASSIVE_FOCUSED or PASSIVE_UNFOCUSED
1670      * state within certain amount of frames.
1671      * </p>
1672      */
1673     private void afModeTestByCamera() throws Exception {
1674         int[] afModes = mStaticInfo.getAfAvailableModesChecked();
1675         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
1676         CaptureRequest.Builder requestBuilder =
1677                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1678         startPreview(requestBuilder, maxPreviewSize, /*listener*/null);
1679 
1680         for (int mode : afModes) {
1681             SimpleCaptureCallback listener;
1682             requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mode);
1683             listener = new SimpleCaptureCallback();
1684             mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1685             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1686 
1687             // Verify AF mode in capture result.
1688             verifyCaptureResultForKey(CaptureResult.CONTROL_AF_MODE, mode, listener,
1689                     NUM_FRAMES_VERIFIED);
1690 
1691             // Verify AF can finish a scan for CONTROL_AF_MODE_CONTINUOUS_* modes.
1692             // In LEGACY mode, a transition to one of the continuous AF modes does not necessarily
1693             // result in a passive AF call if the camera has already been focused, and the scene has
1694             // not changed enough to trigger an AF pass.  Skip this constraint for LEGACY.
1695             if (mStaticInfo.isHardwareLevelLimitedOrBetter() &&
1696                     (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE ||
1697                     mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
1698                 List<Integer> afStateList = new ArrayList<Integer>();
1699                 afStateList.add(CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED);
1700                 afStateList.add(CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED);
1701                 waitForAnyResultValue(listener, CaptureResult.CONTROL_AF_STATE, afStateList,
1702                         NUM_RESULTS_WAIT_TIMEOUT);
1703             }
1704         }
1705     }
1706 
1707     /**
1708      * Test video and optical stabilizations if they are supported by a given camera.
1709      */
1710     private void stabilizationTestByCamera() throws Exception {
1711         // video stabilization test.
1712         List<Key<?>> keys = mStaticInfo.getCharacteristics().getKeys();
1713 
1714         int[] videoStabModes = (keys.contains(CameraCharacteristics.
1715                 CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)) ?
1716                 mStaticInfo.getAvailableVideoStabilizationModesChecked() : new int[0];
1717         int[] opticalStabModes = (keys.contains(
1718                 CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION)) ?
1719                 mStaticInfo.getAvailableOpticalStabilizationChecked() : new int[0];
1720 
1721         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
1722         CaptureRequest.Builder requestBuilder =
1723                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1724         SimpleCaptureCallback listener = new SimpleCaptureCallback();
1725         startPreview(requestBuilder, maxPreviewSize, listener);
1726 
1727         for (int mode : videoStabModes) {
1728             listener = new SimpleCaptureCallback();
1729             requestBuilder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, mode);
1730             mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1731             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1732             verifyCaptureResultForKey(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE, mode,
1733                     listener, NUM_FRAMES_VERIFIED);
1734         }
1735 
1736         for (int mode : opticalStabModes) {
1737             listener = new SimpleCaptureCallback();
1738             requestBuilder.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, mode);
1739             mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1740             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1741             verifyCaptureResultForKey(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE, mode,
1742                     listener, NUM_FRAMES_VERIFIED);
1743         }
1744 
1745         stopPreview();
1746     }
1747 
1748     private void digitalZoomTestByCamera(Size previewSize) throws Exception {
1749         final int ZOOM_STEPS = 15;
1750         final PointF[] TEST_ZOOM_CENTERS;
1751 
1752         final int croppingType = mStaticInfo.getScalerCroppingTypeChecked();
1753         if (croppingType ==
1754                 CameraCharacteristics.SCALER_CROPPING_TYPE_FREEFORM) {
1755             TEST_ZOOM_CENTERS = new PointF[] {
1756                 new PointF(0.5f, 0.5f),   // Center point
1757                 new PointF(0.25f, 0.25f), // top left corner zoom, minimal zoom: 2x
1758                 new PointF(0.75f, 0.25f), // top right corner zoom, minimal zoom: 2x
1759                 new PointF(0.25f, 0.75f), // bottom left corner zoom, minimal zoom: 2x
1760                 new PointF(0.75f, 0.75f), // bottom right corner zoom, minimal zoom: 2x
1761             };
1762 
1763             if (VERBOSE) {
1764                 Log.v(TAG, "Testing zoom with CROPPING_TYPE = FREEFORM");
1765             }
1766         } else {
1767             // CENTER_ONLY
1768             TEST_ZOOM_CENTERS = new PointF[] {
1769                     new PointF(0.5f, 0.5f),   // Center point
1770             };
1771 
1772             if (VERBOSE) {
1773                 Log.v(TAG, "Testing zoom with CROPPING_TYPE = CENTER_ONLY");
1774             }
1775         }
1776 
1777         final float maxZoom = mStaticInfo.getAvailableMaxDigitalZoomChecked();
1778         final Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
1779         Rect[] cropRegions = new Rect[ZOOM_STEPS];
1780         MeteringRectangle[][] expectRegions = new MeteringRectangle[ZOOM_STEPS][];
1781         CaptureRequest.Builder requestBuilder =
1782                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1783         SimpleCaptureCallback listener = new SimpleCaptureCallback();
1784 
1785         updatePreviewSurface(previewSize);
1786         configurePreviewOutput(requestBuilder);
1787 
1788         CaptureRequest[] requests = new CaptureRequest[ZOOM_STEPS];
1789 
1790         // Set algorithm regions to full active region
1791         // TODO: test more different 3A regions
1792         final MeteringRectangle[] defaultMeteringRect = new MeteringRectangle[] {
1793                 new MeteringRectangle (
1794                         /*x*/0, /*y*/0, activeArraySize.width(), activeArraySize.height(),
1795                         /*meteringWeight*/1)
1796         };
1797 
1798         for (int algo = 0; algo < NUM_ALGORITHMS; algo++) {
1799             update3aRegion(requestBuilder, algo,  defaultMeteringRect);
1800         }
1801 
1802         final int CAPTURE_SUBMIT_REPEAT;
1803         {
1804             int maxLatency = mStaticInfo.getSyncMaxLatency();
1805             if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
1806                 CAPTURE_SUBMIT_REPEAT = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY + 1;
1807             } else {
1808                 CAPTURE_SUBMIT_REPEAT = maxLatency + 1;
1809             }
1810         }
1811 
1812         if (VERBOSE) {
1813             Log.v(TAG, "Testing zoom with CAPTURE_SUBMIT_REPEAT = " + CAPTURE_SUBMIT_REPEAT);
1814         }
1815 
1816         for (PointF center : TEST_ZOOM_CENTERS) {
1817             Rect previousCrop = null;
1818 
1819             for (int i = 0; i < ZOOM_STEPS; i++) {
1820                 /*
1821                  * Submit capture request
1822                  */
1823                 float zoomFactor = (float) (1.0f + (maxZoom - 1.0) * i / ZOOM_STEPS);
1824                 cropRegions[i] = getCropRegionForZoom(zoomFactor, center, maxZoom, activeArraySize);
1825                 if (VERBOSE) {
1826                     Log.v(TAG, "Testing Zoom for factor " + zoomFactor + " and center " +
1827                             center + " The cropRegion is " + cropRegions[i] +
1828                             " Preview size is " + previewSize);
1829                 }
1830                 requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRegions[i]);
1831                 requests[i] = requestBuilder.build();
1832                 for (int j = 0; j < CAPTURE_SUBMIT_REPEAT; ++j) {
1833                     if (VERBOSE) {
1834                         Log.v(TAG, "submit crop region " + cropRegions[i]);
1835                     }
1836                     mSession.capture(requests[i], listener, mHandler);
1837                 }
1838 
1839                 /*
1840                  * Validate capture result
1841                  */
1842                 waitForNumResults(listener, CAPTURE_SUBMIT_REPEAT - 1); // Drop first few frames
1843                 CaptureResult result = listener.getCaptureResultForRequest(
1844                         requests[i], NUM_RESULTS_WAIT_TIMEOUT);
1845                 Rect cropRegion = getValueNotNull(result, CaptureResult.SCALER_CROP_REGION);
1846 
1847                 /*
1848                  * Validate resulting crop regions
1849                  */
1850                 if (previousCrop != null) {
1851                     Rect currentCrop = cropRegion;
1852                     mCollector.expectTrue(String.format(
1853                             "Crop region should shrink or stay the same " +
1854                                     "(previous = %s, current = %s)",
1855                                     previousCrop, currentCrop),
1856                             previousCrop.equals(currentCrop) ||
1857                                 (previousCrop.width() > currentCrop.width() &&
1858                                  previousCrop.height() > currentCrop.height()));
1859                 }
1860 
1861                 if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
1862                     mCollector.expectRectsAreSimilar(
1863                             "Request and result crop region should be similar",
1864                             cropRegions[i], cropRegion, CROP_REGION_ERROR_PERCENT_DELTA);
1865                 }
1866 
1867                 if (croppingType == SCALER_CROPPING_TYPE_CENTER_ONLY) {
1868                     mCollector.expectRectCentered(
1869                             "Result crop region should be centered inside the active array",
1870                             new Size(activeArraySize.width(), activeArraySize.height()),
1871                             cropRegion, CROP_REGION_ERROR_PERCENT_CENTERED);
1872                 }
1873 
1874                 /*
1875                  * Validate resulting metering regions
1876                  */
1877 
1878                 // Use the actual reported crop region to calculate the resulting metering region
1879                 expectRegions[i] = getExpectedOutputRegion(
1880                         /*requestRegion*/defaultMeteringRect,
1881                         /*cropRect*/     cropRegion);
1882 
1883                 // Verify Output 3A region is intersection of input 3A region and crop region
1884                 for (int algo = 0; algo < NUM_ALGORITHMS; algo++) {
1885                     validate3aRegion(result, algo, expectRegions[i]);
1886                 }
1887 
1888                 previousCrop = cropRegion;
1889             }
1890 
1891             if (maxZoom > 1.0f) {
1892                 mCollector.expectTrue(
1893                         String.format("Most zoomed-in crop region should be smaller" +
1894                                         "than active array w/h" +
1895                                         "(last crop = %s, active array = %s)",
1896                                         previousCrop, activeArraySize),
1897                             (previousCrop.width() < activeArraySize.width() &&
1898                              previousCrop.height() < activeArraySize.height()));
1899             }
1900         }
1901     }
1902 
1903     private void digitalZoomPreviewCombinationTestByCamera() throws Exception {
1904         final double ASPECT_RATIO_THRESHOLD = 0.001;
1905         List<Double> aspectRatiosTested = new ArrayList<Double>();
1906         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
1907         aspectRatiosTested.add((double)(maxPreviewSize.getWidth()) / maxPreviewSize.getHeight());
1908 
1909         for (Size size : mOrderedPreviewSizes) {
1910             // Max preview size was already tested in testDigitalZoom test. skip it.
1911             if (size.equals(maxPreviewSize)) {
1912                 continue;
1913             }
1914 
1915             // Only test the largest size for each aspect ratio.
1916             double aspectRatio = (double)(size.getWidth()) / size.getHeight();
1917             if (isAspectRatioContained(aspectRatiosTested, aspectRatio, ASPECT_RATIO_THRESHOLD)) {
1918                 continue;
1919             }
1920 
1921             if (VERBOSE) {
1922                 Log.v(TAG, "Test preview size " + size.toString() + " digital zoom");
1923             }
1924 
1925             aspectRatiosTested.add(aspectRatio);
1926             digitalZoomTestByCamera(size);
1927         }
1928     }
1929 
1930     private static boolean isAspectRatioContained(List<Double> aspectRatioList,
1931             double aspectRatio, double delta) {
1932         for (Double ratio : aspectRatioList) {
1933             if (Math.abs(ratio - aspectRatio) < delta) {
1934                 return true;
1935             }
1936         }
1937 
1938         return false;
1939     }
1940 
1941     private void sceneModeTestByCamera() throws Exception {
1942         int[] sceneModes = mStaticInfo.getAvailableSceneModesChecked();
1943         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
1944         CaptureRequest.Builder requestBuilder =
1945                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1946         SimpleCaptureCallback listener = new SimpleCaptureCallback();
1947         requestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
1948         startPreview(requestBuilder, maxPreviewSize, listener);
1949 
1950         for(int mode : sceneModes) {
1951             requestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, mode);
1952             listener = new SimpleCaptureCallback();
1953             mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1954             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1955 
1956             verifyCaptureResultForKey(CaptureResult.CONTROL_SCENE_MODE,
1957                     mode, listener, NUM_FRAMES_VERIFIED);
1958             // This also serves as purpose of showing preview for NUM_FRAMES_VERIFIED
1959             verifyCaptureResultForKey(CaptureResult.CONTROL_MODE,
1960                     CaptureRequest.CONTROL_MODE_USE_SCENE_MODE, listener, NUM_FRAMES_VERIFIED);
1961         }
1962     }
1963 
1964     private void effectModeTestByCamera() throws Exception {
1965         int[] effectModes = mStaticInfo.getAvailableEffectModesChecked();
1966         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
1967         CaptureRequest.Builder requestBuilder =
1968                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1969         requestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
1970         SimpleCaptureCallback listener = new SimpleCaptureCallback();
1971         startPreview(requestBuilder, maxPreviewSize, listener);
1972 
1973         for(int mode : effectModes) {
1974             requestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, mode);
1975             listener = new SimpleCaptureCallback();
1976             mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
1977             waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1978 
1979             verifyCaptureResultForKey(CaptureResult.CONTROL_EFFECT_MODE,
1980                     mode, listener, NUM_FRAMES_VERIFIED);
1981             // This also serves as purpose of showing preview for NUM_FRAMES_VERIFIED
1982             verifyCaptureResultForKey(CaptureResult.CONTROL_MODE,
1983                     CaptureRequest.CONTROL_MODE_AUTO, listener, NUM_FRAMES_VERIFIED);
1984         }
1985     }
1986 
1987     //----------------------------------------------------------------
1988     //---------Below are common functions for all tests.--------------
1989     //----------------------------------------------------------------
1990 
1991     /**
1992      * Enable exposure manual control and change exposure and sensitivity and
1993      * clamp the value into the supported range.
1994      */
1995     private void changeExposure(CaptureRequest.Builder requestBuilder,
1996             long expTime, int sensitivity) {
1997         // Check if the max analog sensitivity is available and no larger than max sensitivity.
1998         // The max analog sensitivity is not actually used here. This is only an extra sanity check.
1999         mStaticInfo.getMaxAnalogSensitivityChecked();
2000 
2001         expTime = mStaticInfo.getExposureClampToRange(expTime);
2002         sensitivity = mStaticInfo.getSensitivityClampToRange(sensitivity);
2003 
2004         requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
2005         requestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, expTime);
2006         requestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, sensitivity);
2007     }
2008     /**
2009      * Enable exposure manual control and change exposure time and
2010      * clamp the value into the supported range.
2011      *
2012      * <p>The sensitivity is set to default value.</p>
2013      */
2014     private void changeExposure(CaptureRequest.Builder requestBuilder, long expTime) {
2015         changeExposure(requestBuilder, expTime, DEFAULT_SENSITIVITY);
2016     }
2017 
2018     /**
2019      * Get the exposure time array that contains multiple exposure time steps in
2020      * the exposure time range.
2021      */
2022     private long[] getExposureTimeTestValues() {
2023         long[] testValues = new long[DEFAULT_NUM_EXPOSURE_TIME_STEPS + 1];
2024         long maxExpTime = mStaticInfo.getExposureMaximumOrDefault(DEFAULT_EXP_TIME_NS);
2025         long minExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
2026 
2027         long range = maxExpTime - minExpTime;
2028         double stepSize = range / (double)DEFAULT_NUM_EXPOSURE_TIME_STEPS;
2029         for (int i = 0; i < testValues.length; i++) {
2030             testValues[i] = maxExpTime - (long)(stepSize * i);
2031             testValues[i] = mStaticInfo.getExposureClampToRange(testValues[i]);
2032         }
2033 
2034         return testValues;
2035     }
2036 
2037     /**
2038      * Generate test focus distances in range of [0, minFocusDistance] in increasing order.
2039      */
2040     private float[] getFocusDistanceTestValuesInOrder() {
2041         float[] testValues = new float[NUM_TEST_FOCUS_DISTANCES + 1];
2042         float minValue = 0;
2043         float maxValue = mStaticInfo.getMinimumFocusDistanceChecked();
2044 
2045         float range = maxValue - minValue;
2046         float stepSize = range / NUM_TEST_FOCUS_DISTANCES;
2047         for (int i = 0; i < testValues.length; i++) {
2048             testValues[i] = minValue + stepSize * i;
2049         }
2050 
2051         return testValues;
2052     }
2053 
2054     /**
2055      * Get the sensitivity array that contains multiple sensitivity steps in the
2056      * sensitivity range.
2057      * <p>
2058      * Sensitivity number of test values is determined by
2059      * {@value #DEFAULT_SENSITIVITY_STEP_SIZE} and sensitivity range, and
2060      * bounded by {@value #DEFAULT_NUM_SENSITIVITY_STEPS}.
2061      * </p>
2062      */
2063     private int[] getSensitivityTestValues() {
2064         int maxSensitivity = mStaticInfo.getSensitivityMaximumOrDefault(
2065                 DEFAULT_SENSITIVITY);
2066         int minSensitivity = mStaticInfo.getSensitivityMinimumOrDefault(
2067                 DEFAULT_SENSITIVITY);
2068 
2069         int range = maxSensitivity - minSensitivity;
2070         int stepSize = DEFAULT_SENSITIVITY_STEP_SIZE;
2071         int numSteps = range / stepSize;
2072         // Bound the test steps to avoid supper long test.
2073         if (numSteps > DEFAULT_NUM_SENSITIVITY_STEPS) {
2074             numSteps = DEFAULT_NUM_SENSITIVITY_STEPS;
2075             stepSize = range / numSteps;
2076         }
2077         int[] testValues = new int[numSteps + 1];
2078         for (int i = 0; i < testValues.length; i++) {
2079             testValues[i] = maxSensitivity - stepSize * i;
2080             testValues[i] = mStaticInfo.getSensitivityClampToRange(testValues[i]);
2081         }
2082 
2083         return testValues;
2084     }
2085 
2086     /**
2087      * Validate the AE manual control exposure time.
2088      *
2089      * <p>Exposure should be close enough, and only round down if they are not equal.</p>
2090      *
2091      * @param request Request exposure time
2092      * @param result Result exposure time
2093      */
2094     private void validateExposureTime(long request, long result) {
2095         long expTimeDelta = request - result;
2096         // First, round down not up, second, need close enough.
2097         mCollector.expectTrue("Exposture time is invalid for AE manaul control test, request: "
2098                 + request + " result: " + result,
2099                 expTimeDelta < EXPOSURE_TIME_ERROR_MARGIN_NS && expTimeDelta >= 0);
2100     }
2101 
2102     /**
2103      * Validate AE manual control sensitivity.
2104      *
2105      * @param request Request sensitivity
2106      * @param result Result sensitivity
2107      */
2108     private void validateSensitivity(int request, int result) {
2109         int sensitivityDelta = request - result;
2110         // First, round down not up, second, need close enough.
2111         mCollector.expectTrue("Sensitivity is invalid for AE manaul control test, request: "
2112                 + request + " result: " + result,
2113                 sensitivityDelta < SENSITIVITY_ERROR_MARGIN && sensitivityDelta >= 0);
2114     }
2115 
2116     /**
2117      * Validate frame duration for a given capture.
2118      *
2119      * <p>Frame duration should be longer than exposure time.</p>
2120      *
2121      * @param result The capture result for a given capture
2122      */
2123     private void validateFrameDurationForCapture(CaptureResult result) {
2124         long expTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
2125         long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
2126         if (VERBOSE) {
2127             Log.v(TAG, "frame duration: " + frameDuration + " Exposure time: " + expTime);
2128         }
2129 
2130         mCollector.expectTrue(String.format("Frame duration (%d) should be longer than exposure"
2131                 + " time (%d) for a given capture", frameDuration, expTime),
2132                 frameDuration >= expTime);
2133 
2134         validatePipelineDepth(result);
2135     }
2136 
2137     /**
2138      * Basic verification for the control mode capture result.
2139      *
2140      * @param key The capture result key to be verified against
2141      * @param requestMode The request mode for this result
2142      * @param listener The capture listener to get capture results
2143      * @param numFramesVerified The number of capture results to be verified
2144      */
2145     private <T> void verifyCaptureResultForKey(CaptureResult.Key<T> key, T requestMode,
2146             SimpleCaptureCallback listener, int numFramesVerified) {
2147         for (int i = 0; i < numFramesVerified; i++) {
2148             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
2149             validatePipelineDepth(result);
2150             T resultMode = getValueNotNull(result, key);
2151             if (VERBOSE) {
2152                 Log.v(TAG, "Expect value: " + requestMode.toString() + " result value: "
2153                         + resultMode.toString());
2154             }
2155             mCollector.expectEquals("Key " + key.getName() + " result should match request",
2156                     requestMode, resultMode);
2157         }
2158     }
2159 
2160     /**
2161      * Verify if the fps is slow down for given input request with certain
2162      * controls inside.
2163      * <p>
2164      * This method selects a max preview size for each fps range, and then
2165      * configure the preview stream. Preview is started with the max preview
2166      * size, and then verify if the result frame duration is in the frame
2167      * duration range.
2168      * </p>
2169      *
2170      * @param requestBuilder The request builder that contains post-processing
2171      *            controls that could impact the output frame rate, such as
2172      *            {@link CaptureRequest.NOISE_REDUCTION_MODE}. The value of
2173      *            these controls must be set to some values such that the frame
2174      *            rate is not slow down.
2175      * @param numFramesVerified The number of frames to be verified
2176      */
2177     private void verifyFpsNotSlowDown(CaptureRequest.Builder requestBuilder,
2178             int numFramesVerified)  throws Exception {
2179         boolean frameDurationAvailable = true;
2180         // Allow a few frames for AE to settle on target FPS range
2181         final int NUM_FRAME_TO_SKIP = 6;
2182         float frameDurationErrorMargin = FRAME_DURATION_ERROR_MARGIN;
2183         if (!mStaticInfo.areKeysAvailable(CaptureResult.SENSOR_FRAME_DURATION)) {
2184             frameDurationAvailable = false;
2185             // Allow a larger error margin (1.5%) for timestamps
2186             frameDurationErrorMargin = 0.015f;
2187         }
2188 
2189         Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
2190         boolean antiBandingOffIsSupported = mStaticInfo.isAntiBandingOffModeSupported();
2191         Range<Integer> fpsRange;
2192         SimpleCaptureCallback resultListener;
2193 
2194         for (int i = 0; i < fpsRanges.length; i += 1) {
2195             fpsRange = fpsRanges[i];
2196             Size previewSz = getMaxPreviewSizeForFpsRange(fpsRange);
2197             // If unable to find a preview size, then log the failure, and skip this run.
2198             if (previewSz == null) {
2199                 if (mStaticInfo.isCapabilitySupported(
2200                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
2201                     mCollector.addMessage(String.format(
2202                             "Unable to find a preview size supporting given fps range %s",
2203                             fpsRange));
2204                 }
2205                 continue;
2206             }
2207 
2208             if (VERBOSE) {
2209                 Log.v(TAG, String.format("Test fps range %s for preview size %s",
2210                         fpsRange, previewSz.toString()));
2211             }
2212             requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
2213             // Turn off auto antibanding to avoid exposure time and frame duration interference
2214             // from antibanding algorithm.
2215             if (antiBandingOffIsSupported) {
2216                 requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
2217                         CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
2218             } else {
2219                 // The device doesn't implement the OFF mode, test continues. It need make sure
2220                 // that the antibanding algorithm doesn't slow down the fps.
2221                 Log.i(TAG, "OFF antibanding mode is not supported, the camera device output must" +
2222                         " not slow down the frame rate regardless of its current antibanding" +
2223                         " mode");
2224             }
2225 
2226             resultListener = new SimpleCaptureCallback();
2227             startPreview(requestBuilder, previewSz, resultListener);
2228             waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
2229             // Wait several more frames for AE to settle on target FPS range
2230             waitForNumResults(resultListener, NUM_FRAME_TO_SKIP);
2231 
2232             long[] frameDurationRange = new long[]{
2233                     (long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
2234             long captureTime = 0, prevCaptureTime = 0;
2235             for (int j = 0; j < numFramesVerified; j++) {
2236                 long frameDuration = frameDurationRange[0];
2237                 CaptureResult result =
2238                         resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
2239                 validatePipelineDepth(result);
2240                 if (frameDurationAvailable) {
2241                     frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
2242                 } else {
2243                     // if frame duration is not available, check timestamp instead
2244                     captureTime = getValueNotNull(result, CaptureResult.SENSOR_TIMESTAMP);
2245                     if (j > 0) {
2246                         frameDuration = captureTime - prevCaptureTime;
2247                     }
2248                     prevCaptureTime = captureTime;
2249                 }
2250                 mCollector.expectInRange(
2251                         "Frame duration must be in the range of " +
2252                                 Arrays.toString(frameDurationRange),
2253                         frameDuration,
2254                         (long) (frameDurationRange[0] * (1 - frameDurationErrorMargin)),
2255                         (long) (frameDurationRange[1] * (1 + frameDurationErrorMargin)));
2256             }
2257         }
2258 
2259         mSession.stopRepeating();
2260     }
2261 
2262     /**
2263      * Validate the pipeline depth result.
2264      *
2265      * @param result The capture result to get pipeline depth data
2266      */
2267     private void validatePipelineDepth(CaptureResult result) {
2268         final byte MIN_PIPELINE_DEPTH = 1;
2269         byte maxPipelineDepth = mStaticInfo.getPipelineMaxDepthChecked();
2270         Byte pipelineDepth = getValueNotNull(result, CaptureResult.REQUEST_PIPELINE_DEPTH);
2271         mCollector.expectInRange(String.format("Pipeline depth must be in the range of [%d, %d]",
2272                 MIN_PIPELINE_DEPTH, maxPipelineDepth), pipelineDepth, MIN_PIPELINE_DEPTH,
2273                 maxPipelineDepth);
2274     }
2275 
2276     /**
2277      * Calculate the anti-flickering corrected exposure time.
2278      * <p>
2279      * If the input exposure time is very short (shorter than flickering
2280      * boundary), which indicate the scene is bright and very likely at outdoor
2281      * environment, skip the correction, as it doesn't make much sense by doing so.
2282      * </p>
2283      * <p>
2284      * For long exposure time (larger than the flickering boundary), find the
2285      * exposure time that is closest to the flickering boundary.
2286      * </p>
2287      *
2288      * @param flickeringMode The flickering mode
2289      * @param exposureTime The input exposureTime to be corrected
2290      * @return anti-flickering corrected exposure time
2291      */
2292     private long getAntiFlickeringExposureTime(int flickeringMode, long exposureTime) {
2293         if (flickeringMode != ANTI_FLICKERING_50HZ && flickeringMode != ANTI_FLICKERING_60HZ) {
2294             throw new IllegalArgumentException("Input anti-flickering mode must be 50 or 60Hz");
2295         }
2296         long flickeringBoundary = EXPOSURE_TIME_BOUNDARY_50HZ_NS;
2297         if (flickeringMode == ANTI_FLICKERING_60HZ) {
2298             flickeringBoundary = EXPOSURE_TIME_BOUNDARY_60HZ_NS;
2299         }
2300 
2301         if (exposureTime <= flickeringBoundary) {
2302             return exposureTime;
2303         }
2304 
2305         // Find the closest anti-flickering corrected exposure time
2306         long correctedExpTime = exposureTime + (flickeringBoundary / 2);
2307         correctedExpTime = correctedExpTime - (correctedExpTime % flickeringBoundary);
2308         return correctedExpTime;
2309     }
2310 
2311     /**
2312      * Update one 3A region in capture request builder if that region is supported. Do nothing
2313      * if the specified 3A region is not supported by camera device.
2314      * @param requestBuilder The request to be updated
2315      * @param algoIdx The index to the algorithm. (AE: 0, AWB: 1, AF: 2)
2316      * @param regions The 3A regions to be set
2317      */
2318     private void update3aRegion(
2319             CaptureRequest.Builder requestBuilder, int algoIdx, MeteringRectangle[] regions)
2320     {
2321         int maxRegions;
2322         CaptureRequest.Key<MeteringRectangle[]> key;
2323 
2324         if (regions == null || regions.length == 0) {
2325             throw new IllegalArgumentException("Invalid input 3A region!");
2326         }
2327 
2328         switch (algoIdx) {
2329             case INDEX_ALGORITHM_AE:
2330                 maxRegions = mStaticInfo.getAeMaxRegionsChecked();
2331                 key = CaptureRequest.CONTROL_AE_REGIONS;
2332                 break;
2333             case INDEX_ALGORITHM_AWB:
2334                 maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
2335                 key = CaptureRequest.CONTROL_AWB_REGIONS;
2336                 break;
2337             case INDEX_ALGORITHM_AF:
2338                 maxRegions = mStaticInfo.getAfMaxRegionsChecked();
2339                 key = CaptureRequest.CONTROL_AF_REGIONS;
2340                 break;
2341             default:
2342                 throw new IllegalArgumentException("Unknown 3A Algorithm!");
2343         }
2344 
2345         if (maxRegions >= regions.length) {
2346             requestBuilder.set(key, regions);
2347         }
2348     }
2349 
2350     /**
2351      * Validate one 3A region in capture result equals to expected region if that region is
2352      * supported. Do nothing if the specified 3A region is not supported by camera device.
2353      * @param result The capture result to be validated
2354      * @param algoIdx The index to the algorithm. (AE: 0, AWB: 1, AF: 2)
2355      * @param expectRegions The 3A regions expected in capture result
2356      */
2357     private void validate3aRegion(
2358             CaptureResult result, int algoIdx, MeteringRectangle[] expectRegions)
2359     {
2360         int maxRegions;
2361         CaptureResult.Key<MeteringRectangle[]> key;
2362         MeteringRectangle[] actualRegion;
2363 
2364         switch (algoIdx) {
2365             case INDEX_ALGORITHM_AE:
2366                 maxRegions = mStaticInfo.getAeMaxRegionsChecked();
2367                 key = CaptureResult.CONTROL_AE_REGIONS;
2368                 break;
2369             case INDEX_ALGORITHM_AWB:
2370                 maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
2371                 key = CaptureResult.CONTROL_AWB_REGIONS;
2372                 break;
2373             case INDEX_ALGORITHM_AF:
2374                 maxRegions = mStaticInfo.getAfMaxRegionsChecked();
2375                 key = CaptureResult.CONTROL_AF_REGIONS;
2376                 break;
2377             default:
2378                 throw new IllegalArgumentException("Unknown 3A Algorithm!");
2379         }
2380 
2381         if (maxRegions > 0)
2382         {
2383             actualRegion = getValueNotNull(result, key);
2384             mCollector.expectEquals(
2385                     "Expected 3A regions: " + Arrays.toString(expectRegions) +
2386                     " does not match actual one: " + Arrays.toString(actualRegion),
2387                     expectRegions, actualRegion);
2388         }
2389     }
2390 }
2391