1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.cts;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.*;
20 import static android.hardware.camera2.cts.helpers.AssertHelpers.assertArrayContains;
21 
22 import android.graphics.Bitmap;
23 import android.graphics.Bitmap.Config;
24 import android.graphics.ImageFormat;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.hardware.camera2.CameraCharacteristics;
28 import android.hardware.camera2.CameraDevice;
29 import android.hardware.camera2.CaptureRequest;
30 import android.hardware.camera2.CaptureResult;
31 import android.hardware.camera2.DngCreator;
32 import android.media.ImageReader;
33 import android.util.Pair;
34 import android.util.Size;
35 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
36 import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
37 import android.hardware.camera2.cts.helpers.Camera2Focuser;
38 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
39 import android.hardware.camera2.params.MeteringRectangle;
40 import android.media.Image;
41 import android.os.ConditionVariable;
42 import android.util.Log;
43 import android.util.Range;
44 import android.util.Rational;
45 import android.view.Surface;
46 
47 import com.android.ex.camera2.blocking.BlockingSessionCallback;
48 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
49 
50 import java.io.ByteArrayOutputStream;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.List;
54 
55 public class StillCaptureTest extends Camera2SurfaceViewTestCase {
56     private static final String TAG = "StillCaptureTest";
57     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
58     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
59     // 60 second to accommodate the possible long exposure time.
60     private static final int RELAXED_CAPTURE_IMAGE_TIMEOUT_MS = CAPTURE_IMAGE_TIMEOUT_MS + 1000;
61     private static final int MAX_REGIONS_AE_INDEX = 0;
62     private static final int MAX_REGIONS_AWB_INDEX = 1;
63     private static final int MAX_REGIONS_AF_INDEX = 2;
64     private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
65     private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
66     private static final int NUM_FRAMES_WAITED = 30;
67     // 5 percent error margin for resulting metering regions
68     private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
69     // Android CDD (5.0 and newer) required number of simultenous bitmap allocations for camera
70     private static final int MAX_ALLOCATED_BITMAPS = 3;
71 
72     @Override
setUp()73     protected void setUp() throws Exception {
74         super.setUp();
75     }
76 
77     @Override
tearDown()78     protected void tearDown() throws Exception {
79         super.tearDown();
80     }
81 
82     /**
83      * Test JPEG capture exif fields for each camera.
84      */
testJpegExif()85     public void testJpegExif() throws Exception {
86         for (int i = 0; i < mCameraIds.length; i++) {
87             try {
88                 Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIds[i]);
89                 openDevice(mCameraIds[i]);
90                 if (!mStaticInfo.isColorOutputSupported()) {
91                     Log.i(TAG, "Camera " + mCameraIds[i] +
92                             " does not support color outputs, skipping");
93                     continue;
94                 }
95                 jpegExifTestByCamera();
96             } finally {
97                 closeDevice();
98                 closeImageReader();
99             }
100         }
101     }
102 
103     /**
104      * Test normal still capture sequence.
105      * <p>
106      * Preview and and jpeg output streams are configured. Max still capture
107      * size is used for jpeg capture. The sequence of still capture being test
108      * is: start preview, auto focus, precapture metering (if AE is not
109      * converged), then capture jpeg. The AWB and AE are in auto modes. AF mode
110      * is CONTINUOUS_PICTURE.
111      * </p>
112      */
testTakePicture()113     public void testTakePicture() throws Exception{
114         for (String id : mCameraIds) {
115             try {
116                 Log.i(TAG, "Testing basic take picture for Camera " + id);
117                 openDevice(id);
118                 if (!mStaticInfo.isColorOutputSupported()) {
119                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
120                     continue;
121                 }
122                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null);
123             } finally {
124                 closeDevice();
125                 closeImageReader();
126             }
127         }
128     }
129 
130     /**
131      * Test basic Raw capture. Raw buffer avaiablility is checked, but raw buffer data is not.
132      */
testBasicRawCapture()133     public void testBasicRawCapture()  throws Exception {
134        for (int i = 0; i < mCameraIds.length; i++) {
135            try {
136                Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
137                openDevice(mCameraIds[i]);
138 
139                if (!mStaticInfo.isCapabilitySupported(
140                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
141                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
142                            ". Skip the test.");
143                    continue;
144                }
145 
146                rawCaptureTestByCamera();
147            } finally {
148                closeDevice();
149                closeImageReader();
150            }
151        }
152     }
153 
154 
155     /**
156      * Test the full raw capture use case.
157      *
158      * This includes:
159      * - Configuring the camera with a preview, jpeg, and raw output stream.
160      * - Running preview until AE/AF can settle.
161      * - Capturing with a request targeting all three output streams.
162      */
testFullRawCapture()163     public void testFullRawCapture() throws Exception {
164         for (int i = 0; i < mCameraIds.length; i++) {
165             try {
166                 Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
167                 openDevice(mCameraIds[i]);
168                 if (!mStaticInfo.isCapabilitySupported(
169                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
170                     Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
171                             ". Skip the test.");
172                     continue;
173                 }
174 
175                 fullRawCaptureTestByCamera();
176             } finally {
177                 closeDevice();
178                 closeImageReader();
179             }
180         }
181     }
182     /**
183      * Test touch for focus.
184      * <p>
185      * AF is in CAF mode when preview is started, test uses several pre-selected
186      * regions to simulate touches. Active scan is triggered to make sure the AF
187      * converges in reasonable time.
188      * </p>
189      */
testTouchForFocus()190     public void testTouchForFocus() throws Exception {
191         for (String id : mCameraIds) {
192             try {
193                 Log.i(TAG, "Testing touch for focus for Camera " + id);
194                 openDevice(id);
195                 int maxAfRegions = mStaticInfo.getAfMaxRegionsChecked();
196                 if (!(mStaticInfo.hasFocuser() && maxAfRegions > 0)) {
197                     continue;
198                 }
199                 // TODO: Relax test to use non-SurfaceView output for depth cases
200                 if (!mStaticInfo.isColorOutputSupported()) {
201                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
202                     continue;
203                 }
204                 touchForFocusTestByCamera();
205             } finally {
206                 closeDevice();
207                 closeImageReader();
208             }
209         }
210     }
211 
212     /**
213      * Test all combination of available preview sizes and still sizes.
214      * <p>
215      * For each still capture, Only the jpeg buffer is validated, capture
216      * result validation is covered by {@link #jpegExifTestByCamera} test.
217      * </p>
218      */
testStillPreviewCombination()219     public void testStillPreviewCombination() throws Exception {
220         for (String id : mCameraIds) {
221             try {
222                 Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
223                 openDevice(id);
224                 if (!mStaticInfo.isColorOutputSupported()) {
225                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
226                     continue;
227                 }
228                 previewStillCombinationTestByCamera();
229             } finally {
230                 closeDevice();
231                 closeImageReader();
232             }
233         }
234     }
235 
236     /**
237      * Test AE compensation.
238      * <p>
239      * For each integer EV compensation setting: retrieve the exposure value (exposure time *
240      * sensitivity) with or without compensation, verify if the exposure value is legal (conformed
241      * to what static info has) and the ratio between two exposure values matches EV compensation
242      * setting. Also test for the behavior that exposure settings should be changed when AE
243      * compensation settings is changed, even when AE lock is ON.
244      * </p>
245      */
testAeCompensation()246     public void testAeCompensation() throws Exception {
247         for (String id : mCameraIds) {
248             try {
249                 Log.i(TAG, "Testing AE compensation for Camera " + id);
250                 openDevice(id);
251 
252                 if (mStaticInfo.isHardwareLevelLegacy()) {
253                     Log.i(TAG, "Skipping test on legacy devices");
254                     continue;
255                 }
256                 if (!mStaticInfo.isColorOutputSupported()) {
257                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
258                     continue;
259                 }
260                 aeCompensationTestByCamera();
261             } finally {
262                 closeDevice();
263                 closeImageReader();
264             }
265         }
266     }
267 
268     /**
269      * Test Ae region for still capture.
270      */
testAeRegions()271     public void testAeRegions() throws Exception {
272         for (String id : mCameraIds) {
273             try {
274                 Log.i(TAG, "Testing AE regions for Camera " + id);
275                 openDevice(id);
276 
277                 boolean aeRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
278                 if (!aeRegionsSupported) {
279                     continue;
280                 }
281 
282                 ArrayList<MeteringRectangle[]> aeRegionTestCases = get3ARegionTestCasesForCamera();
283                 for (MeteringRectangle[] aeRegions : aeRegionTestCases) {
284                     takePictureTestByCamera(aeRegions, /*awbRegions*/null, /*afRegions*/null);
285                 }
286             } finally {
287                 closeDevice();
288                 closeImageReader();
289             }
290         }
291     }
292 
293     /**
294      * Test AWB region for still capture.
295      */
testAwbRegions()296     public void testAwbRegions() throws Exception {
297         for (String id : mCameraIds) {
298             try {
299                 Log.i(TAG, "Testing AE regions for Camera " + id);
300                 openDevice(id);
301 
302                 boolean awbRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AWB_INDEX);
303                 if (!awbRegionsSupported) {
304                     continue;
305                 }
306 
307                 ArrayList<MeteringRectangle[]> awbRegionTestCases = get3ARegionTestCasesForCamera();
308                 for (MeteringRectangle[] awbRegions : awbRegionTestCases) {
309                     takePictureTestByCamera(/*aeRegions*/null, awbRegions, /*afRegions*/null);
310                 }
311             } finally {
312                 closeDevice();
313                 closeImageReader();
314             }
315         }
316     }
317 
318     /**
319      * Test Af region for still capture.
320      */
testAfRegions()321     public void testAfRegions() throws Exception {
322         for (String id : mCameraIds) {
323             try {
324                 Log.i(TAG, "Testing AF regions for Camera " + id);
325                 openDevice(id);
326 
327                 boolean afRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
328                 if (!afRegionsSupported) {
329                     continue;
330                 }
331 
332                 ArrayList<MeteringRectangle[]> afRegionTestCases = get3ARegionTestCasesForCamera();
333                 for (MeteringRectangle[] afRegions : afRegionTestCases) {
334                     takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, afRegions);
335                 }
336             } finally {
337                 closeDevice();
338                 closeImageReader();
339             }
340         }
341     }
342 
343     /**
344      * Test preview is still running after a still request
345      */
testPreviewPersistence()346     public void testPreviewPersistence() throws Exception {
347         for (String id : mCameraIds) {
348             try {
349                 Log.i(TAG, "Testing preview persistence for Camera " + id);
350                 openDevice(id);
351                 if (!mStaticInfo.isColorOutputSupported()) {
352                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
353                     continue;
354                 }
355                 previewPersistenceTestByCamera();
356             } finally {
357                 closeDevice();
358                 closeImageReader();
359             }
360         }
361     }
362 
testAePrecaptureTriggerCancelJpegCapture()363     public void testAePrecaptureTriggerCancelJpegCapture() throws Exception {
364         for (String id : mCameraIds) {
365             try {
366                 Log.i(TAG, "Testing AE precapture cancel for jpeg capture for Camera " + id);
367                 openDevice(id);
368 
369                 // Legacy device doesn't support AE precapture trigger
370                 if (mStaticInfo.isHardwareLevelLegacy()) {
371                     Log.i(TAG, "Skipping AE precapture trigger cancel test on legacy devices");
372                     continue;
373                 }
374                 if (!mStaticInfo.isColorOutputSupported()) {
375                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
376                     continue;
377                 }
378                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
379                         /*addAeTriggerCancel*/true, /*allocateBitmap*/false);
380             } finally {
381                 closeDevice();
382                 closeImageReader();
383             }
384         }
385     }
386 
387     /**
388      * Test allocate some bitmaps while taking picture.
389      * <p>
390      * Per android CDD (5.0 and newer), android devices should support allocation of at least 3
391      * bitmaps equal to the size of the images produced by the largest resolution camera sensor on
392      * the devices.
393      * </p>
394      */
testAllocateBitmap()395     public void testAllocateBitmap() throws Exception {
396         for (String id : mCameraIds) {
397             try {
398                 Log.i(TAG, "Testing bitmap allocations for Camera " + id);
399                 openDevice(id);
400                 if (!mStaticInfo.isColorOutputSupported()) {
401                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
402                     continue;
403                 }
404                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
405                         /*addAeTriggerCancel*/false, /*allocateBitmap*/true);
406             } finally {
407                 closeDevice();
408                 closeImageReader();
409             }
410         }
411 
412     }
413 
414     /**
415      * Start preview,take a picture and test preview is still running after snapshot
416      */
previewPersistenceTestByCamera()417     private void previewPersistenceTestByCamera() throws Exception {
418         Size maxStillSz = mOrderedStillSizes.get(0);
419         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
420 
421         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
422         SimpleCaptureCallback stillResultListener = new SimpleCaptureCallback();
423         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
424         CaptureRequest.Builder previewRequest =
425                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
426         CaptureRequest.Builder stillRequest =
427                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
428         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
429                 maxStillSz, resultListener, imageListener);
430 
431         // make sure preview is actually running
432         waitForNumResults(resultListener, NUM_FRAMES_WAITED);
433 
434         // take a picture
435         CaptureRequest request = stillRequest.build();
436         mSession.capture(request, stillResultListener, mHandler);
437         stillResultListener.getCaptureResultForRequest(request,
438                 WAIT_FOR_RESULT_TIMEOUT_MS);
439 
440         // validate image
441         Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
442         validateJpegCapture(image, maxStillSz);
443 
444         // make sure preview is still running after still capture
445         waitForNumResults(resultListener, NUM_FRAMES_WAITED);
446 
447         stopPreview();
448 
449         // Free image resources
450         image.close();
451         closeImageReader();
452         return;
453     }
454 
455     /**
456      * Take a picture for a given set of 3A regions for a particular camera.
457      * <p>
458      * Before take a still capture, it triggers an auto focus and lock it first,
459      * then wait for AWB to converge and lock it, then trigger a precapture
460      * metering sequence and wait for AE converged. After capture is received, the
461      * capture result and image are validated.
462      * </p>
463      *
464      * @param aeRegions AE regions for this capture
465      * @param awbRegions AWB regions for this capture
466      * @param afRegions AF regions for this capture
467      */
takePictureTestByCamera( MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions, MeteringRectangle[] afRegions)468     private void takePictureTestByCamera(
469             MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
470             MeteringRectangle[] afRegions) throws Exception {
471         takePictureTestByCamera(aeRegions, awbRegions, afRegions,
472                 /*addAeTriggerCancel*/false, /*allocateBitmap*/false);
473     }
474 
475     /**
476      * Take a picture for a given set of 3A regions for a particular camera.
477      * <p>
478      * Before take a still capture, it triggers an auto focus and lock it first,
479      * then wait for AWB to converge and lock it, then trigger a precapture
480      * metering sequence and wait for AE converged. After capture is received, the
481      * capture result and image are validated. If {@code addAeTriggerCancel} is true,
482      * a precapture trigger cancel will be inserted between two adjacent triggers, which
483      * should effective cancel the first trigger.
484      * </p>
485      *
486      * @param aeRegions AE regions for this capture
487      * @param awbRegions AWB regions for this capture
488      * @param afRegions AF regions for this capture
489      * @param addAeTriggerCancel If a AE precapture trigger cancel is sent after the trigger.
490      * @param allocateBitmap If a set of bitmaps are allocated during the test for memory test.
491      */
takePictureTestByCamera( MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions, MeteringRectangle[] afRegions, boolean addAeTriggerCancel, boolean allocateBitmap)492     private void takePictureTestByCamera(
493             MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
494             MeteringRectangle[] afRegions, boolean addAeTriggerCancel, boolean allocateBitmap)
495                     throws Exception {
496 
497         boolean hasFocuser = mStaticInfo.hasFocuser();
498 
499         Size maxStillSz = mOrderedStillSizes.get(0);
500         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
501         CaptureResult result;
502         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
503         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
504         CaptureRequest.Builder previewRequest =
505                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
506         CaptureRequest.Builder stillRequest =
507                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
508         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
509                 maxStillSz, resultListener, imageListener);
510 
511         // Set AE mode to ON_AUTO_FLASH if flash is available.
512         if (mStaticInfo.hasFlash()) {
513             previewRequest.set(CaptureRequest.CONTROL_AE_MODE,
514                     CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
515             stillRequest.set(CaptureRequest.CONTROL_AE_MODE,
516                     CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
517         }
518 
519         Camera2Focuser focuser = null;
520         /**
521          * Step 1: trigger an auto focus run, and wait for AF locked.
522          */
523         boolean canSetAfRegion = hasFocuser && (afRegions != null) &&
524                 isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
525         if (hasFocuser) {
526             SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
527             focuser = new Camera2Focuser(mCamera, mSession, mPreviewSurface, afListener,
528                     mStaticInfo.getCharacteristics(), mHandler);
529             if (canSetAfRegion) {
530                 stillRequest.set(CaptureRequest.CONTROL_AF_REGIONS, afRegions);
531             }
532             focuser.startAutoFocus(afRegions);
533             afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
534         }
535 
536         /**
537          * Have to get the current AF mode to be used for other 3A repeating
538          * request, otherwise, the new AF mode in AE/AWB request could be
539          * different with existing repeating requests being sent by focuser,
540          * then it could make AF unlocked too early. Beside that, for still
541          * capture, AF mode must not be different with the one in current
542          * repeating request, otherwise, the still capture itself would trigger
543          * an AF mode change, and the AF lock would be lost for this capture.
544          */
545         int currentAfMode = CaptureRequest.CONTROL_AF_MODE_OFF;
546         if (hasFocuser) {
547             currentAfMode = focuser.getCurrentAfMode();
548         }
549         previewRequest.set(CaptureRequest.CONTROL_AF_MODE, currentAfMode);
550         stillRequest.set(CaptureRequest.CONTROL_AF_MODE, currentAfMode);
551 
552         /**
553          * Step 2: AF is already locked, wait for AWB converged, then lock it.
554          */
555         resultListener = new SimpleCaptureCallback();
556         boolean canSetAwbRegion =
557                 (awbRegions != null) && isRegionsSupportedFor3A(MAX_REGIONS_AWB_INDEX);
558         if (canSetAwbRegion) {
559             previewRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
560             stillRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
561         }
562         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
563         if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
564             waitForResultValue(resultListener, CaptureResult.CONTROL_AWB_STATE,
565                     CaptureResult.CONTROL_AWB_STATE_CONVERGED, NUM_RESULTS_WAIT_TIMEOUT);
566         } else {
567             // LEGACY Devices don't have the AWB_STATE reported in results, so just wait
568             waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
569         }
570         boolean canSetAwbLock = mStaticInfo.isAwbLockSupported();
571         if (canSetAwbLock) {
572             previewRequest.set(CaptureRequest.CONTROL_AWB_LOCK, true);
573         }
574         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
575         // Validate the next result immediately for region and mode.
576         result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
577         mCollector.expectEquals("AWB mode in result and request should be same",
578                 previewRequest.get(CaptureRequest.CONTROL_AWB_MODE),
579                 result.get(CaptureResult.CONTROL_AWB_MODE));
580         if (canSetAwbRegion) {
581             MeteringRectangle[] resultAwbRegions =
582                     getValueNotNull(result, CaptureResult.CONTROL_AWB_REGIONS);
583             mCollector.expectEquals("AWB regions in result and request should be same",
584                     awbRegions, resultAwbRegions);
585         }
586 
587         /**
588          * Step 3: trigger an AE precapture metering sequence and wait for AE converged.
589          */
590         resultListener = new SimpleCaptureCallback();
591         boolean canSetAeRegion =
592                 (aeRegions != null) && isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
593         if (canSetAeRegion) {
594             previewRequest.set(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
595             stillRequest.set(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
596         }
597         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
598         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
599                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
600         mSession.capture(previewRequest.build(), resultListener, mHandler);
601         if (addAeTriggerCancel) {
602             // Cancel the current precapture trigger, then send another trigger.
603             // The camera device should behave as if the first trigger is not sent.
604             // Wait one request to make the trigger start doing something before cancel.
605             waitForNumResults(resultListener, /*numResultsWait*/ 1);
606             previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
607                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
608             mSession.capture(previewRequest.build(), resultListener, mHandler);
609             waitForResultValue(resultListener, CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER,
610                     CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL,
611                     NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
612             // Issue another trigger
613             previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
614                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
615             mSession.capture(previewRequest.build(), resultListener, mHandler);
616         }
617         waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
618 
619         // Validate the next result immediately for region and mode.
620         result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
621         mCollector.expectEquals("AE mode in result and request should be same",
622                 previewRequest.get(CaptureRequest.CONTROL_AE_MODE),
623                 result.get(CaptureResult.CONTROL_AE_MODE));
624         if (canSetAeRegion) {
625             MeteringRectangle[] resultAeRegions =
626                     getValueNotNull(result, CaptureResult.CONTROL_AE_REGIONS);
627 
628             mCollector.expectMeteringRegionsAreSimilar(
629                     "AE regions in result and request should be similar",
630                     aeRegions,
631                     resultAeRegions,
632                     METERING_REGION_ERROR_PERCENT_DELTA);
633         }
634 
635         /**
636          * Step 4: take a picture when all 3A are in good state.
637          */
638         resultListener = new SimpleCaptureCallback();
639         CaptureRequest request = stillRequest.build();
640         mSession.capture(request, resultListener, mHandler);
641         // Validate the next result immediately for region and mode.
642         result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
643         mCollector.expectEquals("AF mode in result and request should be same",
644                 stillRequest.get(CaptureRequest.CONTROL_AF_MODE),
645                 result.get(CaptureResult.CONTROL_AF_MODE));
646         if (canSetAfRegion) {
647             MeteringRectangle[] resultAfRegions =
648                     getValueNotNull(result, CaptureResult.CONTROL_AF_REGIONS);
649             mCollector.expectMeteringRegionsAreSimilar(
650                     "AF regions in result and request should be similar",
651                     afRegions,
652                     resultAfRegions,
653                     METERING_REGION_ERROR_PERCENT_DELTA);
654         }
655 
656         if (hasFocuser) {
657             // Unlock auto focus.
658             focuser.cancelAutoFocus();
659         }
660 
661         // validate image
662         Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
663         validateJpegCapture(image, maxStillSz);
664         // Test if the system can allocate 3 bitmap successfully, per android CDD camera memory
665         // requirements added by CDD 5.0
666         if (allocateBitmap) {
667             Bitmap bm[] = new Bitmap[MAX_ALLOCATED_BITMAPS];
668             for (int i = 0; i < MAX_ALLOCATED_BITMAPS; i++) {
669                 bm[i] = Bitmap.createBitmap(
670                         maxStillSz.getWidth(), maxStillSz.getHeight(), Config.ARGB_8888);
671                 assertNotNull("Created bitmap #" + i + " shouldn't be null", bm[i]);
672             }
673         }
674 
675         // Free image resources
676         image.close();
677 
678         stopPreview();
679     }
680 
681     /**
682      * Test touch region for focus by camera.
683      */
touchForFocusTestByCamera()684     private void touchForFocusTestByCamera() throws Exception {
685         SimpleCaptureCallback listener = new SimpleCaptureCallback();
686         CaptureRequest.Builder requestBuilder =
687                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
688         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
689         startPreview(requestBuilder, maxPreviewSz, listener);
690 
691         SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
692         Camera2Focuser focuser = new Camera2Focuser(mCamera, mSession, mPreviewSurface, afListener,
693                 mStaticInfo.getCharacteristics(), mHandler);
694         ArrayList<MeteringRectangle[]> testAfRegions = get3ARegionTestCasesForCamera();
695 
696         for (MeteringRectangle[] afRegions : testAfRegions) {
697             focuser.touchForAutoFocus(afRegions);
698             afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
699             focuser.cancelAutoFocus();
700         }
701     }
702 
previewStillCombinationTestByCamera()703     private void previewStillCombinationTestByCamera() throws Exception {
704         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
705         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
706 
707         for (Size stillSz : mOrderedStillSizes)
708             for (Size previewSz : mOrderedPreviewSizes) {
709                 if (VERBOSE) {
710                     Log.v(TAG, "Testing JPEG capture size " + stillSz.toString()
711                             + " with preview size " + previewSz.toString() + " for camera "
712                             + mCamera.getId());
713                 }
714                 CaptureRequest.Builder previewRequest =
715                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
716                 CaptureRequest.Builder stillRequest =
717                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
718                 prepareStillCaptureAndStartPreview(previewRequest, stillRequest, previewSz,
719                         stillSz, resultListener, imageListener);
720                 mSession.capture(stillRequest.build(), resultListener, mHandler);
721                 Image image = imageListener.getImage((mStaticInfo.isHardwareLevelLegacy()) ?
722                         RELAXED_CAPTURE_IMAGE_TIMEOUT_MS : CAPTURE_IMAGE_TIMEOUT_MS);
723                 validateJpegCapture(image, stillSz);
724 
725                 // Free image resources
726                 image.close();
727 
728                 // stopPreview must be called here to make sure next time a preview stream
729                 // is created with new size.
730                 stopPreview();
731             }
732     }
733 
734     /**
735      * Basic raw capture test for each camera.
736      */
rawCaptureTestByCamera()737     private void rawCaptureTestByCamera() throws Exception {
738         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
739         Size size = mStaticInfo.getRawDimensChecked();
740 
741         // Prepare raw capture and start preview.
742         CaptureRequest.Builder previewBuilder =
743                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
744         CaptureRequest.Builder rawBuilder =
745                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
746         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
747         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
748         prepareRawCaptureAndStartPreview(previewBuilder, rawBuilder, maxPreviewSz, size,
749                 resultListener, imageListener);
750 
751         if (VERBOSE) {
752             Log.v(TAG, "Testing Raw capture with size " + size.toString()
753                     + ", preview size " + maxPreviewSz);
754         }
755 
756         CaptureRequest rawRequest = rawBuilder.build();
757         mSession.capture(rawRequest, resultListener, mHandler);
758 
759         Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
760         validateRaw16Image(image, size);
761         if (DEBUG) {
762             byte[] rawBuffer = getDataFromImage(image);
763             String rawFileName = DEBUG_FILE_NAME_BASE + "/test" + "_" + size.toString() + "_cam" +
764                     mCamera.getId() + ".raw16";
765             Log.d(TAG, "Dump raw file into " + rawFileName);
766             dumpFile(rawFileName, rawBuffer);
767         }
768 
769         // Free image resources
770         image.close();
771 
772         stopPreview();
773     }
774 
fullRawCaptureTestByCamera()775     private void fullRawCaptureTestByCamera() throws Exception {
776         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
777         Size maxStillSz = mOrderedStillSizes.get(0);
778 
779         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
780         SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
781         SimpleImageReaderListener rawListener = new SimpleImageReaderListener();
782 
783         Size size = mStaticInfo.getRawDimensChecked();
784 
785         if (VERBOSE) {
786             Log.v(TAG, "Testing multi capture with size " + size.toString()
787                     + ", preview size " + maxPreviewSz);
788         }
789 
790         // Prepare raw capture and start preview.
791         CaptureRequest.Builder previewBuilder =
792                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
793         CaptureRequest.Builder multiBuilder =
794                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
795 
796         ImageReader rawReader = null;
797         ImageReader jpegReader = null;
798 
799         try {
800             // Create ImageReaders.
801             rawReader = makeImageReader(size,
802                     ImageFormat.RAW_SENSOR, MAX_READER_IMAGES, rawListener, mHandler);
803             jpegReader = makeImageReader(maxStillSz,
804                     ImageFormat.JPEG, MAX_READER_IMAGES, jpegListener, mHandler);
805             updatePreviewSurface(maxPreviewSz);
806 
807             // Configure output streams with preview and jpeg streams.
808             List<Surface> outputSurfaces = new ArrayList<Surface>();
809             outputSurfaces.add(rawReader.getSurface());
810             outputSurfaces.add(jpegReader.getSurface());
811             outputSurfaces.add(mPreviewSurface);
812             mSessionListener = new BlockingSessionCallback();
813             mSession = configureCameraSession(mCamera, outputSurfaces,
814                     mSessionListener, mHandler);
815 
816             // Configure the requests.
817             previewBuilder.addTarget(mPreviewSurface);
818             multiBuilder.addTarget(mPreviewSurface);
819             multiBuilder.addTarget(rawReader.getSurface());
820             multiBuilder.addTarget(jpegReader.getSurface());
821 
822             // Start preview.
823             mSession.setRepeatingRequest(previewBuilder.build(), null, mHandler);
824 
825             // Poor man's 3A, wait 2 seconds for AE/AF (if any) to settle.
826             // TODO: Do proper 3A trigger and lock (see testTakePictureTest).
827             Thread.sleep(3000);
828 
829             multiBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
830                     CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
831             CaptureRequest multiRequest = multiBuilder.build();
832 
833             mSession.capture(multiRequest, resultListener, mHandler);
834 
835             CaptureResult result = resultListener.getCaptureResultForRequest(multiRequest,
836                     NUM_RESULTS_WAIT_TIMEOUT);
837             Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
838             basicValidateJpegImage(jpegImage, maxStillSz);
839             Image rawImage = rawListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
840             validateRaw16Image(rawImage, size);
841             verifyRawCaptureResult(multiRequest, result);
842 
843 
844             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
845             try (DngCreator dngCreator = new DngCreator(mStaticInfo.getCharacteristics(), result)) {
846                 dngCreator.writeImage(outputStream, rawImage);
847             }
848 
849             if (DEBUG) {
850                 byte[] rawBuffer = outputStream.toByteArray();
851                 String rawFileName = DEBUG_FILE_NAME_BASE + "/raw16_" + TAG + size.toString() +
852                         "_cam_" + mCamera.getId() + ".dng";
853                 Log.d(TAG, "Dump raw file into " + rawFileName);
854                 dumpFile(rawFileName, rawBuffer);
855 
856                 byte[] jpegBuffer = getDataFromImage(jpegImage);
857                 String jpegFileName = DEBUG_FILE_NAME_BASE + "/jpeg_" + TAG + size.toString() +
858                         "_cam_" + mCamera.getId() + ".jpg";
859                 Log.d(TAG, "Dump jpeg file into " + rawFileName);
860                 dumpFile(jpegFileName, jpegBuffer);
861             }
862 
863             stopPreview();
864         } finally {
865             CameraTestUtils.closeImageReader(rawReader);
866             CameraTestUtils.closeImageReader(jpegReader);
867             rawReader = null;
868             jpegReader = null;
869         }
870     }
871 
872     /**
873      * Validate that raw {@link CaptureResult}.
874      *
875      * @param rawRequest a {@link CaptureRequest} use to capture a RAW16 image.
876      * @param rawResult the {@link CaptureResult} corresponding to the given request.
877      */
verifyRawCaptureResult(CaptureRequest rawRequest, CaptureResult rawResult)878     private void verifyRawCaptureResult(CaptureRequest rawRequest, CaptureResult rawResult) {
879         assertNotNull(rawRequest);
880         assertNotNull(rawResult);
881 
882         Rational[] empty = new Rational[] { Rational.ZERO, Rational.ZERO, Rational.ZERO};
883         Rational[] neutralColorPoint = mCollector.expectKeyValueNotNull("NeutralColorPoint",
884                 rawResult, CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
885         if (neutralColorPoint != null) {
886             mCollector.expectEquals("NeutralColorPoint length", empty.length,
887                     neutralColorPoint.length);
888             mCollector.expectNotEquals("NeutralColorPoint cannot be all zeroes, ", empty,
889                     neutralColorPoint);
890             mCollector.expectValuesGreaterOrEqual("NeutralColorPoint", neutralColorPoint,
891                     Rational.ZERO);
892         }
893 
894         mCollector.expectKeyValueGreaterOrEqual(rawResult, CaptureResult.SENSOR_GREEN_SPLIT, 0.0f);
895 
896         Pair<Double, Double>[] noiseProfile = mCollector.expectKeyValueNotNull("NoiseProfile",
897                 rawResult, CaptureResult.SENSOR_NOISE_PROFILE);
898         if (noiseProfile != null) {
899             mCollector.expectEquals("NoiseProfile length", noiseProfile.length,
900                 /*Num CFA channels*/4);
901             for (Pair<Double, Double> p : noiseProfile) {
902                 mCollector.expectTrue("NoiseProfile coefficients " + p +
903                         " must have: S > 0, O >= 0", p.first > 0 && p.second >= 0);
904             }
905         }
906 
907         Integer hotPixelMode = mCollector.expectKeyValueNotNull("HotPixelMode", rawResult,
908                 CaptureResult.HOT_PIXEL_MODE);
909         Boolean hotPixelMapMode = mCollector.expectKeyValueNotNull("HotPixelMapMode", rawResult,
910                 CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
911         Point[] hotPixelMap = rawResult.get(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
912 
913         Size pixelArraySize = mStaticInfo.getPixelArraySizeChecked();
914         boolean[] availableHotPixelMapModes = mStaticInfo.getValueFromKeyNonNull(
915                         CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES);
916 
917         if (hotPixelMode != null) {
918             Integer requestMode = mCollector.expectKeyValueNotNull(rawRequest,
919                     CaptureRequest.HOT_PIXEL_MODE);
920             if (requestMode != null) {
921                 mCollector.expectKeyValueEquals(rawResult, CaptureResult.HOT_PIXEL_MODE,
922                         requestMode);
923             }
924         }
925 
926         if (hotPixelMapMode != null) {
927             Boolean requestMapMode = mCollector.expectKeyValueNotNull(rawRequest,
928                     CaptureRequest.STATISTICS_HOT_PIXEL_MAP_MODE);
929             if (requestMapMode != null) {
930                 mCollector.expectKeyValueEquals(rawResult,
931                         CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE, requestMapMode);
932             }
933 
934             if (!hotPixelMapMode) {
935                 mCollector.expectTrue("HotPixelMap must be empty", hotPixelMap == null ||
936                         hotPixelMap.length == 0);
937             } else {
938                 mCollector.expectTrue("HotPixelMap must not be empty", hotPixelMap != null);
939                 mCollector.expectNotNull("AvailableHotPixelMapModes must not be null",
940                         availableHotPixelMapModes);
941                 if (availableHotPixelMapModes != null) {
942                     mCollector.expectContains("HotPixelMapMode", availableHotPixelMapModes, true);
943                 }
944 
945                 int height = pixelArraySize.getHeight();
946                 int width = pixelArraySize.getWidth();
947                 for (Point p : hotPixelMap) {
948                     mCollector.expectTrue("Hotpixel " + p + " must be in pixelArray " +
949                             pixelArraySize, p.x >= 0 && p.x < width && p.y >= 0 && p.y < height);
950                 }
951             }
952         }
953         // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
954 
955     }
956 
957     /**
958      * Issue a Jpeg capture and validate the exif information.
959      * <p>
960      * TODO: Differentiate full and limited device, some of the checks rely on
961      * per frame control and synchronization, most of them don't.
962      * </p>
963      */
jpegExifTestByCamera()964     private void jpegExifTestByCamera() throws Exception {
965         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
966         Size maxStillSz = mOrderedStillSizes.get(0);
967         if (VERBOSE) {
968             Log.v(TAG, "Testing JPEG exif with jpeg size " + maxStillSz.toString()
969                     + ", preview size " + maxPreviewSz);
970         }
971 
972         // prepare capture and start preview.
973         CaptureRequest.Builder previewBuilder =
974                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
975         CaptureRequest.Builder stillBuilder =
976                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
977         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
978         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
979         prepareStillCaptureAndStartPreview(previewBuilder, stillBuilder, maxPreviewSz, maxStillSz,
980                 resultListener, imageListener);
981 
982         // Set the jpeg keys, then issue a capture
983         Size[] thumbnailSizes = mStaticInfo.getAvailableThumbnailSizesChecked();
984         Size maxThumbnailSize = thumbnailSizes[thumbnailSizes.length - 1];
985         Size[] testThumbnailSizes = new Size[EXIF_TEST_DATA.length];
986         Arrays.fill(testThumbnailSizes, maxThumbnailSize);
987         // Make sure thumbnail size (0, 0) is covered.
988         testThumbnailSizes[0] = new Size(0, 0);
989 
990         for (int i = 0; i < EXIF_TEST_DATA.length; i++) {
991             setJpegKeys(stillBuilder, EXIF_TEST_DATA[i], testThumbnailSizes[i], mCollector);
992 
993             // Capture a jpeg image.
994             CaptureRequest request = stillBuilder.build();
995             mSession.capture(request, resultListener, mHandler);
996             CaptureResult stillResult =
997                     resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
998             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
999 
1000             verifyJpegKeys(image, stillResult, maxStillSz, testThumbnailSizes[i], EXIF_TEST_DATA[i],
1001                     mStaticInfo, mCollector);
1002 
1003             // Free image resources
1004             image.close();
1005         }
1006     }
1007 
aeCompensationTestByCamera()1008     private void aeCompensationTestByCamera() throws Exception {
1009         Range<Integer> compensationRange = mStaticInfo.getAeCompensationRangeChecked();
1010         // Skip the test if exposure compensation is not supported.
1011         if (compensationRange.equals(Range.create(0, 0))) {
1012             return;
1013         }
1014 
1015         Rational step = mStaticInfo.getAeCompensationStepChecked();
1016         float stepF = (float) step.getNumerator() / step.getDenominator();
1017         int stepsPerEv = (int) Math.round(1.0 / stepF);
1018         int numSteps = (compensationRange.getUpper() - compensationRange.getLower()) / stepsPerEv;
1019 
1020         Size maxStillSz = mOrderedStillSizes.get(0);
1021         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
1022         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
1023         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
1024         CaptureRequest.Builder previewRequest =
1025                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1026         CaptureRequest.Builder stillRequest =
1027                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
1028         boolean canSetAeLock = mStaticInfo.isAeLockSupported();
1029         boolean canReadSensorSettings = mStaticInfo.isCapabilitySupported(
1030                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS);
1031 
1032         if (canSetAeLock) {
1033             stillRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
1034         }
1035 
1036         CaptureResult normalResult;
1037         CaptureResult compensatedResult;
1038 
1039         boolean canReadExposureValueRange = mStaticInfo.areKeysAvailable(
1040                 CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
1041                 CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
1042         boolean canVerifyExposureValue = canReadSensorSettings && canReadExposureValueRange;
1043         long minExposureValue = -1;
1044         long maxExposureValuePreview = -1;
1045         long maxExposureValueStill = -1;
1046         if (canReadExposureValueRange) {
1047             // Minimum exposure settings is mostly static while maximum exposure setting depends on
1048             // frame rate range which in term depends on capture request.
1049             minExposureValue = mStaticInfo.getSensitivityMinimumOrDefault() *
1050                     mStaticInfo.getExposureMinimumOrDefault() / 1000;
1051             long maxSensitivity = mStaticInfo.getSensitivityMaximumOrDefault();
1052             long maxExposureTimeUs = mStaticInfo.getExposureMaximumOrDefault() / 1000;
1053             maxExposureValuePreview = getMaxExposureValue(previewRequest, maxExposureTimeUs,
1054                     maxSensitivity);
1055             maxExposureValueStill = getMaxExposureValue(stillRequest, maxExposureTimeUs,
1056                     maxSensitivity);
1057         }
1058 
1059         // Set the max number of images to be same as the burst count, as the verification
1060         // could be much slower than producing rate, and we don't want to starve producer.
1061         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
1062                 maxStillSz, resultListener, numSteps, imageListener);
1063 
1064         for (int i = 0; i <= numSteps; i++) {
1065             int exposureCompensation = i * stepsPerEv + compensationRange.getLower();
1066             double expectedRatio = Math.pow(2.0, exposureCompensation / stepsPerEv);
1067 
1068             // Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
1069             waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1070             normalResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
1071 
1072             long normalExposureValue = -1;
1073             if (canVerifyExposureValue) {
1074                 // get and check if current exposure value is valid
1075                 normalExposureValue = getExposureValue(normalResult);
1076                 mCollector.expectInRange("Exposure setting out of bound", normalExposureValue,
1077                         minExposureValue, maxExposureValuePreview);
1078 
1079                 // Only run the test if expectedExposureValue is within valid range
1080                 long expectedExposureValue = (long) (normalExposureValue * expectedRatio);
1081                 if (expectedExposureValue < minExposureValue ||
1082                     expectedExposureValue > maxExposureValueStill) {
1083                     continue;
1084                 }
1085                 Log.v(TAG, "Expect ratio: " + expectedRatio +
1086                         " normalExposureValue: " + normalExposureValue +
1087                         " expectedExposureValue: " + expectedExposureValue +
1088                         " minExposureValue: " + minExposureValue +
1089                         " maxExposureValuePreview: " + maxExposureValuePreview +
1090                         " maxExposureValueStill: " + maxExposureValueStill);
1091             }
1092 
1093             // Now issue exposure compensation and wait for AE locked. AE could take a few
1094             // frames to go back to locked state
1095             previewRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
1096                     exposureCompensation);
1097             if (canSetAeLock) {
1098                 previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
1099             }
1100             mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
1101             if (canSetAeLock) {
1102                 waitForAeLocked(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1103             } else {
1104                 waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
1105             }
1106 
1107             // Issue still capture
1108             if (VERBOSE) {
1109                 Log.v(TAG, "Verifying capture result for ae compensation value "
1110                         + exposureCompensation);
1111             }
1112 
1113             stillRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, exposureCompensation);
1114             CaptureRequest request = stillRequest.build();
1115             mSession.capture(request, resultListener, mHandler);
1116 
1117             compensatedResult = resultListener.getCaptureResultForRequest(
1118                     request, WAIT_FOR_RESULT_TIMEOUT_MS);
1119 
1120             if (canVerifyExposureValue) {
1121                 // Verify the exposure value compensates as requested
1122                 long compensatedExposureValue = getExposureValue(compensatedResult);
1123                 mCollector.expectInRange("Exposure setting out of bound", compensatedExposureValue,
1124                         minExposureValue, maxExposureValueStill);
1125                 double observedRatio = (double) compensatedExposureValue / normalExposureValue;
1126                 double error = observedRatio / expectedRatio;
1127                 String errorString = String.format(
1128                         "Exposure compensation ratio exceeds error tolerence:" +
1129                         " expected(%f) observed(%f)." +
1130                         " Normal exposure time %d us, sensitivity %d." +
1131                         " Compensated exposure time %d us, sensitivity %d",
1132                         expectedRatio, observedRatio,
1133                         (int) (getValueNotNull(
1134                                 normalResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
1135                         getValueNotNull(normalResult, CaptureResult.SENSOR_SENSITIVITY),
1136                         (int) (getValueNotNull(
1137                                 compensatedResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
1138                         getValueNotNull(compensatedResult, CaptureResult.SENSOR_SENSITIVITY));
1139                 mCollector.expectInRange(errorString, error,
1140                         1.0 - AE_COMPENSATION_ERROR_TOLERANCE,
1141                         1.0 + AE_COMPENSATION_ERROR_TOLERANCE);
1142             }
1143 
1144             mCollector.expectEquals("Exposure compensation result should match requested value.",
1145                     exposureCompensation,
1146                     compensatedResult.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
1147             if (canSetAeLock) {
1148                 mCollector.expectTrue("Exposure lock should be set",
1149                         compensatedResult.get(CaptureResult.CONTROL_AE_LOCK));
1150             }
1151 
1152             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
1153             validateJpegCapture(image, maxStillSz);
1154             image.close();
1155 
1156             // Recover AE compensation and lock
1157             previewRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
1158             if (canSetAeLock) {
1159                 previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, false);
1160             }
1161             mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
1162         }
1163     }
1164 
getExposureValue(CaptureResult result)1165     private long getExposureValue(CaptureResult result) throws Exception {
1166         int expTimeUs = (int) (getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000);
1167         int sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
1168         return expTimeUs * sensitivity;
1169     }
1170 
getMaxExposureValue(CaptureRequest.Builder request, long maxExposureTimeUs, long maxSensitivity)1171     private long getMaxExposureValue(CaptureRequest.Builder request, long maxExposureTimeUs,
1172                 long maxSensitivity)  throws Exception {
1173         Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
1174         long maxFrameDurationUs = Math.round(1000000.0 / fpsRange.getLower());
1175         long currentMaxExposureTimeUs = Math.min(maxFrameDurationUs, maxExposureTimeUs);
1176         return currentMaxExposureTimeUs * maxSensitivity;
1177     }
1178 
1179 
1180     //----------------------------------------------------------------
1181     //---------Below are common functions for all tests.--------------
1182     //----------------------------------------------------------------
1183     /**
1184      * Validate standard raw (RAW16) capture image.
1185      *
1186      * @param image The raw16 format image captured
1187      * @param rawSize The expected raw size
1188      */
validateRaw16Image(Image image, Size rawSize)1189     private static void validateRaw16Image(Image image, Size rawSize) {
1190         CameraTestUtils.validateImage(image, rawSize.getWidth(), rawSize.getHeight(),
1191                 ImageFormat.RAW_SENSOR, /*filePath*/null);
1192     }
1193 
1194     /**
1195      * Validate JPEG capture image object sanity and test.
1196      * <p>
1197      * In addition to image object sanity, this function also does the decoding
1198      * test, which is slower.
1199      * </p>
1200      *
1201      * @param image The JPEG image to be verified.
1202      * @param jpegSize The JPEG capture size to be verified against.
1203      */
validateJpegCapture(Image image, Size jpegSize)1204     private static void validateJpegCapture(Image image, Size jpegSize) {
1205         CameraTestUtils.validateImage(image, jpegSize.getWidth(), jpegSize.getHeight(),
1206                 ImageFormat.JPEG, /*filePath*/null);
1207     }
1208 
1209     private static class SimpleAutoFocusListener implements Camera2Focuser.AutoFocusListener {
1210         final ConditionVariable focusDone = new ConditionVariable();
1211         @Override
onAutoFocusLocked(boolean success)1212         public void onAutoFocusLocked(boolean success) {
1213             focusDone.open();
1214         }
1215 
waitForAutoFocusDone(long timeoutMs)1216         public void waitForAutoFocusDone(long timeoutMs) {
1217             if (focusDone.block(timeoutMs)) {
1218                 focusDone.close();
1219             } else {
1220                 throw new TimeoutRuntimeException("Wait for auto focus done timed out after "
1221                         + timeoutMs + "ms");
1222             }
1223         }
1224     }
1225 
1226     /**
1227      * Get 5 3A region test cases, each with one square region in it.
1228      * The first one is at center, the other four are at corners of
1229      * active array rectangle.
1230      *
1231      * @return array of test 3A regions
1232      */
get3ARegionTestCasesForCamera()1233     private ArrayList<MeteringRectangle[]> get3ARegionTestCasesForCamera() {
1234         final int TEST_3A_REGION_NUM = 5;
1235         final int DEFAULT_REGION_WEIGHT = 30;
1236         final int DEFAULT_REGION_SCALE_RATIO = 8;
1237         ArrayList<MeteringRectangle[]> testCases =
1238                 new ArrayList<MeteringRectangle[]>(TEST_3A_REGION_NUM);
1239         final Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
1240         int regionWidth = activeArraySize.width() / DEFAULT_REGION_SCALE_RATIO - 1;
1241         int regionHeight = activeArraySize.height() / DEFAULT_REGION_SCALE_RATIO - 1;
1242         int centerX = activeArraySize.width() / 2;
1243         int centerY = activeArraySize.height() / 2;
1244         int bottomRightX = activeArraySize.width() - 1;
1245         int bottomRightY = activeArraySize.height() - 1;
1246 
1247         // Center region
1248         testCases.add(
1249                 new MeteringRectangle[] {
1250                     new MeteringRectangle(
1251                             centerX - regionWidth / 2,  // x
1252                             centerY - regionHeight / 2, // y
1253                             regionWidth,                // width
1254                             regionHeight,               // height
1255                             DEFAULT_REGION_WEIGHT)});
1256 
1257         // Upper left corner
1258         testCases.add(
1259                 new MeteringRectangle[] {
1260                     new MeteringRectangle(
1261                             0,                // x
1262                             0,                // y
1263                             regionWidth,      // width
1264                             regionHeight,     // height
1265                             DEFAULT_REGION_WEIGHT)});
1266 
1267         // Upper right corner
1268         testCases.add(
1269                 new MeteringRectangle[] {
1270                     new MeteringRectangle(
1271                             bottomRightX - regionWidth, // x
1272                             0,                          // y
1273                             regionWidth,                // width
1274                             regionHeight,               // height
1275                             DEFAULT_REGION_WEIGHT)});
1276 
1277         // Bottom left corner
1278         testCases.add(
1279                 new MeteringRectangle[] {
1280                     new MeteringRectangle(
1281                             0,                           // x
1282                             bottomRightY - regionHeight, // y
1283                             regionWidth,                 // width
1284                             regionHeight,                // height
1285                             DEFAULT_REGION_WEIGHT)});
1286 
1287         // Bottom right corner
1288         testCases.add(
1289                 new MeteringRectangle[] {
1290                     new MeteringRectangle(
1291                             bottomRightX - regionWidth,  // x
1292                             bottomRightY - regionHeight, // y
1293                             regionWidth,                 // width
1294                             regionHeight,                // height
1295                             DEFAULT_REGION_WEIGHT)});
1296 
1297         if (VERBOSE) {
1298             StringBuilder sb = new StringBuilder();
1299             for (MeteringRectangle[] mr : testCases) {
1300                 sb.append("{");
1301                 sb.append(Arrays.toString(mr));
1302                 sb.append("}, ");
1303             }
1304             if (sb.length() > 1)
1305                 sb.setLength(sb.length() - 2); // Remove the redundant comma and space at the end
1306             Log.v(TAG, "Generated test regions are: " + sb.toString());
1307         }
1308 
1309         return testCases;
1310     }
1311 
isRegionsSupportedFor3A(int index)1312     private boolean isRegionsSupportedFor3A(int index) {
1313         int maxRegions = 0;
1314         switch (index) {
1315             case MAX_REGIONS_AE_INDEX:
1316                 maxRegions = mStaticInfo.getAeMaxRegionsChecked();
1317                 break;
1318             case MAX_REGIONS_AWB_INDEX:
1319                 maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
1320                 break;
1321             case  MAX_REGIONS_AF_INDEX:
1322                 maxRegions = mStaticInfo.getAfMaxRegionsChecked();
1323                 break;
1324             default:
1325                 throw new IllegalArgumentException("Unknown algorithm index");
1326         }
1327         boolean isRegionsSupported = maxRegions > 0;
1328         if (index == MAX_REGIONS_AF_INDEX && isRegionsSupported) {
1329             mCollector.expectTrue(
1330                     "Device reports non-zero max AF region count for a camera without focuser!",
1331                     mStaticInfo.hasFocuser());
1332             isRegionsSupported = isRegionsSupported && mStaticInfo.hasFocuser();
1333         }
1334 
1335         return isRegionsSupported;
1336     }
1337 }
1338