1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mediaframeworktest.stress;
18
19import com.android.ex.camera2.blocking.BlockingSessionCallback;
20import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
21import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
22import com.android.mediaframeworktest.helpers.Camera2Focuser;
23import com.android.mediaframeworktest.helpers.CameraTestUtils;
24import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
25
26import android.graphics.ImageFormat;
27import android.graphics.Point;
28import android.hardware.camera2.CameraCharacteristics;
29import android.hardware.camera2.CameraDevice;
30import android.hardware.camera2.CaptureRequest;
31import android.hardware.camera2.CaptureResult;
32import android.hardware.camera2.DngCreator;
33import android.hardware.camera2.params.MeteringRectangle;
34import android.media.Image;
35import android.media.ImageReader;
36import android.os.ConditionVariable;
37import android.util.Log;
38import android.util.Pair;
39import android.util.Rational;
40import android.util.Size;
41import android.view.Surface;
42
43import java.io.ByteArrayOutputStream;
44import java.util.ArrayList;
45import java.util.List;
46
47import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
48import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
49import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
50import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage;
51import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
52import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile;
53import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage;
54import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
55import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
56
57/**
58 * <p>Tests for still capture API.</p>
59 *
60 * adb shell am instrument \
61 *    -e class com.android.mediaframeworktest.stress.Camera2StillCaptureTest#testTakePicture \
62 *    -e iterations 200 \
63 *    -e waitIntervalMs 1000 \
64 *    -e resultToFile false \
65 *    -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner
66 */
67public class Camera2StillCaptureTest extends Camera2SurfaceViewTestCase {
68    private static final String TAG = "StillCaptureTest";
69    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
70    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
71    // 60 second to accommodate the possible long exposure time.
72    private static final int MAX_REGIONS_AE_INDEX = 0;
73    private static final int MAX_REGIONS_AWB_INDEX = 1;
74    private static final int MAX_REGIONS_AF_INDEX = 2;
75    private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
76    private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
77    // 5 percent error margin for resulting metering regions
78    private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
79
80    @Override
81    protected void setUp() throws Exception {
82        super.setUp();
83    }
84
85    @Override
86    protected void tearDown() throws Exception {
87        super.tearDown();
88    }
89
90    /**
91     * Test normal still capture sequence.
92     * <p>
93     * Preview and and jpeg output streams are configured. Max still capture
94     * size is used for jpeg capture. The sequence of still capture being test
95     * is: start preview, auto focus, precapture metering (if AE is not
96     * converged), then capture jpeg. The AWB and AE are in auto modes. AF mode
97     * is CONTINUOUS_PICTURE.
98     * </p>
99     */
100    public void testTakePicture() throws Exception{
101        for (String id : mCameraIds) {
102            try {
103                Log.i(TAG, "Testing basic take picture for Camera " + id);
104                openDevice(id);
105                if (!mStaticInfo.isColorOutputSupported()) {
106                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
107                    continue;
108                }
109
110                // Test iteration starts...
111                for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
112                    Log.v(TAG, String.format("Taking pictures: %d/%d", iteration + 1,
113                            getIterationCount()));
114                    takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null,
115                            /*afRegions*/null);
116                    getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
117                    Thread.sleep(getTestWaitIntervalMs());
118                }
119            } finally {
120                closeDevice();
121                closeImageReader();
122            }
123        }
124    }
125
126    /**
127     * Test the full raw capture use case.
128     *
129     * This includes:
130     * - Configuring the camera with a preview, jpeg, and raw output stream.
131     * - Running preview until AE/AF can settle.
132     * - Capturing with a request targeting all three output streams.
133     */
134    public void testFullRawCapture() throws Exception {
135        for (int i = 0; i < mCameraIds.length; i++) {
136            try {
137                Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
138                openDevice(mCameraIds[i]);
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                // Test iteration starts...
147                for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
148                    Log.v(TAG, String.format("Taking full RAW pictures: %d/%d", iteration + 1,
149                            getIterationCount()));
150                    fullRawCaptureTestByCamera();
151                    getResultPrinter().printStatus(getIterationCount(), iteration + 1,
152                            mCameraIds[i]);
153                    Thread.sleep(getTestWaitIntervalMs());
154                }
155            } finally {
156                closeDevice();
157                closeImageReader();
158            }
159        }
160    }
161
162    /**
163     * Take a picture for a given set of 3A regions for a particular camera.
164     * <p>
165     * Before take a still capture, it triggers an auto focus and lock it first,
166     * then wait for AWB to converge and lock it, then trigger a precapture
167     * metering sequence and wait for AE converged. After capture is received, the
168     * capture result and image are validated.
169     * </p>
170     *
171     * @param aeRegions AE regions for this capture
172     * @param awbRegions AWB regions for this capture
173     * @param afRegions AF regions for this capture
174     */
175    private void takePictureTestByCamera(
176            MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
177            MeteringRectangle[] afRegions) throws Exception {
178        takePictureTestByCamera(aeRegions, awbRegions, afRegions,
179                /*addAeTriggerCancel*/false);
180    }
181
182    /**
183     * Take a picture for a given set of 3A regions for a particular camera.
184     * <p>
185     * Before take a still capture, it triggers an auto focus and lock it first,
186     * then wait for AWB to converge and lock it, then trigger a precapture
187     * metering sequence and wait for AE converged. After capture is received, the
188     * capture result and image are validated. If {@code addAeTriggerCancel} is true,
189     * a precapture trigger cancel will be inserted between two adjacent triggers, which
190     * should effective cancel the first trigger.
191     * </p>
192     *
193     * @param aeRegions AE regions for this capture
194     * @param awbRegions AWB regions for this capture
195     * @param afRegions AF regions for this capture
196     * @param addAeTriggerCancel If a AE precapture trigger cancel is sent after the trigger.
197     */
198    private void takePictureTestByCamera(
199            MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
200            MeteringRectangle[] afRegions, boolean addAeTriggerCancel) throws Exception {
201
202        boolean hasFocuser = mStaticInfo.hasFocuser();
203
204        Size maxStillSz = mOrderedStillSizes.get(0);
205        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
206        CaptureResult result;
207        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
208        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
209        CaptureRequest.Builder previewRequest =
210                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
211        CaptureRequest.Builder stillRequest =
212                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
213        prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
214                maxStillSz, resultListener, imageListener);
215
216        // Set AE mode to ON_AUTO_FLASH if flash is available.
217        if (mStaticInfo.hasFlash()) {
218            previewRequest.set(CaptureRequest.CONTROL_AE_MODE,
219                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
220            stillRequest.set(CaptureRequest.CONTROL_AE_MODE,
221                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
222        }
223
224        Camera2Focuser focuser = null;
225        /**
226         * Step 1: trigger an auto focus run, and wait for AF locked.
227         */
228        boolean canSetAfRegion = hasFocuser && (afRegions != null) &&
229                isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
230        if (hasFocuser) {
231            SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
232            focuser = new Camera2Focuser(mCamera, mSession, mPreviewSurface, afListener,
233                    mStaticInfo.getCharacteristics(), mHandler);
234            if (canSetAfRegion) {
235                stillRequest.set(CaptureRequest.CONTROL_AF_REGIONS, afRegions);
236            }
237            focuser.startAutoFocus(afRegions);
238            afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
239        }
240
241        /**
242         * Have to get the current AF mode to be used for other 3A repeating
243         * request, otherwise, the new AF mode in AE/AWB request could be
244         * different with existing repeating requests being sent by focuser,
245         * then it could make AF unlocked too early. Beside that, for still
246         * capture, AF mode must not be different with the one in current
247         * repeating request, otherwise, the still capture itself would trigger
248         * an AF mode change, and the AF lock would be lost for this capture.
249         */
250        int currentAfMode = CaptureRequest.CONTROL_AF_MODE_OFF;
251        if (hasFocuser) {
252            currentAfMode = focuser.getCurrentAfMode();
253        }
254        previewRequest.set(CaptureRequest.CONTROL_AF_MODE, currentAfMode);
255        stillRequest.set(CaptureRequest.CONTROL_AF_MODE, currentAfMode);
256
257        /**
258         * Step 2: AF is already locked, wait for AWB converged, then lock it.
259         */
260        resultListener = new SimpleCaptureCallback();
261        boolean canSetAwbRegion =
262                (awbRegions != null) && isRegionsSupportedFor3A(MAX_REGIONS_AWB_INDEX);
263        if (canSetAwbRegion) {
264            previewRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
265            stillRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
266        }
267        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
268        if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
269            waitForResultValue(resultListener, CaptureResult.CONTROL_AWB_STATE,
270                    CaptureResult.CONTROL_AWB_STATE_CONVERGED, NUM_RESULTS_WAIT_TIMEOUT);
271        } else {
272            // LEGACY Devices don't have the AWB_STATE reported in results, so just wait
273            waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
274        }
275        boolean canSetAwbLock = mStaticInfo.isAwbLockSupported();
276        if (canSetAwbLock) {
277            previewRequest.set(CaptureRequest.CONTROL_AWB_LOCK, true);
278        }
279        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
280        // Validate the next result immediately for region and mode.
281        result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
282        mCollector.expectEquals("AWB mode in result and request should be same",
283                previewRequest.get(CaptureRequest.CONTROL_AWB_MODE),
284                result.get(CaptureResult.CONTROL_AWB_MODE));
285        if (canSetAwbRegion) {
286            MeteringRectangle[] resultAwbRegions =
287                    getValueNotNull(result, CaptureResult.CONTROL_AWB_REGIONS);
288            mCollector.expectEquals("AWB regions in result and request should be same",
289                    awbRegions, resultAwbRegions);
290        }
291
292        /**
293         * Step 3: trigger an AE precapture metering sequence and wait for AE converged.
294         */
295        resultListener = new SimpleCaptureCallback();
296        boolean canSetAeRegion =
297                (aeRegions != null) && isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
298        if (canSetAeRegion) {
299            previewRequest.set(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
300            stillRequest.set(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
301        }
302        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
303        previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
304                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
305        mSession.capture(previewRequest.build(), resultListener, mHandler);
306        if (addAeTriggerCancel) {
307            // Cancel the current precapture trigger, then send another trigger.
308            // The camera device should behave as if the first trigger is not sent.
309            // Wait one request to make the trigger start doing something before cancel.
310            waitForNumResults(resultListener, /*numResultsWait*/ 1);
311            previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
312                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
313            mSession.capture(previewRequest.build(), resultListener, mHandler);
314            waitForResultValue(resultListener, CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER,
315                    CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL,
316                    NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
317            // Issue another trigger
318            previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
319                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
320            mSession.capture(previewRequest.build(), resultListener, mHandler);
321        }
322        waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
323
324        // Validate the next result immediately for region and mode.
325        result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
326        mCollector.expectEquals("AE mode in result and request should be same",
327                previewRequest.get(CaptureRequest.CONTROL_AE_MODE),
328                result.get(CaptureResult.CONTROL_AE_MODE));
329        if (canSetAeRegion) {
330            MeteringRectangle[] resultAeRegions =
331                    getValueNotNull(result, CaptureResult.CONTROL_AE_REGIONS);
332
333            mCollector.expectMeteringRegionsAreSimilar(
334                    "AE regions in result and request should be similar",
335                    aeRegions,
336                    resultAeRegions,
337                    METERING_REGION_ERROR_PERCENT_DELTA);
338        }
339
340        /**
341         * Step 4: take a picture when all 3A are in good state.
342         */
343        resultListener = new SimpleCaptureCallback();
344        CaptureRequest request = stillRequest.build();
345        mSession.capture(request, resultListener, mHandler);
346        // Validate the next result immediately for region and mode.
347        result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
348        mCollector.expectEquals("AF mode in result and request should be same",
349                stillRequest.get(CaptureRequest.CONTROL_AF_MODE),
350                result.get(CaptureResult.CONTROL_AF_MODE));
351        if (canSetAfRegion) {
352            MeteringRectangle[] resultAfRegions =
353                    getValueNotNull(result, CaptureResult.CONTROL_AF_REGIONS);
354            mCollector.expectMeteringRegionsAreSimilar(
355                    "AF regions in result and request should be similar",
356                    afRegions,
357                    resultAfRegions,
358                    METERING_REGION_ERROR_PERCENT_DELTA);
359        }
360
361        if (hasFocuser) {
362            // Unlock auto focus.
363            focuser.cancelAutoFocus();
364        }
365
366        // validate image
367        Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
368        validateJpegCapture(image, maxStillSz);
369
370        // Free image resources
371        image.close();
372
373        stopPreview();
374    }
375
376    private void fullRawCaptureTestByCamera() throws Exception {
377        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
378        Size maxStillSz = mOrderedStillSizes.get(0);
379
380        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
381        SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
382        SimpleImageReaderListener rawListener = new SimpleImageReaderListener();
383
384        Size size = mStaticInfo.getRawDimensChecked();
385
386        if (VERBOSE) {
387            Log.v(TAG, "Testing multi capture with size " + size.toString()
388                    + ", preview size " + maxPreviewSz);
389        }
390
391        // Prepare raw capture and start preview.
392        CaptureRequest.Builder previewBuilder =
393                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
394        CaptureRequest.Builder multiBuilder =
395                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
396
397        ImageReader rawReader = null;
398        ImageReader jpegReader = null;
399
400        try {
401            // Create ImageReaders.
402            rawReader = makeImageReader(size,
403                    ImageFormat.RAW_SENSOR, MAX_READER_IMAGES, rawListener, mHandler);
404            jpegReader = makeImageReader(maxStillSz,
405                    ImageFormat.JPEG, MAX_READER_IMAGES, jpegListener, mHandler);
406            updatePreviewSurface(maxPreviewSz);
407
408            // Configure output streams with preview and jpeg streams.
409            List<Surface> outputSurfaces = new ArrayList<Surface>();
410            outputSurfaces.add(rawReader.getSurface());
411            outputSurfaces.add(jpegReader.getSurface());
412            outputSurfaces.add(mPreviewSurface);
413            mSessionListener = new BlockingSessionCallback();
414            mSession = configureCameraSession(mCamera, outputSurfaces,
415                    mSessionListener, mHandler);
416
417            // Configure the requests.
418            previewBuilder.addTarget(mPreviewSurface);
419            multiBuilder.addTarget(mPreviewSurface);
420            multiBuilder.addTarget(rawReader.getSurface());
421            multiBuilder.addTarget(jpegReader.getSurface());
422
423            // Start preview.
424            mSession.setRepeatingRequest(previewBuilder.build(), null, mHandler);
425
426            // Poor man's 3A, wait 2 seconds for AE/AF (if any) to settle.
427            // TODO: Do proper 3A trigger and lock (see testTakePictureTest).
428            Thread.sleep(3000);
429
430            multiBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
431                    CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
432            CaptureRequest multiRequest = multiBuilder.build();
433
434            mSession.capture(multiRequest, resultListener, mHandler);
435
436            CaptureResult result = resultListener.getCaptureResultForRequest(multiRequest,
437                    NUM_RESULTS_WAIT_TIMEOUT);
438            Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
439            basicValidateJpegImage(jpegImage, maxStillSz);
440            Image rawImage = rawListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
441            validateRaw16Image(rawImage, size);
442            verifyRawCaptureResult(multiRequest, result);
443
444
445            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
446            try (DngCreator dngCreator = new DngCreator(mStaticInfo.getCharacteristics(), result)) {
447                dngCreator.writeImage(outputStream, rawImage);
448            }
449
450            if (DEBUG) {
451                byte[] rawBuffer = outputStream.toByteArray();
452                String rawFileName = DEBUG_FILE_NAME_BASE + "/raw16_" + TAG + size.toString() +
453                        "_cam_" + mCamera.getId() + ".dng";
454                Log.d(TAG, "Dump raw file into " + rawFileName);
455                dumpFile(rawFileName, rawBuffer);
456
457                byte[] jpegBuffer = getDataFromImage(jpegImage);
458                String jpegFileName = DEBUG_FILE_NAME_BASE + "/jpeg_" + TAG + size.toString() +
459                        "_cam_" + mCamera.getId() + ".jpg";
460                Log.d(TAG, "Dump jpeg file into " + rawFileName);
461                dumpFile(jpegFileName, jpegBuffer);
462            }
463
464            stopPreview();
465        } finally {
466            CameraTestUtils.closeImageReader(rawReader);
467            CameraTestUtils.closeImageReader(jpegReader);
468            rawReader = null;
469            jpegReader = null;
470        }
471    }
472
473    /**
474     * Validate that raw {@link CaptureResult}.
475     *
476     * @param rawRequest a {@link CaptureRequest} use to capture a RAW16 image.
477     * @param rawResult the {@link CaptureResult} corresponding to the given request.
478     */
479    private void verifyRawCaptureResult(CaptureRequest rawRequest, CaptureResult rawResult) {
480        assertNotNull(rawRequest);
481        assertNotNull(rawResult);
482
483        Rational[] empty = new Rational[] { Rational.ZERO, Rational.ZERO, Rational.ZERO};
484        Rational[] neutralColorPoint = mCollector.expectKeyValueNotNull("NeutralColorPoint",
485                rawResult, CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
486        if (neutralColorPoint != null) {
487            mCollector.expectEquals("NeutralColorPoint length", empty.length,
488                    neutralColorPoint.length);
489            mCollector.expectNotEquals("NeutralColorPoint cannot be all zeroes, ", empty,
490                    neutralColorPoint);
491            mCollector.expectValuesGreaterOrEqual("NeutralColorPoint", neutralColorPoint,
492                    Rational.ZERO);
493        }
494
495        mCollector.expectKeyValueGreaterOrEqual(rawResult, CaptureResult.SENSOR_GREEN_SPLIT, 0.0f);
496
497        Pair<Double, Double>[] noiseProfile = mCollector.expectKeyValueNotNull("NoiseProfile",
498                rawResult, CaptureResult.SENSOR_NOISE_PROFILE);
499        if (noiseProfile != null) {
500            mCollector.expectEquals("NoiseProfile length", noiseProfile.length,
501                /*Num CFA channels*/4);
502            for (Pair<Double, Double> p : noiseProfile) {
503                mCollector.expectTrue("NoiseProfile coefficients " + p +
504                        " must have: S > 0, O >= 0", p.first > 0 && p.second >= 0);
505            }
506        }
507
508        Integer hotPixelMode = mCollector.expectKeyValueNotNull("HotPixelMode", rawResult,
509                CaptureResult.HOT_PIXEL_MODE);
510        Boolean hotPixelMapMode = mCollector.expectKeyValueNotNull("HotPixelMapMode", rawResult,
511                CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
512        Point[] hotPixelMap = rawResult.get(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
513
514        Size pixelArraySize = mStaticInfo.getPixelArraySizeChecked();
515        boolean[] availableHotPixelMapModes = mStaticInfo.getValueFromKeyNonNull(
516                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES);
517
518        if (hotPixelMode != null) {
519            Integer requestMode = mCollector.expectKeyValueNotNull(rawRequest,
520                    CaptureRequest.HOT_PIXEL_MODE);
521            if (requestMode != null) {
522                mCollector.expectKeyValueEquals(rawResult, CaptureResult.HOT_PIXEL_MODE,
523                        requestMode);
524            }
525        }
526
527        if (hotPixelMapMode != null) {
528            Boolean requestMapMode = mCollector.expectKeyValueNotNull(rawRequest,
529                    CaptureRequest.STATISTICS_HOT_PIXEL_MAP_MODE);
530            if (requestMapMode != null) {
531                mCollector.expectKeyValueEquals(rawResult,
532                        CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE, requestMapMode);
533            }
534
535            if (!hotPixelMapMode) {
536                mCollector.expectTrue("HotPixelMap must be empty", hotPixelMap == null ||
537                        hotPixelMap.length == 0);
538            } else {
539                mCollector.expectTrue("HotPixelMap must not be empty", hotPixelMap != null);
540                mCollector.expectNotNull("AvailableHotPixelMapModes must not be null",
541                        availableHotPixelMapModes);
542                if (availableHotPixelMapModes != null) {
543                    mCollector.expectContains("HotPixelMapMode", availableHotPixelMapModes, true);
544                }
545
546                int height = pixelArraySize.getHeight();
547                int width = pixelArraySize.getWidth();
548                for (Point p : hotPixelMap) {
549                    mCollector.expectTrue("Hotpixel " + p + " must be in pixelArray " +
550                            pixelArraySize, p.x >= 0 && p.x < width && p.y >= 0 && p.y < height);
551                }
552            }
553        }
554        // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
555
556    }
557
558    //----------------------------------------------------------------
559    //---------Below are common functions for all tests.--------------
560    //----------------------------------------------------------------
561    /**
562     * Validate standard raw (RAW16) capture image.
563     *
564     * @param image The raw16 format image captured
565     * @param rawSize The expected raw size
566     */
567    private static void validateRaw16Image(Image image, Size rawSize) {
568        CameraTestUtils.validateImage(image, rawSize.getWidth(), rawSize.getHeight(),
569                ImageFormat.RAW_SENSOR, /*filePath*/null);
570    }
571
572    /**
573     * Validate JPEG capture image object sanity and test.
574     * <p>
575     * In addition to image object sanity, this function also does the decoding
576     * test, which is slower.
577     * </p>
578     *
579     * @param image The JPEG image to be verified.
580     * @param jpegSize The JPEG capture size to be verified against.
581     */
582    private static void validateJpegCapture(Image image, Size jpegSize) {
583        CameraTestUtils.validateImage(image, jpegSize.getWidth(), jpegSize.getHeight(),
584                ImageFormat.JPEG, /*filePath*/null);
585    }
586
587    private static class SimpleAutoFocusListener implements Camera2Focuser.AutoFocusListener {
588        final ConditionVariable focusDone = new ConditionVariable();
589        @Override
590        public void onAutoFocusLocked(boolean success) {
591            focusDone.open();
592        }
593
594        public void waitForAutoFocusDone(long timeoutMs) {
595            if (focusDone.block(timeoutMs)) {
596                focusDone.close();
597            } else {
598                throw new TimeoutRuntimeException("Wait for auto focus done timed out after "
599                        + timeoutMs + "ms");
600            }
601        }
602    }
603
604    private boolean isRegionsSupportedFor3A(int index) {
605        int maxRegions = 0;
606        switch (index) {
607            case MAX_REGIONS_AE_INDEX:
608                maxRegions = mStaticInfo.getAeMaxRegionsChecked();
609                break;
610            case MAX_REGIONS_AWB_INDEX:
611                maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
612                break;
613            case  MAX_REGIONS_AF_INDEX:
614                maxRegions = mStaticInfo.getAfMaxRegionsChecked();
615                break;
616            default:
617                throw new IllegalArgumentException("Unknown algorithm index");
618        }
619        boolean isRegionsSupported = maxRegions > 0;
620        if (index == MAX_REGIONS_AF_INDEX && isRegionsSupported) {
621            mCollector.expectTrue(
622                    "Device reports non-zero max AF region count for a camera without focuser!",
623                    mStaticInfo.hasFocuser());
624            isRegionsSupported = isRegionsSupported && mStaticInfo.hasFocuser();
625        }
626
627        return isRegionsSupported;
628    }
629}
630