1/*
2 * Copyright 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.helpers;
18
19import junit.framework.Assert;
20
21import android.graphics.ImageFormat;
22import android.graphics.Rect;
23import android.hardware.camera2.CameraCharacteristics;
24import android.hardware.camera2.CameraCharacteristics.Key;
25import android.hardware.camera2.CameraMetadata;
26import android.hardware.camera2.CaptureRequest;
27import android.hardware.camera2.CaptureResult;
28import android.hardware.camera2.params.StreamConfigurationMap;
29import android.util.Log;
30import android.util.Range;
31import android.util.Rational;
32import android.util.Size;
33
34import java.lang.reflect.Array;
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Collection;
38import java.util.HashMap;
39import java.util.HashSet;
40import java.util.List;
41import java.util.Set;
42
43import static com.android.mediaframeworktest.helpers.AssertHelpers.assertArrayContainsAnyOf;
44
45/**
46 * Helpers to get common static info out of the camera.
47 *
48 * <p>Avoid boiler plate by putting repetitive get/set patterns in this class.</p>
49 *
50 * <p>Attempt to be durable against the camera device having bad or missing metadata
51 * by providing reasonable defaults and logging warnings when that happens.</p>
52 */
53/**
54 * (non-Javadoc)
55 * @see android.hardware.camera2.cts.helpers.StaticMetadata
56 */
57public class StaticMetadata {
58
59    private static final String TAG = "StaticMetadata";
60    private static final int IGNORE_SIZE_CHECK = -1;
61
62    private static final long SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST = 100000L; // 100us
63    private static final long SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST = 100000000; // 100ms
64    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST = 100;
65    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST = 800;
66    private static final int STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST = 4;
67    private static final int TONEMAP_MAX_CURVE_POINTS_AT_LEAST = 64;
68    private static final int CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MIN = -2;
69    private static final int CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MAX = 2;
70    private static final Rational CONTROL_AE_COMPENSATION_STEP_DEFAULT = new Rational(1, 2);
71    private static final byte REQUEST_PIPELINE_MAX_DEPTH_MAX = 8;
72    private static final int MAX_REPROCESS_MAX_CAPTURE_STALL = 4;
73
74    // TODO: Consider making this work across any metadata object, not just camera characteristics
75    private final CameraCharacteristics mCharacteristics;
76    private final CheckLevel mLevel;
77    private final CameraErrorCollector mCollector;
78
79    // Index with android.control.aeMode
80    public static final String[] AE_MODE_NAMES = new String[] {
81        "AE_MODE_OFF",
82        "AE_MODE_ON",
83        "AE_MODE_ON_AUTO_FLASH",
84        "AE_MODE_ON_ALWAYS_FLASH",
85        "AE_MODE_ON_AUTO_FLASH_REDEYE"
86    };
87
88    // Index with android.control.afMode
89    public static final String[] AF_MODE_NAMES = new String[] {
90        "AF_MODE_OFF",
91        "AF_MODE_AUTO",
92        "AF_MODE_MACRO",
93        "AF_MODE_CONTINUOUS_VIDEO",
94        "AF_MODE_CONTINUOUS_PICTURE",
95        "AF_MODE_EDOF"
96    };
97
98    // Index with android.control.aeState
99    public static final String[] AE_STATE_NAMES = new String[] {
100        "AE_STATE_INACTIVE",
101        "AE_STATE_SEARCHING",
102        "AE_STATE_CONVERGED",
103        "AE_STATE_LOCKED",
104        "AE_STATE_FLASH_REQUIRED",
105        "AE_STATE_PRECAPTURE"
106    };
107
108    // Index with android.control.afState
109    public static final String[] AF_STATE_NAMES = new String[] {
110        "AF_STATE_INACTIVE",
111        "AF_STATE_PASSIVE_SCAN",
112        "AF_STATE_PASSIVE_FOCUSED",
113        "AF_STATE_ACTIVE_SCAN",
114        "AF_STATE_FOCUSED_LOCKED",
115        "AF_STATE_NOT_FOCUSED_LOCKED",
116        "AF_STATE_PASSIVE_UNFOCUSED"
117    };
118
119    public enum CheckLevel {
120        /** Only log warnings for metadata check failures. Execution continues. */
121        WARN,
122        /**
123         * Use ErrorCollector to collect the metadata check failures, Execution
124         * continues.
125         */
126        COLLECT,
127        /** Assert the metadata check failures. Execution aborts. */
128        ASSERT
129    }
130
131    /**
132     * Construct a new StaticMetadata object.
133     *
134     *<p> Default constructor, only log warnings for the static metadata check failures</p>
135     *
136     * @param characteristics static info for a camera
137     * @throws IllegalArgumentException if characteristics was null
138     */
139    public StaticMetadata(CameraCharacteristics characteristics) {
140        this(characteristics, CheckLevel.WARN, /*collector*/null);
141    }
142
143    /**
144     * Construct a new StaticMetadata object with {@link CameraErrorCollector}.
145     * <p>
146     * When level is not {@link CheckLevel.COLLECT}, the {@link CameraErrorCollector} will be
147     * ignored, otherwise, it will be used to log the check failures.
148     * </p>
149     *
150     * @param characteristics static info for a camera
151     * @param collector The {@link CameraErrorCollector} used by this StaticMetadata
152     * @throws IllegalArgumentException if characteristics or collector was null.
153     */
154    public StaticMetadata(CameraCharacteristics characteristics, CameraErrorCollector collector) {
155        this(characteristics, CheckLevel.COLLECT, collector);
156    }
157
158    /**
159     * Construct a new StaticMetadata object with {@link CheckLevel} and
160     * {@link CameraErrorCollector}.
161     * <p>
162     * When level is not {@link CheckLevel.COLLECT}, the {@link CameraErrorCollector} will be
163     * ignored, otherwise, it will be used to log the check failures.
164     * </p>
165     *
166     * @param characteristics static info for a camera
167     * @param level The {@link CheckLevel} of this StaticMetadata
168     * @param collector The {@link CameraErrorCollector} used by this StaticMetadata
169     * @throws IllegalArgumentException if characteristics was null or level was
170     *         {@link CheckLevel.COLLECT} but collector was null.
171     */
172    public StaticMetadata(CameraCharacteristics characteristics, CheckLevel level,
173            CameraErrorCollector collector) {
174        if (characteristics == null) {
175            throw new IllegalArgumentException("characteristics was null");
176        }
177        if (level == CheckLevel.COLLECT && collector == null) {
178            throw new IllegalArgumentException("collector must valid when COLLECT level is set");
179        }
180
181        mCharacteristics = characteristics;
182        mLevel = level;
183        mCollector = collector;
184    }
185
186    /**
187     * Get the CameraCharacteristics associated with this StaticMetadata.
188     *
189     * @return A non-null CameraCharacteristics object
190     */
191    public CameraCharacteristics getCharacteristics() {
192        return mCharacteristics;
193    }
194
195    /**
196     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
197     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL}.
198     *
199     * <p>If the camera device is not reporting the hardwareLevel, this
200     * will cause the test to fail.</p>
201     *
202     * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
203     */
204    public boolean isHardwareLevelFull() {
205        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
206    }
207
208    /**
209     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
210     * Return the supported hardware level of the device, or fail if no value is reported.
211     *
212     * @return the supported hardware level as a constant defined for
213     *      {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}.
214     */
215    public int getHardwareLevelChecked() {
216        Integer hwLevel = getValueFromKeyNonNull(
217                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
218        if (hwLevel == null) {
219            Assert.fail("No supported hardware level reported.");
220        }
221        return hwLevel;
222    }
223
224    /**
225     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
226     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY}.
227     *
228     * <p>If the camera device is not reporting the hardwareLevel, this
229     * will cause the test to fail.</p>
230     *
231     * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
232     */
233    public boolean isHardwareLevelLegacy() {
234        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
235    }
236
237    /**
238     * Whether or not the per frame control is supported by the camera device.
239     *
240     * @return {@code true} if per frame control is supported, {@code false} otherwise.
241     */
242    public boolean isPerFrameControlSupported() {
243        return getSyncMaxLatency() == CameraMetadata.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
244    }
245
246    /**
247     * Get the maximum number of frames to wait for a request settings being applied
248     *
249     * @return CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN for unknown latency
250     *         CameraMetadata.SYNC_MAX_LATENCY_PER_FRAME_CONTROL for per frame control
251     *         a positive int otherwise
252     */
253    public int getSyncMaxLatency() {
254        Integer value = getValueFromKeyNonNull(CameraCharacteristics.SYNC_MAX_LATENCY);
255        if (value == null) {
256            return CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN;
257        }
258        return value;
259    }
260
261    /**
262     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
263     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
264     *
265     * <p>If the camera device is incorrectly reporting the hardwareLevel, this
266     * will always return {@code true}.</p>
267     *
268     * @return {@code true} if the device is {@code LIMITED}, {@code false} otherwise.
269     */
270    public boolean isHardwareLevelLimited() {
271        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
272    }
273
274    /**
275     * Whether or not the hardware level reported by {@code android.info.supportedHardwareLevel}
276     * is at least {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
277     *
278     * <p>If the camera device is incorrectly reporting the hardwareLevel, this
279     * will always return {@code false}.</p>
280     *
281     * @return
282     *          {@code true} if the device is {@code LIMITED} or {@code FULL},
283     *          {@code false} otherwise (i.e. LEGACY).
284     */
285    public boolean isHardwareLevelLimitedOrBetter() {
286        Integer hwLevel = getValueFromKeyNonNull(
287                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
288
289        if (hwLevel == null) {
290            return false;
291        }
292
293        // Normal. Device could be limited.
294        int hwLevelInt = hwLevel;
295        return hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
296                hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
297    }
298
299    /**
300     * Get the maximum number of partial result a request can expect
301     *
302     * @return 1 if partial result is not supported.
303     *         a integer value larger than 1 if partial result is supported.
304     */
305    public int getPartialResultCount() {
306        Integer value = mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
307        if (value == null) {
308            // Optional key. Default value is 1 if key is missing.
309            return 1;
310        }
311        return value;
312    }
313
314    /**
315     * Get the exposure time value and clamp to the range if needed.
316     *
317     * @param exposure Input exposure time value to check.
318     * @return Exposure value in the legal range.
319     */
320    public long getExposureClampToRange(long exposure) {
321        long minExposure = getExposureMinimumOrDefault(Long.MAX_VALUE);
322        long maxExposure = getExposureMaximumOrDefault(Long.MIN_VALUE);
323        if (minExposure > SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST) {
324            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
325                    String.format(
326                    "Min value %d is too large, set to maximal legal value %d",
327                    minExposure, SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST));
328            minExposure = SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST;
329        }
330        if (maxExposure < SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST) {
331            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
332                    String.format(
333                    "Max value %d is too small, set to minimal legal value %d",
334                    maxExposure, SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST));
335            maxExposure = SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST;
336        }
337
338        return Math.max(minExposure, Math.min(maxExposure, exposure));
339    }
340
341    /**
342     * Check if the camera device support focuser.
343     *
344     * @return true if camera device support focuser, false otherwise.
345     */
346    public boolean hasFocuser() {
347        if (areKeysAvailable(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)) {
348            // LEGACY devices don't have lens.info.minimumFocusDistance, so guard this query
349            return (getMinimumFocusDistanceChecked() > 0);
350        } else {
351            // Check available AF modes
352            int[] availableAfModes = mCharacteristics.get(
353                    CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
354
355            if (availableAfModes == null) {
356                return false;
357            }
358
359            // Assume that if we have an AF mode which doesn't ignore AF trigger, we have a focuser
360            boolean hasFocuser = false;
361            loop: for (int mode : availableAfModes) {
362                switch (mode) {
363                    case CameraMetadata.CONTROL_AF_MODE_AUTO:
364                    case CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
365                    case CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
366                    case CameraMetadata.CONTROL_AF_MODE_MACRO:
367                        hasFocuser = true;
368                        break loop;
369                }
370            }
371
372            return hasFocuser;
373        }
374    }
375
376    /**
377     * Check if the camera device has flash unit.
378     * @return true if flash unit is available, false otherwise.
379     */
380    public boolean hasFlash() {
381        return getFlashInfoChecked();
382    }
383
384    /**
385     * Get minimum focus distance.
386     *
387     * @return minimum focus distance, 0 if minimum focus distance is invalid.
388     */
389    public float getMinimumFocusDistanceChecked() {
390        Key<Float> key = CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE;
391        Float minFocusDistance;
392
393        /**
394         * android.lens.info.minimumFocusDistance - required for FULL and MANUAL_SENSOR-capable
395         *   devices; optional for all other devices.
396         */
397        if (isHardwareLevelFull() || isCapabilitySupported(
398                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
399            minFocusDistance = getValueFromKeyNonNull(key);
400        } else {
401            minFocusDistance = mCharacteristics.get(key);
402        }
403
404        if (minFocusDistance == null) {
405            return 0.0f;
406        }
407
408        checkTrueForKey(key, " minFocusDistance value shouldn't be negative",
409                minFocusDistance >= 0);
410        if (minFocusDistance < 0) {
411            minFocusDistance = 0.0f;
412        }
413
414        return minFocusDistance;
415    }
416
417    /**
418     * Get focusDistanceCalibration.
419     *
420     * @return focusDistanceCalibration, UNCALIBRATED if value is invalid.
421     */
422    public int getFocusDistanceCalibrationChecked() {
423        Key<Integer> key = CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
424        Integer calibration = getValueFromKeyNonNull(key);
425
426        if (calibration == null) {
427            return CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
428        }
429
430        checkTrueForKey(key, " value is out of range" ,
431                calibration >= CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED &&
432                calibration <= CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED);
433
434        return calibration;
435    }
436
437    /**
438     * Get max AE regions and do sanity check.
439     *
440     * @return AE max regions supported by the camera device
441     */
442    public int getAeMaxRegionsChecked() {
443        Integer regionCount = mCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
444        if (regionCount == null) {
445            return 0;
446        }
447        return regionCount;
448    }
449
450    /**
451     * Get max AWB regions and do sanity check.
452     *
453     * @return AWB max regions supported by the camera device
454     */
455    public int getAwbMaxRegionsChecked() {
456        Integer regionCount = mCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
457        if (regionCount == null) {
458            return 0;
459        }
460        return regionCount;
461    }
462
463    /**
464     * Get max AF regions and do sanity check.
465     *
466     * @return AF max regions supported by the camera device
467     */
468    public int getAfMaxRegionsChecked() {
469        Integer regionCount = mCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
470        if (regionCount == null) {
471            return 0;
472        }
473        return regionCount;
474    }
475    /**
476     * Get the available anti-banding modes.
477     *
478     * @return The array contains available anti-banding modes.
479     */
480    public int[] getAeAvailableAntiBandingModesChecked() {
481        Key<int[]> key = CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
482        int[] modes = getValueFromKeyNonNull(key);
483
484        boolean foundAuto = false;
485        boolean found50Hz = false;
486        boolean found60Hz = false;
487        for (int mode : modes) {
488            checkTrueForKey(key, "mode value " + mode + " is out if range",
489                    mode >= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF ||
490                    mode <= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
491            if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO) {
492                foundAuto = true;
493            } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ) {
494                found50Hz = true;
495            } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ) {
496                found60Hz = true;
497            }
498        }
499        // Must contain AUTO mode or one of 50/60Hz mode.
500        checkTrueForKey(key, "Either AUTO mode or both 50HZ/60HZ mode should present",
501                foundAuto || (found50Hz && found60Hz));
502
503        return modes;
504    }
505
506    /**
507     * Check if the antibanding OFF mode is supported.
508     *
509     * @return true if antibanding OFF mode is supported, false otherwise.
510     */
511    public boolean isAntiBandingOffModeSupported() {
512        List<Integer> antiBandingModes =
513                Arrays.asList(CameraTestUtils.toObject(getAeAvailableAntiBandingModesChecked()));
514
515        return antiBandingModes.contains(CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF);
516    }
517
518    public Boolean getFlashInfoChecked() {
519        Key<Boolean> key = CameraCharacteristics.FLASH_INFO_AVAILABLE;
520        Boolean hasFlash = getValueFromKeyNonNull(key);
521
522        // In case the failOnKey only gives warning.
523        if (hasFlash == null) {
524            return false;
525        }
526
527        return hasFlash;
528    }
529
530    public int[] getAvailableTestPatternModesChecked() {
531        Key<int[]> key =
532                CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES;
533        int[] modes = getValueFromKeyNonNull(key);
534
535        if (modes == null) {
536            return new int[0];
537        }
538
539        int expectValue = CameraCharacteristics.SENSOR_TEST_PATTERN_MODE_OFF;
540        Integer[] boxedModes = CameraTestUtils.toObject(modes);
541        checkTrueForKey(key, " value must contain OFF mode",
542                Arrays.asList(boxedModes).contains(expectValue));
543
544        return modes;
545    }
546
547    /**
548     * Get available thumbnail sizes and do the sanity check.
549     *
550     * @return The array of available thumbnail sizes
551     */
552    public Size[] getAvailableThumbnailSizesChecked() {
553        Key<Size[]> key = CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES;
554        Size[] sizes = getValueFromKeyNonNull(key);
555        final List<Size> sizeList = Arrays.asList(sizes);
556
557        // Size must contain (0, 0).
558        checkTrueForKey(key, "size should contain (0, 0)", sizeList.contains(new Size(0, 0)));
559
560        // Each size must be distinct.
561        checkElementDistinct(key, sizeList);
562
563        // Must be sorted in ascending order by area, by width if areas are same.
564        List<Size> orderedSizes =
565                CameraTestUtils.getAscendingOrderSizes(sizeList, /*ascending*/true);
566        checkTrueForKey(key, "Sizes should be in ascending order: Original " + sizeList.toString()
567                + ", Expected " + orderedSizes.toString(), orderedSizes.equals(sizeList));
568
569        // TODO: Aspect ratio match, need wait for android.scaler.availableStreamConfigurations
570        // implementation see b/12958122.
571
572        return sizes;
573    }
574
575    /**
576     * Get available focal lengths and do the sanity check.
577     *
578     * @return The array of available focal lengths
579     */
580    public float[] getAvailableFocalLengthsChecked() {
581        Key<float[]> key = CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
582        float[] focalLengths = getValueFromKeyNonNull(key);
583
584        checkTrueForKey(key, "Array should contain at least one element", focalLengths.length >= 1);
585
586        for (int i = 0; i < focalLengths.length; i++) {
587            checkTrueForKey(key,
588                    String.format("focalLength[%d] %f should be positive.", i, focalLengths[i]),
589                    focalLengths[i] > 0);
590        }
591        checkElementDistinct(key, Arrays.asList(CameraTestUtils.toObject(focalLengths)));
592
593        return focalLengths;
594    }
595
596    /**
597     * Get available apertures and do the sanity check.
598     *
599     * @return The non-null array of available apertures
600     */
601    public float[] getAvailableAperturesChecked() {
602        Key<float[]> key = CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES;
603        float[] apertures = getValueFromKeyNonNull(key);
604
605        checkTrueForKey(key, "Array should contain at least one element", apertures.length >= 1);
606
607        for (int i = 0; i < apertures.length; i++) {
608            checkTrueForKey(key,
609                    String.format("apertures[%d] %f should be positive.", i, apertures[i]),
610                    apertures[i] > 0);
611        }
612        checkElementDistinct(key, Arrays.asList(CameraTestUtils.toObject(apertures)));
613
614        return apertures;
615    }
616
617    /**
618     * Get and check the available hot pixel map modes.
619     *
620     * @return the available hot pixel map modes
621     */
622    public int[] getAvailableHotPixelModesChecked() {
623        Key<int[]> key = CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
624        int[] modes = getValueFromKeyNonNull(key);
625
626        if (modes == null) {
627            return new int[0];
628        }
629
630        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
631        if (isHardwareLevelFull()) {
632            checkTrueForKey(key, "Full-capability camera devices must support FAST mode",
633                    modeList.contains(CameraMetadata.HOT_PIXEL_MODE_FAST));
634        }
635
636        if (isHardwareLevelLimitedOrBetter()) {
637            // FAST and HIGH_QUALITY mode must be both present or both not present
638            List<Integer> coupledModes = Arrays.asList(new Integer[] {
639                    CameraMetadata.HOT_PIXEL_MODE_FAST,
640                    CameraMetadata.HOT_PIXEL_MODE_HIGH_QUALITY
641            });
642            checkTrueForKey(
643                    key, " FAST and HIGH_QUALITY mode must both present or both not present",
644                    containsAllOrNone(modeList, coupledModes));
645        }
646        checkElementDistinct(key, modeList);
647        checkArrayValuesInRange(key, modes, CameraMetadata.HOT_PIXEL_MODE_OFF,
648                CameraMetadata.HOT_PIXEL_MODE_HIGH_QUALITY);
649
650        return modes;
651    }
652
653    /**
654     * Get and check available face detection modes.
655     *
656     * @return The non-null array of available face detection modes
657     */
658    public int[] getAvailableFaceDetectModesChecked() {
659        Key<int[]> key = CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
660        int[] modes = getValueFromKeyNonNull(key);
661
662        if (modes == null) {
663            return new int[0];
664        }
665
666        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
667        checkTrueForKey(key, "Array should contain OFF mode",
668                modeList.contains(CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF));
669        checkElementDistinct(key, modeList);
670        checkArrayValuesInRange(key, modes, CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF,
671                CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL);
672
673        return modes;
674    }
675
676    /**
677     * Get and check max face detected count.
678     *
679     * @return max number of faces that can be detected
680     */
681    public int getMaxFaceCountChecked() {
682        Key<Integer> key = CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT;
683        Integer count = getValueFromKeyNonNull(key);
684
685        if (count == null) {
686            return 0;
687        }
688
689        List<Integer> faceDetectModes =
690                Arrays.asList(CameraTestUtils.toObject(getAvailableFaceDetectModesChecked()));
691        if (faceDetectModes.contains(CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF) &&
692                faceDetectModes.size() == 1) {
693            checkTrueForKey(key, " value must be 0 if only OFF mode is supported in "
694                    + "availableFaceDetectionModes", count == 0);
695        } else {
696            int maxFaceCountAtLeast = STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST;
697
698            // Legacy mode may support fewer than STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST faces.
699            if (isHardwareLevelLegacy()) {
700                maxFaceCountAtLeast = 1;
701            }
702            checkTrueForKey(key, " value must be no less than " + maxFaceCountAtLeast + " if SIMPLE"
703                    + "or FULL is also supported in availableFaceDetectionModes",
704                    count >= maxFaceCountAtLeast);
705        }
706
707        return count;
708    }
709
710    /**
711     * Get and check the available tone map modes.
712     *
713     * @return the available tone map modes
714     */
715    public int[] getAvailableToneMapModesChecked() {
716        Key<int[]> key = CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES;
717        int[] modes = getValueFromKeyNonNull(key);
718
719        if (modes == null) {
720            return new int[0];
721        }
722
723        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
724        checkTrueForKey(key, " Camera devices must always support FAST mode",
725                modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
726        // Qualification check for MANUAL_POSTPROCESSING capability is in
727        // StaticMetadataTest#testCapabilities
728
729        if (isHardwareLevelLimitedOrBetter()) {
730            // FAST and HIGH_QUALITY mode must be both present or both not present
731            List<Integer> coupledModes = Arrays.asList(new Integer[] {
732                    CameraMetadata.TONEMAP_MODE_FAST,
733                    CameraMetadata.TONEMAP_MODE_HIGH_QUALITY
734            });
735            checkTrueForKey(
736                    key, " FAST and HIGH_QUALITY mode must both present or both not present",
737                    containsAllOrNone(modeList, coupledModes));
738        }
739        checkElementDistinct(key, modeList);
740        checkArrayValuesInRange(key, modes, CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE,
741                CameraMetadata.TONEMAP_MODE_PRESET_CURVE);
742
743        return modes;
744    }
745
746    /**
747     * Get and check max tonemap curve point.
748     *
749     * @return Max tonemap curve points.
750     */
751    public int getMaxTonemapCurvePointChecked() {
752        Key<Integer> key = CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS;
753        Integer count = getValueFromKeyNonNull(key);
754        List<Integer> modeList =
755                Arrays.asList(CameraTestUtils.toObject(getAvailableToneMapModesChecked()));
756        boolean tonemapCurveOutputSupported =
757                modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE) ||
758                modeList.contains(CameraMetadata.TONEMAP_MODE_GAMMA_VALUE) ||
759                modeList.contains(CameraMetadata.TONEMAP_MODE_PRESET_CURVE);
760
761        if (count == null) {
762            if (tonemapCurveOutputSupported) {
763                Assert.fail("Tonemap curve output is supported but MAX_CURVE_POINTS is null");
764            }
765            return 0;
766        }
767
768        if (tonemapCurveOutputSupported) {
769            checkTrueForKey(key, "Tonemap curve output supported camera device must support "
770                    + "maxCurvePoints >= " + TONEMAP_MAX_CURVE_POINTS_AT_LEAST,
771                    count >= TONEMAP_MAX_CURVE_POINTS_AT_LEAST);
772        }
773
774        return count;
775    }
776
777    /**
778     * Get and check pixel array size.
779     */
780    public Size getPixelArraySizeChecked() {
781        Key<Size> key = CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE;
782        Size pixelArray = getValueFromKeyNonNull(key);
783        if (pixelArray == null) {
784            return new Size(0, 0);
785        }
786
787        return pixelArray;
788    }
789
790    /**
791     * Get and check pre-correction active array size.
792     */
793    public Rect getPreCorrectedActiveArraySizeChecked() {
794        Key<Rect> key = CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE;
795        Rect activeArray = getValueFromKeyNonNull(key);
796
797        if (activeArray == null) {
798            return new Rect(0, 0, 0, 0);
799        }
800
801        Size pixelArraySize = getPixelArraySizeChecked();
802        checkTrueForKey(key, "values left/top are invalid", activeArray.left >= 0 && activeArray.top >= 0);
803        checkTrueForKey(key, "values width/height are invalid",
804                activeArray.width() <= pixelArraySize.getWidth() &&
805                activeArray.height() <= pixelArraySize.getHeight());
806
807        return activeArray;
808    }
809
810    /**
811     * Get and check active array size.
812     */
813    public Rect getActiveArraySizeChecked() {
814        Key<Rect> key = CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE;
815        Rect activeArray = getValueFromKeyNonNull(key);
816
817        if (activeArray == null) {
818            return new Rect(0, 0, 0, 0);
819        }
820
821        Size pixelArraySize = getPixelArraySizeChecked();
822        checkTrueForKey(key, "values left/top are invalid", activeArray.left >= 0 && activeArray.top >= 0);
823        checkTrueForKey(key, "values width/height are invalid",
824                activeArray.width() <= pixelArraySize.getWidth() &&
825                activeArray.height() <= pixelArraySize.getHeight());
826
827        return activeArray;
828    }
829
830    /**
831     * Get the dimensions to use for RAW16 buffers.
832     */
833    public Size getRawDimensChecked() throws Exception {
834        Size[] targetCaptureSizes = getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
835                        StaticMetadata.StreamDirection.Output);
836        Assert.assertTrue("No capture sizes available for RAW format!",
837                targetCaptureSizes.length != 0);
838        Rect activeArray = getPreCorrectedActiveArraySizeChecked();
839        Size preCorrectionActiveArraySize =
840                new Size(activeArray.width(), activeArray.height());
841        Size pixelArraySize = getPixelArraySizeChecked();
842        Assert.assertTrue("Missing pre-correction active array size", activeArray.width() > 0 &&
843                activeArray.height() > 0);
844        Assert.assertTrue("Missing pixel array size", pixelArraySize.getWidth() > 0 &&
845                pixelArraySize.getHeight() > 0);
846        Size[] allowedArraySizes = new Size[] { preCorrectionActiveArraySize,
847                pixelArraySize };
848        return assertArrayContainsAnyOf("Available sizes for RAW format" +
849                " must include either the pre-corrected active array size, or the full " +
850                "pixel array size", targetCaptureSizes, allowedArraySizes);
851    }
852
853    /**
854     * Get the sensitivity value and clamp to the range if needed.
855     *
856     * @param sensitivity Input sensitivity value to check.
857     * @return Sensitivity value in legal range.
858     */
859    public int getSensitivityClampToRange(int sensitivity) {
860        int minSensitivity = getSensitivityMinimumOrDefault(Integer.MAX_VALUE);
861        int maxSensitivity = getSensitivityMaximumOrDefault(Integer.MIN_VALUE);
862        if (minSensitivity > SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST) {
863            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
864                    String.format(
865                    "Min value %d is too large, set to maximal legal value %d",
866                    minSensitivity, SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST));
867            minSensitivity = SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST;
868        }
869        if (maxSensitivity < SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST) {
870            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
871                    String.format(
872                    "Max value %d is too small, set to minimal legal value %d",
873                    maxSensitivity, SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST));
874            maxSensitivity = SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST;
875        }
876
877        return Math.max(minSensitivity, Math.min(maxSensitivity, sensitivity));
878    }
879
880    /**
881     * Get maxAnalogSensitivity for a camera device.
882     * <p>
883     * This is only available for FULL capability device, return 0 if it is unavailable.
884     * </p>
885     *
886     * @return maxAnalogSensitivity, 0 if it is not available.
887     */
888    public int getMaxAnalogSensitivityChecked() {
889
890        Key<Integer> key = CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY;
891        Integer maxAnalogsensitivity = mCharacteristics.get(key);
892        if (maxAnalogsensitivity == null) {
893            if (isHardwareLevelFull()) {
894                Assert.fail("Full device should report max analog sensitivity");
895            }
896            return 0;
897        }
898
899        int minSensitivity = getSensitivityMinimumOrDefault();
900        int maxSensitivity = getSensitivityMaximumOrDefault();
901        checkTrueForKey(key, " Max analog sensitivity " + maxAnalogsensitivity
902                + " should be no larger than max sensitivity " + maxSensitivity,
903                maxAnalogsensitivity <= maxSensitivity);
904        checkTrueForKey(key, " Max analog sensitivity " + maxAnalogsensitivity
905                + " should be larger than min sensitivity " + maxSensitivity,
906                maxAnalogsensitivity > minSensitivity);
907
908        return maxAnalogsensitivity;
909    }
910
911    /**
912     * Get hyperfocalDistance and do the sanity check.
913     * <p>
914     * Note that, this tag is optional, will return -1 if this tag is not
915     * available.
916     * </p>
917     *
918     * @return hyperfocalDistance of this device, -1 if this tag is not available.
919     */
920    public float getHyperfocalDistanceChecked() {
921        Key<Float> key = CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE;
922        Float hyperfocalDistance = getValueFromKeyNonNull(key);
923        if (hyperfocalDistance == null) {
924            return -1;
925        }
926
927        if (hasFocuser()) {
928            float minFocusDistance = getMinimumFocusDistanceChecked();
929            checkTrueForKey(key, String.format(" hyperfocal distance %f should be in the range of"
930                    + " should be in the range of (%f, %f]", hyperfocalDistance, 0.0f,
931                    minFocusDistance),
932                    hyperfocalDistance > 0 && hyperfocalDistance <= minFocusDistance);
933        }
934
935        return hyperfocalDistance;
936    }
937
938    /**
939     * Get the minimum value for a sensitivity range from android.sensor.info.sensitivityRange.
940     *
941     * <p>If the camera is incorrectly reporting values, log a warning and return
942     * the default value instead, which is the largest minimum value required to be supported
943     * by all camera devices.</p>
944     *
945     * @return The value reported by the camera device or the defaultValue otherwise.
946     */
947    public int getSensitivityMinimumOrDefault() {
948        return getSensitivityMinimumOrDefault(SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST);
949    }
950
951    /**
952     * Get the minimum value for a sensitivity range from android.sensor.info.sensitivityRange.
953     *
954     * <p>If the camera is incorrectly reporting values, log a warning and return
955     * the default value instead.</p>
956     *
957     * @param defaultValue Value to return if no legal value is available
958     * @return The value reported by the camera device or the defaultValue otherwise.
959     */
960    public int getSensitivityMinimumOrDefault(int defaultValue) {
961        Range<Integer> range = getValueFromKeyNonNull(
962                CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
963        if (range == null) {
964            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
965                    "had no valid minimum value; using default of " + defaultValue);
966            return defaultValue;
967        }
968        return range.getLower();
969    }
970
971    /**
972     * Get the maximum value for a sensitivity range from android.sensor.info.sensitivityRange.
973     *
974     * <p>If the camera is incorrectly reporting values, log a warning and return
975     * the default value instead, which is the smallest maximum value required to be supported
976     * by all camera devices.</p>
977     *
978     * @return The value reported by the camera device or the defaultValue otherwise.
979     */
980    public int getSensitivityMaximumOrDefault() {
981        return getSensitivityMaximumOrDefault(SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST);
982    }
983
984    /**
985     * Get the maximum value for a sensitivity range from android.sensor.info.sensitivityRange.
986     *
987     * <p>If the camera is incorrectly reporting values, log a warning and return
988     * the default value instead.</p>
989     *
990     * @param defaultValue Value to return if no legal value is available
991     * @return The value reported by the camera device or the defaultValue otherwise.
992     */
993    public int getSensitivityMaximumOrDefault(int defaultValue) {
994        Range<Integer> range = getValueFromKeyNonNull(
995                CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
996        if (range == null) {
997            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
998                    "had no valid maximum value; using default of " + defaultValue);
999            return defaultValue;
1000        }
1001        return range.getUpper();
1002    }
1003
1004    /**
1005     * Get the minimum value for an exposure range from android.sensor.info.exposureTimeRange.
1006     *
1007     * <p>If the camera is incorrectly reporting values, log a warning and return
1008     * the default value instead.</p>
1009     *
1010     * @param defaultValue Value to return if no legal value is available
1011     * @return The value reported by the camera device or the defaultValue otherwise.
1012     */
1013    public long getExposureMinimumOrDefault(long defaultValue) {
1014        Range<Long> range = getValueFromKeyNonNull(
1015                CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
1016        if (range == null) {
1017            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
1018                    "had no valid minimum value; using default of " + defaultValue);
1019            return defaultValue;
1020        }
1021        return range.getLower();
1022    }
1023
1024    /**
1025     * Get the minimum value for an exposure range from android.sensor.info.exposureTimeRange.
1026     *
1027     * <p>If the camera is incorrectly reporting values, log a warning and return
1028     * the default value instead, which is the largest minimum value required to be supported
1029     * by all camera devices.</p>
1030     *
1031     * @return The value reported by the camera device or the defaultValue otherwise.
1032     */
1033    public long getExposureMinimumOrDefault() {
1034        return getExposureMinimumOrDefault(SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST);
1035    }
1036
1037    /**
1038     * Get the maximum value for an exposure range from android.sensor.info.exposureTimeRange.
1039     *
1040     * <p>If the camera is incorrectly reporting values, log a warning and return
1041     * the default value instead.</p>
1042     *
1043     * @param defaultValue Value to return if no legal value is available
1044     * @return The value reported by the camera device or the defaultValue otherwise.
1045     */
1046    public long getExposureMaximumOrDefault(long defaultValue) {
1047        Range<Long> range = getValueFromKeyNonNull(
1048                CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
1049        if (range == null) {
1050            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
1051                    "had no valid maximum value; using default of " + defaultValue);
1052            return defaultValue;
1053        }
1054        return range.getUpper();
1055    }
1056
1057    /**
1058     * Get the maximum value for an exposure range from android.sensor.info.exposureTimeRange.
1059     *
1060     * <p>If the camera is incorrectly reporting values, log a warning and return
1061     * the default value instead, which is the smallest maximum value required to be supported
1062     * by all camera devices.</p>
1063     *
1064     * @return The value reported by the camera device or the defaultValue otherwise.
1065     */
1066    public long getExposureMaximumOrDefault() {
1067        return getExposureMaximumOrDefault(SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST);
1068    }
1069
1070    /**
1071     * get android.control.availableModes and do the sanity check.
1072     *
1073     * @return available control modes.
1074     */
1075    public int[] getAvailableControlModesChecked() {
1076        Key<int[]> modesKey = CameraCharacteristics.CONTROL_AVAILABLE_MODES;
1077        int[] modes = getValueFromKeyNonNull(modesKey);
1078        if (modes == null) {
1079            modes = new int[0];
1080        }
1081
1082        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1083        checkTrueForKey(modesKey, "value is empty", !modeList.isEmpty());
1084
1085        // All camera device must support AUTO
1086        checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain AUTO mode",
1087                modeList.contains(CameraMetadata.CONTROL_MODE_AUTO));
1088
1089        boolean isAeOffSupported =  Arrays.asList(
1090                CameraTestUtils.toObject(getAeAvailableModesChecked())).contains(
1091                        CameraMetadata.CONTROL_AE_MODE_OFF);
1092        boolean isAfOffSupported =  Arrays.asList(
1093                CameraTestUtils.toObject(getAfAvailableModesChecked())).contains(
1094                        CameraMetadata.CONTROL_AF_MODE_OFF);
1095        boolean isAwbOffSupported =  Arrays.asList(
1096                CameraTestUtils.toObject(getAwbAvailableModesChecked())).contains(
1097                        CameraMetadata.CONTROL_AWB_MODE_OFF);
1098        if (isAeOffSupported && isAfOffSupported && isAwbOffSupported) {
1099            // 3A OFF controls are supported, OFF mode must be supported here.
1100            checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain OFF mode",
1101                    modeList.contains(CameraMetadata.CONTROL_MODE_OFF));
1102        }
1103
1104        if (isSceneModeSupported()) {
1105            checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain"
1106                    + " USE_SCENE_MODE",
1107                    modeList.contains(CameraMetadata.CONTROL_MODE_USE_SCENE_MODE));
1108        }
1109
1110        return modes;
1111    }
1112
1113    public boolean isSceneModeSupported() {
1114        List<Integer> availableSceneModes = Arrays.asList(
1115                CameraTestUtils.toObject(getAvailableSceneModesChecked()));
1116
1117        if (availableSceneModes.isEmpty()) {
1118            return false;
1119        }
1120
1121        // If sceneMode is not supported, camera device will contain single entry: DISABLED.
1122        return availableSceneModes.size() > 1 ||
1123                !availableSceneModes.contains(CameraMetadata.CONTROL_SCENE_MODE_DISABLED);
1124    }
1125
1126    /**
1127     * Get aeAvailableModes and do the sanity check.
1128     *
1129     * <p>Depending on the check level this class has, for WAR or COLLECT levels,
1130     * If the aeMode list is invalid, return an empty mode array. The the caller doesn't
1131     * have to abort the execution even the aeMode list is invalid.</p>
1132     * @return AE available modes
1133     */
1134    public int[] getAeAvailableModesChecked() {
1135        Key<int[]> modesKey = CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES;
1136        int[] modes = getValueFromKeyNonNull(modesKey);
1137        if (modes == null) {
1138            modes = new int[0];
1139        }
1140        List<Integer> modeList = new ArrayList<Integer>();
1141        for (int mode : modes) {
1142            // Skip vendor-added modes
1143            if (mode <= CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
1144                modeList.add(mode);
1145            }
1146        }
1147        checkTrueForKey(modesKey, "value is empty", !modeList.isEmpty());
1148        modes = new int[modeList.size()];
1149        for (int i = 0; i < modeList.size(); i++) {
1150            modes[i] = modeList.get(i);
1151        }
1152
1153        checkTrueForKey(modesKey, "value is empty", !modeList.isEmpty());
1154
1155        // All camera device must support ON
1156        checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain ON mode",
1157                modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON));
1158
1159        // All camera devices with flash units support ON_AUTO_FLASH and ON_ALWAYS_FLASH
1160        Key<Boolean> flashKey= CameraCharacteristics.FLASH_INFO_AVAILABLE;
1161        Boolean hasFlash = getValueFromKeyNonNull(flashKey);
1162        if (hasFlash == null) {
1163            hasFlash = false;
1164        }
1165        if (hasFlash) {
1166            boolean flashModeConsistentWithFlash =
1167                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) &&
1168                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
1169            checkTrueForKey(modesKey,
1170                    "value must contain ON_AUTO_FLASH and ON_ALWAYS_FLASH and  when flash is" +
1171                    "available", flashModeConsistentWithFlash);
1172        } else {
1173            boolean flashModeConsistentWithoutFlash =
1174                    !(modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) ||
1175                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH) ||
1176                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE));
1177            checkTrueForKey(modesKey,
1178                    "value must not contain ON_AUTO_FLASH, ON_ALWAYS_FLASH and" +
1179                    "ON_AUTO_FLASH_REDEYE when flash is unavailable",
1180                    flashModeConsistentWithoutFlash);
1181        }
1182
1183        // FULL mode camera devices always support OFF mode.
1184        boolean condition =
1185                !isHardwareLevelFull() || modeList.contains(CameraMetadata.CONTROL_AE_MODE_OFF);
1186        checkTrueForKey(modesKey, "Full capability device must have OFF mode", condition);
1187
1188        // Boundary check.
1189        for (int mode : modes) {
1190            checkTrueForKey(modesKey, "Value " + mode + " is out of bound",
1191                    mode >= CameraMetadata.CONTROL_AE_MODE_OFF
1192                    && mode <= CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
1193        }
1194
1195        return modes;
1196    }
1197
1198    /**
1199     * Get available AWB modes and do the sanity check.
1200     *
1201     * @return array that contains available AWB modes, empty array if awbAvailableModes is
1202     * unavailable.
1203     */
1204    public int[] getAwbAvailableModesChecked() {
1205        Key<int[]> key =
1206                CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES;
1207        int[] awbModes = getValueFromKeyNonNull(key);
1208
1209        if (awbModes == null) {
1210            return new int[0];
1211        }
1212
1213        List<Integer> modesList = Arrays.asList(CameraTestUtils.toObject(awbModes));
1214        checkTrueForKey(key, " All camera devices must support AUTO mode",
1215                modesList.contains(CameraMetadata.CONTROL_AWB_MODE_AUTO));
1216        if (isHardwareLevelFull()) {
1217            checkTrueForKey(key, " Full capability camera devices must support OFF mode",
1218                    modesList.contains(CameraMetadata.CONTROL_AWB_MODE_OFF));
1219        }
1220
1221        return awbModes;
1222    }
1223
1224    /**
1225     * Get available AF modes and do the sanity check.
1226     *
1227     * @return array that contains available AF modes, empty array if afAvailableModes is
1228     * unavailable.
1229     */
1230    public int[] getAfAvailableModesChecked() {
1231        Key<int[]> key =
1232                CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES;
1233        int[] afModes = getValueFromKeyNonNull(key);
1234
1235        if (afModes == null) {
1236            return new int[0];
1237        }
1238
1239        List<Integer> modesList = new ArrayList<Integer>();
1240        for (int afMode : afModes) {
1241            // Skip vendor-added AF modes
1242            if (afMode > CameraCharacteristics.CONTROL_AF_MODE_EDOF) continue;
1243            modesList.add(afMode);
1244        }
1245        afModes = new int[modesList.size()];
1246        for (int i = 0; i < modesList.size(); i++) {
1247            afModes[i] = modesList.get(i);
1248        }
1249
1250        if (isHardwareLevelLimitedOrBetter()) {
1251            // Some LEGACY mode devices do not support AF OFF
1252            checkTrueForKey(key, " All camera devices must support OFF mode",
1253                    modesList.contains(CameraMetadata.CONTROL_AF_MODE_OFF));
1254        }
1255        if (hasFocuser()) {
1256            checkTrueForKey(key, " Camera devices that have focuser units must support AUTO mode",
1257                    modesList.contains(CameraMetadata.CONTROL_AF_MODE_AUTO));
1258        }
1259
1260        return afModes;
1261    }
1262
1263    /**
1264     * Get supported raw output sizes and do the check.
1265     *
1266     * @return Empty size array if raw output is not supported
1267     */
1268    public Size[] getRawOutputSizesChecked() {
1269        return getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
1270                StreamDirection.Output);
1271    }
1272
1273    /**
1274     * Get supported jpeg output sizes and do the check.
1275     *
1276     * @return Empty size array if jpeg output is not supported
1277     */
1278    public Size[] getJpegOutputSizesChecked() {
1279        return getAvailableSizesForFormatChecked(ImageFormat.JPEG,
1280                StreamDirection.Output);
1281    }
1282
1283    /**
1284     * Used to determine the stream direction for various helpers that look up
1285     * format or size information.
1286     */
1287    public enum StreamDirection {
1288        /** Stream is used with {@link android.hardware.camera2.CameraDevice#configureOutputs} */
1289        Output,
1290        /** Stream is used with {@code CameraDevice#configureInputs} -- NOT YET PUBLIC */
1291        Input
1292    }
1293
1294    /**
1295     * Get available formats for a given direction.
1296     *
1297     * @param direction The stream direction, input or output.
1298     * @return The formats of the given direction, empty array if no available format is found.
1299     */
1300    public int[] getAvailableFormats(StreamDirection direction) {
1301        Key<StreamConfigurationMap> key =
1302                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
1303        StreamConfigurationMap config = getValueFromKeyNonNull(key);
1304
1305        if (config == null) {
1306            return new int[0];
1307        }
1308
1309        switch (direction) {
1310            case Output:
1311                return config.getOutputFormats();
1312            case Input:
1313                return config.getInputFormats();
1314            default:
1315                throw new IllegalArgumentException("direction must be output or input");
1316        }
1317    }
1318
1319    /**
1320     * Get valid output formats for a given input format.
1321     *
1322     * @param inputFormat The input format used to produce the output images.
1323     * @return The output formats for the given input format, empty array if
1324     *         no available format is found.
1325     */
1326    public int[] getValidOutputFormatsForInput(int inputFormat) {
1327        Key<StreamConfigurationMap> key =
1328                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
1329        StreamConfigurationMap config = getValueFromKeyNonNull(key);
1330
1331        if (config == null) {
1332            return new int[0];
1333        }
1334
1335        return config.getValidOutputFormatsForInput(inputFormat);
1336    }
1337
1338    /**
1339     * Get available sizes for given format and direction.
1340     *
1341     * @param format The format for the requested size array.
1342     * @param direction The stream direction, input or output.
1343     * @return The sizes of the given format, empty array if no available size is found.
1344     */
1345    public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction) {
1346        return getAvailableSizesForFormatChecked(format, direction,
1347                /*fastSizes*/true, /*slowSizes*/true);
1348    }
1349
1350    /**
1351     * Get available sizes for given format and direction, and whether to limit to slow or fast
1352     * resolutions.
1353     *
1354     * @param format The format for the requested size array.
1355     * @param direction The stream direction, input or output.
1356     * @param fastSizes whether to include getOutputSizes() sizes (generally faster)
1357     * @param slowSizes whether to include getHighResolutionOutputSizes() sizes (generally slower)
1358     * @return The sizes of the given format, empty array if no available size is found.
1359     */
1360    public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction,
1361            boolean fastSizes, boolean slowSizes) {
1362        Key<StreamConfigurationMap> key =
1363                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
1364        StreamConfigurationMap config = getValueFromKeyNonNull(key);
1365
1366        if (config == null) {
1367            return new Size[0];
1368        }
1369
1370        Size[] sizes = null;
1371
1372        switch (direction) {
1373            case Output:
1374                Size[] fastSizeList = null;
1375                Size[] slowSizeList = null;
1376                if (fastSizes) {
1377                    fastSizeList = config.getOutputSizes(format);
1378                }
1379                if (slowSizes) {
1380                    slowSizeList = config.getHighResolutionOutputSizes(format);
1381                }
1382                if (fastSizeList != null && slowSizeList != null) {
1383                    sizes = new Size[slowSizeList.length + fastSizeList.length];
1384                    System.arraycopy(fastSizeList, 0, sizes, 0, fastSizeList.length);
1385                    System.arraycopy(slowSizeList, 0, sizes, fastSizeList.length, slowSizeList.length);
1386                } else if (fastSizeList != null) {
1387                    sizes = fastSizeList;
1388                } else if (slowSizeList != null) {
1389                    sizes = slowSizeList;
1390                }
1391                break;
1392            case Input:
1393                sizes = config.getInputSizes(format);
1394                break;
1395            default:
1396                throw new IllegalArgumentException("direction must be output or input");
1397        }
1398
1399        if (sizes == null) {
1400            sizes = new Size[0];
1401        }
1402
1403        return sizes;
1404    }
1405
1406    /**
1407     * Get available AE target fps ranges.
1408     *
1409     * @return Empty int array if aeAvailableTargetFpsRanges is invalid.
1410     */
1411    @SuppressWarnings("raw")
1412    public Range<Integer>[] getAeAvailableTargetFpsRangesChecked() {
1413        Key<Range<Integer>[]> key =
1414                CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
1415        Range<Integer>[] fpsRanges = getValueFromKeyNonNull(key);
1416
1417        if (fpsRanges == null) {
1418            return new Range[0];
1419        }
1420
1421        // Round down to 2 boundary if it is not integer times of 2, to avoid array out of bound
1422        // in case the above check fails.
1423        int fpsRangeLength = fpsRanges.length;
1424        int minFps, maxFps;
1425        long maxFrameDuration = getMaxFrameDurationChecked();
1426        for (int i = 0; i < fpsRangeLength; i += 1) {
1427            minFps = fpsRanges[i].getLower();
1428            maxFps = fpsRanges[i].getUpper();
1429            checkTrueForKey(key, " min fps must be no larger than max fps!",
1430                    minFps > 0 && maxFps >= minFps);
1431            long maxDuration = (long) (1e9 / minFps);
1432            checkTrueForKey(key, String.format(
1433                    " the frame duration %d for min fps %d must smaller than maxFrameDuration %d",
1434                    maxDuration, minFps, maxFrameDuration), maxDuration <= maxFrameDuration);
1435        }
1436        return fpsRanges;
1437    }
1438
1439    public static String getAeModeName(int aeMode) {
1440        return (aeMode >= AE_MODE_NAMES.length) ? String.format("VENDOR_AE_MODE_%d", aeMode) :
1441            AE_MODE_NAMES[aeMode];
1442    }
1443
1444    public static String getAfModeName(int afMode) {
1445        return (afMode >= AF_MODE_NAMES.length) ? String.format("VENDOR_AF_MODE_%d", afMode) :
1446            AF_MODE_NAMES[afMode];
1447    }
1448
1449    /**
1450     * Get the highest supported target FPS range.
1451     * Prioritizes maximizing the min FPS, then the max FPS without lowering min FPS.
1452     */
1453    public Range<Integer> getAeMaxTargetFpsRange() {
1454        Range<Integer>[] fpsRanges = getAeAvailableTargetFpsRangesChecked();
1455
1456        Range<Integer> targetRange = fpsRanges[0];
1457        // Assume unsorted list of target FPS ranges, so use two passes, first maximize min FPS
1458        for (Range<Integer> candidateRange : fpsRanges) {
1459            if (candidateRange.getLower() > targetRange.getLower()) {
1460                targetRange = candidateRange;
1461            }
1462        }
1463        // Then maximize max FPS while not lowering min FPS
1464        for (Range<Integer> candidateRange : fpsRanges) {
1465            if (candidateRange.getLower() >= targetRange.getLower() &&
1466                    candidateRange.getUpper() > targetRange.getUpper()) {
1467                targetRange = candidateRange;
1468            }
1469        }
1470        return targetRange;
1471    }
1472
1473    /**
1474     * Get max frame duration.
1475     *
1476     * @return 0 if maxFrameDuration is null
1477     */
1478    public long getMaxFrameDurationChecked() {
1479        Key<Long> key =
1480                CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION;
1481        Long maxDuration = getValueFromKeyNonNull(key);
1482
1483        if (maxDuration == null) {
1484            return 0;
1485        }
1486
1487        return maxDuration;
1488    }
1489
1490    /**
1491     * Get available minimal frame durations for a given format.
1492     *
1493     * @param format One of the format from {@link ImageFormat}.
1494     * @return HashMap of minimal frame durations for different sizes, empty HashMap
1495     *         if availableMinFrameDurations is null.
1496     */
1497    public HashMap<Size, Long> getAvailableMinFrameDurationsForFormatChecked(int format) {
1498
1499        HashMap<Size, Long> minDurationMap = new HashMap<Size, Long>();
1500
1501        Key<StreamConfigurationMap> key =
1502                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
1503        StreamConfigurationMap config = getValueFromKeyNonNull(key);
1504
1505        if (config == null) {
1506            return minDurationMap;
1507        }
1508
1509        for (Size size : getAvailableSizesForFormatChecked(format,
1510                StreamDirection.Output)) {
1511            long minFrameDuration = config.getOutputMinFrameDuration(format, size);
1512
1513            if (minFrameDuration != 0) {
1514                minDurationMap.put(new Size(size.getWidth(), size.getHeight()), minFrameDuration);
1515            }
1516        }
1517
1518        return minDurationMap;
1519    }
1520
1521    public int[] getAvailableEdgeModesChecked() {
1522        Key<int[]> key = CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES;
1523        int[] edgeModes = getValueFromKeyNonNull(key);
1524
1525        if (edgeModes == null) {
1526            return new int[0];
1527        }
1528
1529        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(edgeModes));
1530        // Full device should always include OFF and FAST
1531        if (isHardwareLevelFull()) {
1532            checkTrueForKey(key, "Full device must contain OFF and FAST edge modes",
1533                    modeList.contains(CameraMetadata.EDGE_MODE_OFF) &&
1534                    modeList.contains(CameraMetadata.EDGE_MODE_FAST));
1535        }
1536
1537        if (isHardwareLevelLimitedOrBetter()) {
1538            // FAST and HIGH_QUALITY mode must be both present or both not present
1539            List<Integer> coupledModes = Arrays.asList(new Integer[] {
1540                    CameraMetadata.EDGE_MODE_FAST,
1541                    CameraMetadata.EDGE_MODE_HIGH_QUALITY
1542            });
1543            checkTrueForKey(
1544                    key, " FAST and HIGH_QUALITY mode must both present or both not present",
1545                    containsAllOrNone(modeList, coupledModes));
1546        }
1547
1548        return edgeModes;
1549    }
1550
1551    public int[] getAvailableNoiseReductionModesChecked() {
1552        Key<int[]> key =
1553                CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
1554        int[] noiseReductionModes = getValueFromKeyNonNull(key);
1555
1556        if (noiseReductionModes == null) {
1557            return new int[0];
1558        }
1559
1560        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(noiseReductionModes));
1561        // Full device should always include OFF and FAST
1562        if (isHardwareLevelFull()) {
1563
1564            checkTrueForKey(key, "Full device must contain OFF and FAST noise reduction modes",
1565                    modeList.contains(CameraMetadata.NOISE_REDUCTION_MODE_OFF) &&
1566                    modeList.contains(CameraMetadata.NOISE_REDUCTION_MODE_FAST));
1567        }
1568
1569        if (isHardwareLevelLimitedOrBetter()) {
1570            // FAST and HIGH_QUALITY mode must be both present or both not present
1571            List<Integer> coupledModes = Arrays.asList(new Integer[] {
1572                    CameraMetadata.NOISE_REDUCTION_MODE_FAST,
1573                    CameraMetadata.NOISE_REDUCTION_MODE_HIGH_QUALITY
1574            });
1575            checkTrueForKey(
1576                    key, " FAST and HIGH_QUALITY mode must both present or both not present",
1577                    containsAllOrNone(modeList, coupledModes));
1578        }
1579        return noiseReductionModes;
1580    }
1581
1582    /**
1583     * Get value of key android.control.aeCompensationStep and do the sanity check.
1584     *
1585     * @return default value if the value is null.
1586     */
1587    public Rational getAeCompensationStepChecked() {
1588        Key<Rational> key =
1589                CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP;
1590        Rational compensationStep = getValueFromKeyNonNull(key);
1591
1592        if (compensationStep == null) {
1593            // Return default step.
1594            return CONTROL_AE_COMPENSATION_STEP_DEFAULT;
1595        }
1596
1597        // Legacy devices don't have a minimum step requirement
1598        if (isHardwareLevelLimitedOrBetter()) {
1599            float compensationStepF =
1600                    (float) compensationStep.getNumerator() / compensationStep.getDenominator();
1601            checkTrueForKey(key, " value must be no more than 1/2", compensationStepF <= 0.5f);
1602        }
1603
1604        return compensationStep;
1605    }
1606
1607    /**
1608     * Get value of key android.control.aeCompensationRange and do the sanity check.
1609     *
1610     * @return default value if the value is null or malformed.
1611     */
1612    public Range<Integer> getAeCompensationRangeChecked() {
1613        Key<Range<Integer>> key =
1614                CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE;
1615        Range<Integer> compensationRange = getValueFromKeyNonNull(key);
1616        Rational compensationStep = getAeCompensationStepChecked();
1617        float compensationStepF = compensationStep.floatValue();
1618        final Range<Integer> DEFAULT_RANGE = Range.create(
1619                (int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MIN / compensationStepF),
1620                (int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MAX / compensationStepF));
1621        final Range<Integer> ZERO_RANGE = Range.create(0, 0);
1622        if (compensationRange == null) {
1623            return ZERO_RANGE;
1624        }
1625
1626        // Legacy devices don't have a minimum range requirement
1627        if (isHardwareLevelLimitedOrBetter() && !compensationRange.equals(ZERO_RANGE)) {
1628            checkTrueForKey(key, " range value must be at least " + DEFAULT_RANGE
1629                    + ", actual " + compensationRange + ", compensation step " + compensationStep,
1630                   compensationRange.getLower() <= DEFAULT_RANGE.getLower() &&
1631                   compensationRange.getUpper() >= DEFAULT_RANGE.getUpper());
1632        }
1633
1634        return compensationRange;
1635    }
1636
1637    /**
1638     * Get availableVideoStabilizationModes and do the sanity check.
1639     *
1640     * @return available video stabilization modes, empty array if it is unavailable.
1641     */
1642    public int[] getAvailableVideoStabilizationModesChecked() {
1643        Key<int[]> key =
1644                CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
1645        int[] modes = getValueFromKeyNonNull(key);
1646
1647        if (modes == null) {
1648            return new int[0];
1649        }
1650
1651        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1652        checkTrueForKey(key, " All device should support OFF mode",
1653                modeList.contains(CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF));
1654        checkArrayValuesInRange(key, modes,
1655                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF,
1656                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON);
1657
1658        return modes;
1659    }
1660
1661    public boolean isVideoStabilizationSupported() {
1662        Integer[] videoStabModes =
1663                CameraTestUtils.toObject(getAvailableVideoStabilizationModesChecked());
1664        return Arrays.asList(videoStabModes).contains(
1665                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON);
1666    }
1667
1668    /**
1669     * Get availableOpticalStabilization and do the sanity check.
1670     *
1671     * @return available optical stabilization modes, empty array if it is unavailable.
1672     */
1673    public int[] getAvailableOpticalStabilizationChecked() {
1674        Key<int[]> key =
1675                CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
1676        int[] modes = getValueFromKeyNonNull(key);
1677
1678        if (modes == null) {
1679            return new int[0];
1680        }
1681
1682        checkArrayValuesInRange(key, modes,
1683                CameraMetadata.LENS_OPTICAL_STABILIZATION_MODE_OFF,
1684                CameraMetadata.LENS_OPTICAL_STABILIZATION_MODE_ON);
1685
1686        return modes;
1687    }
1688
1689    /**
1690     * Get the scaler's max digital zoom ({@code >= 1.0f}) ratio between crop and active array
1691     * @return the max zoom ratio, or {@code 1.0f} if the value is unavailable
1692     */
1693    public float getAvailableMaxDigitalZoomChecked() {
1694        Key<Float> key =
1695                CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
1696
1697        Float maxZoom = getValueFromKeyNonNull(key);
1698        if (maxZoom == null) {
1699            return 1.0f;
1700        }
1701
1702        checkTrueForKey(key, " max digital zoom should be no less than 1",
1703                maxZoom >= 1.0f && !Float.isNaN(maxZoom) && !Float.isInfinite(maxZoom));
1704
1705        return maxZoom;
1706    }
1707
1708    public int[] getAvailableSceneModesChecked() {
1709        Key<int[]> key =
1710                CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES;
1711        int[] modes = getValueFromKeyNonNull(key);
1712
1713        if (modes == null) {
1714            return new int[0];
1715        }
1716
1717        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1718        // FACE_PRIORITY must be included if face detection is supported.
1719        if (areKeysAvailable(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT) &&
1720                getMaxFaceCountChecked() > 0) {
1721            checkTrueForKey(key, " FACE_PRIORITY must be included if face detection is supported",
1722                    modeList.contains(CameraMetadata.CONTROL_SCENE_MODE_FACE_PRIORITY));
1723        }
1724
1725        return modes;
1726    }
1727
1728    public int[] getAvailableEffectModesChecked() {
1729        Key<int[]> key =
1730                CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS;
1731        int[] modes = getValueFromKeyNonNull(key);
1732
1733        if (modes == null) {
1734            return new int[0];
1735        }
1736
1737        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1738        // OFF must be included.
1739        checkTrueForKey(key, " OFF must be included",
1740                modeList.contains(CameraMetadata.CONTROL_EFFECT_MODE_OFF));
1741
1742        return modes;
1743    }
1744
1745    /**
1746     * Get and check the available color aberration modes
1747     *
1748     * @return the available color aberration modes
1749     */
1750    public int[] getAvailableColorAberrationModesChecked() {
1751        Key<int[]> key =
1752                CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
1753        int[] modes = getValueFromKeyNonNull(key);
1754
1755        if (modes == null) {
1756            return new int[0];
1757        }
1758
1759        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1760        checkTrueForKey(key, " Camera devices must always support either OFF or FAST mode",
1761                modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF) ||
1762                modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_FAST));
1763
1764        if (isHardwareLevelLimitedOrBetter()) {
1765            // FAST and HIGH_QUALITY mode must be both present or both not present
1766            List<Integer> coupledModes = Arrays.asList(new Integer[] {
1767                    CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_FAST,
1768                    CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY
1769            });
1770            checkTrueForKey(
1771                    key, " FAST and HIGH_QUALITY mode must both present or both not present",
1772                    containsAllOrNone(modeList, coupledModes));
1773        }
1774        checkElementDistinct(key, modeList);
1775        checkArrayValuesInRange(key, modes,
1776                CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF,
1777                CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY);
1778
1779        return modes;
1780    }
1781
1782    /**
1783     * Get max pipeline depth and do the sanity check.
1784     *
1785     * @return max pipeline depth, default value if it is not available.
1786     */
1787    public byte getPipelineMaxDepthChecked() {
1788        Key<Byte> key =
1789                CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH;
1790        Byte maxDepth = getValueFromKeyNonNull(key);
1791
1792        if (maxDepth == null) {
1793            return REQUEST_PIPELINE_MAX_DEPTH_MAX;
1794        }
1795
1796        checkTrueForKey(key, " max pipeline depth should be no larger than "
1797                + REQUEST_PIPELINE_MAX_DEPTH_MAX, maxDepth <= REQUEST_PIPELINE_MAX_DEPTH_MAX);
1798
1799        return maxDepth;
1800    }
1801
1802    /**
1803     * Get available lens shading modes.
1804     */
1805     public int[] getAvailableLensShadingModesChecked() {
1806         Key<int[]> key =
1807                 CameraCharacteristics.SHADING_AVAILABLE_MODES;
1808         int[] modes = getValueFromKeyNonNull(key);
1809         if (modes == null) {
1810             return new int[0];
1811         }
1812
1813         List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1814         // FAST must be included.
1815         checkTrueForKey(key, " FAST must be included",
1816                 modeList.contains(CameraMetadata.SHADING_MODE_FAST));
1817
1818         if (isCapabilitySupported(
1819                 CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING)) {
1820             checkTrueForKey(key, " OFF must be included for MANUAL_POST_PROCESSING devices",
1821                     modeList.contains(CameraMetadata.SHADING_MODE_OFF));
1822         }
1823         return modes;
1824     }
1825
1826     /**
1827      * Get available lens shading map modes.
1828      */
1829      public int[] getAvailableLensShadingMapModesChecked() {
1830          Key<int[]> key =
1831                  CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES;
1832          int[] modes = getValueFromKeyNonNull(key);
1833          if (modes == null) {
1834              return new int[0];
1835          }
1836
1837          List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
1838
1839          if (isCapabilitySupported(
1840                  CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
1841              checkTrueForKey(key, " ON must be included for RAW capability devices",
1842                      modeList.contains(CameraMetadata.STATISTICS_LENS_SHADING_MAP_MODE_ON));
1843          }
1844          return modes;
1845      }
1846
1847
1848    /**
1849     * Get available capabilities and do the sanity check.
1850     *
1851     * @return reported available capabilities list, empty list if the value is unavailable.
1852     */
1853    public List<Integer> getAvailableCapabilitiesChecked() {
1854        Key<int[]> key =
1855                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES;
1856        int[] availableCaps = getValueFromKeyNonNull(key);
1857        List<Integer> capList;
1858
1859        if (availableCaps == null) {
1860            return new ArrayList<Integer>();
1861        }
1862
1863        checkArrayValuesInRange(key, availableCaps,
1864                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
1865                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO);
1866        capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
1867        return capList;
1868    }
1869
1870    /**
1871     * Determine whether the current device supports a capability or not.
1872     *
1873     * @param capability (non-negative)
1874     *
1875     * @return {@code true} if the capability is supported, {@code false} otherwise.
1876     *
1877     * @throws IllegalArgumentException if {@code capability} was negative
1878     *
1879     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
1880     */
1881    public boolean isCapabilitySupported(int capability) {
1882        if (capability < 0) {
1883            throw new IllegalArgumentException("capability must be non-negative");
1884        }
1885
1886        List<Integer> availableCapabilities = getAvailableCapabilitiesChecked();
1887
1888        return availableCapabilities.contains(capability);
1889    }
1890
1891    /**
1892     * Determine whether or not all the {@code keys} are available characteristics keys
1893     * (as in {@link CameraCharacteristics#getKeys}.
1894     *
1895     * <p>If this returns {@code true}, then querying for this key from a characteristics
1896     * object will always return a non-{@code null} value.</p>
1897     *
1898     * @param keys collection of camera characteristics keys
1899     * @return whether or not all characteristics keys are available
1900     */
1901    public final boolean areCharacteristicsKeysAvailable(
1902            Collection<Key<?>> keys) {
1903        return mCharacteristics.getKeys().containsAll(keys);
1904    }
1905
1906    /**
1907     * Determine whether or not all the {@code keys} are available result keys
1908     * (as in {@link CameraCharacteristics#getAvailableCaptureResultKeys}.
1909     *
1910     * <p>If this returns {@code true}, then querying for this key from a result
1911     * object will almost always return a non-{@code null} value.</p>
1912     *
1913     * <p>In some cases (e.g. lens shading map), the request must have additional settings
1914     * configured in order for the key to correspond to a value.</p>
1915     *
1916     * @param keys collection of capture result keys
1917     * @return whether or not all result keys are available
1918     */
1919    public final boolean areResultKeysAvailable(Collection<CaptureResult.Key<?>> keys) {
1920        return mCharacteristics.getAvailableCaptureResultKeys().containsAll(keys);
1921    }
1922
1923    /**
1924     * Determine whether or not all the {@code keys} are available request keys
1925     * (as in {@link CameraCharacteristics#getAvailableCaptureRequestKeys}.
1926     *
1927     * <p>If this returns {@code true}, then setting this key in the request builder
1928     * may have some effect (and if it's {@code false}, then the camera device will
1929     * definitely ignore it).</p>
1930     *
1931     * <p>In some cases (e.g. manual control of exposure), other keys must be also be set
1932     * in order for a key to take effect (e.g. control.mode set to OFF).</p>
1933     *
1934     * @param keys collection of capture request keys
1935     * @return whether or not all result keys are available
1936     */
1937    public final boolean areRequestKeysAvailable(Collection<CaptureRequest.Key<?>> keys) {
1938        return mCharacteristics.getAvailableCaptureRequestKeys().containsAll(keys);
1939    }
1940
1941    /**
1942     * Determine whether or not all the {@code keys} are available characteristics keys
1943     * (as in {@link CameraCharacteristics#getKeys}.
1944     *
1945     * <p>If this returns {@code true}, then querying for this key from a characteristics
1946     * object will always return a non-{@code null} value.</p>
1947     *
1948     * @param keys one or more camera characteristic keys
1949     * @return whether or not all characteristics keys are available
1950     */
1951    @SafeVarargs
1952    public final boolean areKeysAvailable(Key<?>... keys) {
1953        return areCharacteristicsKeysAvailable(Arrays.asList(keys));
1954    }
1955
1956    /**
1957     * Determine whether or not all the {@code keys} are available result keys
1958     * (as in {@link CameraCharacteristics#getAvailableCaptureResultKeys}.
1959     *
1960     * <p>If this returns {@code true}, then querying for this key from a result
1961     * object will almost always return a non-{@code null} value.</p>
1962     *
1963     * <p>In some cases (e.g. lens shading map), the request must have additional settings
1964     * configured in order for the key to correspond to a value.</p>
1965     *
1966     * @param keys one or more capture result keys
1967     * @return whether or not all result keys are available
1968     */
1969    @SafeVarargs
1970    public final boolean areKeysAvailable(CaptureResult.Key<?>... keys) {
1971        return areResultKeysAvailable(Arrays.asList(keys));
1972    }
1973
1974    /**
1975     * Determine whether or not all the {@code keys} are available request keys
1976     * (as in {@link CameraCharacteristics#getAvailableCaptureRequestKeys}.
1977     *
1978     * <p>If this returns {@code true}, then setting this key in the request builder
1979     * may have some effect (and if it's {@code false}, then the camera device will
1980     * definitely ignore it).</p>
1981     *
1982     * <p>In some cases (e.g. manual control of exposure), other keys must be also be set
1983     * in order for a key to take effect (e.g. control.mode set to OFF).</p>
1984     *
1985     * @param keys one or more capture request keys
1986     * @return whether or not all result keys are available
1987     */
1988    @SafeVarargs
1989    public final boolean areKeysAvailable(CaptureRequest.Key<?>... keys) {
1990        return areRequestKeysAvailable(Arrays.asList(keys));
1991    }
1992
1993    /*
1994     * Determine if camera device support AE lock control
1995     *
1996     * @return {@code true} if AE lock control is supported
1997     */
1998    public boolean isAeLockSupported() {
1999        return getValueFromKeyNonNull(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
2000    }
2001
2002    /*
2003     * Determine if camera device support AWB lock control
2004     *
2005     * @return {@code true} if AWB lock control is supported
2006     */
2007    public boolean isAwbLockSupported() {
2008        return getValueFromKeyNonNull(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
2009    }
2010
2011
2012    /*
2013     * Determine if camera device support manual lens shading map control
2014     *
2015     * @return {@code true} if manual lens shading map control is supported
2016     */
2017    public boolean isManualLensShadingMapSupported() {
2018        return areKeysAvailable(CaptureRequest.SHADING_MODE);
2019    }
2020
2021    /**
2022     * Determine if camera device support manual color correction control
2023     *
2024     * @return {@code true} if manual color correction control is supported
2025     */
2026    public boolean isColorCorrectionSupported() {
2027        return areKeysAvailable(CaptureRequest.COLOR_CORRECTION_MODE);
2028    }
2029
2030    /**
2031     * Determine if camera device support manual tone mapping control
2032     *
2033     * @return {@code true} if manual tone mapping control is supported
2034     */
2035    public boolean isManualToneMapSupported() {
2036        return areKeysAvailable(CaptureRequest.TONEMAP_MODE);
2037    }
2038
2039    /**
2040     * Determine if camera device support manual color aberration control
2041     *
2042     * @return {@code true} if manual color aberration control is supported
2043     */
2044    public boolean isManualColorAberrationControlSupported() {
2045        return areKeysAvailable(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE);
2046    }
2047
2048    /**
2049     * Determine if camera device support edge mode control
2050     *
2051     * @return {@code true} if edge mode control is supported
2052     */
2053    public boolean isEdgeModeControlSupported() {
2054        return areKeysAvailable(CaptureRequest.EDGE_MODE);
2055    }
2056
2057    /**
2058     * Determine if camera device support hot pixel mode control
2059     *
2060     * @return {@code true} if hot pixel mode control is supported
2061     */
2062    public boolean isHotPixelMapModeControlSupported() {
2063        return areKeysAvailable(CaptureRequest.HOT_PIXEL_MODE);
2064    }
2065
2066    /**
2067     * Determine if camera device support noise reduction mode control
2068     *
2069     * @return {@code true} if noise reduction mode control is supported
2070     */
2071    public boolean isNoiseReductionModeControlSupported() {
2072        return areKeysAvailable(CaptureRequest.NOISE_REDUCTION_MODE);
2073    }
2074
2075    /**
2076     * Get max number of output raw streams and do the basic sanity check.
2077     *
2078     * @return reported max number of raw output stream
2079     */
2080    public int getMaxNumOutputStreamsRawChecked() {
2081        Integer maxNumStreams =
2082                getValueFromKeyNonNull(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
2083        if (maxNumStreams == null)
2084            return 0;
2085        return maxNumStreams;
2086    }
2087
2088    /**
2089     * Get max number of output processed streams and do the basic sanity check.
2090     *
2091     * @return reported max number of processed output stream
2092     */
2093    public int getMaxNumOutputStreamsProcessedChecked() {
2094        Integer maxNumStreams =
2095                getValueFromKeyNonNull(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
2096        if (maxNumStreams == null)
2097            return 0;
2098        return maxNumStreams;
2099    }
2100
2101    /**
2102     * Get max number of output stalling processed streams and do the basic sanity check.
2103     *
2104     * @return reported max number of stalling processed output stream
2105     */
2106    public int getMaxNumOutputStreamsProcessedStallChecked() {
2107        Integer maxNumStreams =
2108                getValueFromKeyNonNull(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
2109        if (maxNumStreams == null)
2110            return 0;
2111        return maxNumStreams;
2112    }
2113
2114    /**
2115     * Get lens facing and do the sanity check
2116     * @return lens facing, return default value (BACK) if value is unavailable.
2117     */
2118    public int getLensFacingChecked() {
2119        Key<Integer> key =
2120                CameraCharacteristics.LENS_FACING;
2121        Integer facing = getValueFromKeyNonNull(key);
2122
2123        if (facing == null) {
2124            return CameraCharacteristics.LENS_FACING_BACK;
2125        }
2126
2127        checkTrueForKey(key, " value is out of range ",
2128                facing >= CameraCharacteristics.LENS_FACING_FRONT &&
2129                facing <= CameraCharacteristics.LENS_FACING_BACK);
2130        return facing;
2131    }
2132
2133    /**
2134     * Get maxCaptureStall frames or default value (if value doesn't exist)
2135     * @return maxCaptureStall frames or default value.
2136     */
2137    public int getMaxCaptureStallOrDefault() {
2138        Key<Integer> key =
2139                CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL;
2140        Integer value = getValueFromKeyNonNull(key);
2141
2142        if (value == null) {
2143            return MAX_REPROCESS_MAX_CAPTURE_STALL;
2144        }
2145
2146        checkTrueForKey(key, " value is out of range ",
2147                value >= 0 &&
2148                value <= MAX_REPROCESS_MAX_CAPTURE_STALL);
2149
2150        return value;
2151    }
2152
2153    /**
2154     * Get the scaler's cropping type (center only or freeform)
2155     * @return cropping type, return default value (CENTER_ONLY) if value is unavailable
2156     */
2157    public int getScalerCroppingTypeChecked() {
2158        Key<Integer> key =
2159                CameraCharacteristics.SCALER_CROPPING_TYPE;
2160        Integer value = getValueFromKeyNonNull(key);
2161
2162        if (value == null) {
2163            return CameraCharacteristics.SCALER_CROPPING_TYPE_CENTER_ONLY;
2164        }
2165
2166        checkTrueForKey(key, " value is out of range ",
2167                value >= CameraCharacteristics.SCALER_CROPPING_TYPE_CENTER_ONLY &&
2168                value <= CameraCharacteristics.SCALER_CROPPING_TYPE_FREEFORM);
2169
2170        return value;
2171    }
2172
2173    /**
2174     * Check if the constrained high speed video is supported by the camera device.
2175     * The high speed FPS ranges and sizes are sanitized in
2176     * ExtendedCameraCharacteristicsTest#testConstrainedHighSpeedCapability.
2177     *
2178     * @return true if the constrained high speed video is supported, false otherwise.
2179     */
2180    public boolean isConstrainedHighSpeedVideoSupported() {
2181        List<Integer> availableCapabilities = getAvailableCapabilitiesChecked();
2182        return (availableCapabilities.contains(
2183                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO));
2184    }
2185
2186    /**
2187     * Check if high speed video is supported (HIGH_SPEED_VIDEO scene mode is
2188     * supported, supported high speed fps ranges and sizes are valid).
2189     *
2190     * @return true if high speed video is supported.
2191     */
2192    public boolean isHighSpeedVideoSupported() {
2193        List<Integer> sceneModes =
2194                Arrays.asList(CameraTestUtils.toObject(getAvailableSceneModesChecked()));
2195        if (sceneModes.contains(CameraCharacteristics.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO)) {
2196            StreamConfigurationMap config =
2197                    getValueFromKeyNonNull(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
2198            if (config == null) {
2199                return false;
2200            }
2201            Size[] availableSizes = config.getHighSpeedVideoSizes();
2202            if (availableSizes.length == 0) {
2203                return false;
2204            }
2205
2206            for (Size size : availableSizes) {
2207                Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
2208                if (availableFpsRanges.length == 0) {
2209                    return false;
2210                }
2211            }
2212
2213            return true;
2214        } else {
2215            return false;
2216        }
2217    }
2218
2219    /**
2220     * Check if depth output is supported, based on the depth capability
2221     */
2222    public boolean isDepthOutputSupported() {
2223        return isCapabilitySupported(
2224                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
2225    }
2226
2227    /**
2228     * Check if standard outputs (PRIVATE, YUV, JPEG) outputs are supported, based on the
2229     * backwards-compatible capability
2230     */
2231    public boolean isColorOutputSupported() {
2232        return isCapabilitySupported(
2233                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
2234    }
2235
2236    /**
2237     * Check if optical black regions key is supported.
2238     */
2239    public boolean isOpticalBlackRegionSupported() {
2240        return areKeysAvailable(CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS);
2241    }
2242
2243    /**
2244     * Check if the dynamic black level is supported.
2245     *
2246     * <p>
2247     * Note that: This also indicates if the white level is supported, as dynamic black and white
2248     * level must be all supported or none of them is supported.
2249     * </p>
2250     */
2251    public boolean isDynamicBlackLevelSupported() {
2252        return areKeysAvailable(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
2253    }
2254
2255    /**
2256     * Get the value in index for a fixed-size array from a given key.
2257     *
2258     * <p>If the camera device is incorrectly reporting values, log a warning and return
2259     * the default value instead.</p>
2260     *
2261     * @param key Key to fetch
2262     * @param defaultValue Default value to return if camera device uses invalid values
2263     * @param name Human-readable name for the array index (logging only)
2264     * @param index Array index of the subelement
2265     * @param size Expected fixed size of the array
2266     *
2267     * @return The value reported by the camera device, or the defaultValue otherwise.
2268     */
2269    private <T> T getArrayElementOrDefault(Key<?> key, T defaultValue, String name, int index,
2270            int size) {
2271        T elementValue = getArrayElementCheckRangeNonNull(
2272                key,
2273                index,
2274                size);
2275
2276        if (elementValue == null) {
2277            failKeyCheck(key,
2278                    "had no valid " + name + " value; using default of " + defaultValue);
2279            elementValue = defaultValue;
2280        }
2281
2282        return elementValue;
2283    }
2284
2285    /**
2286     * Fetch an array sub-element from an array value given by a key.
2287     *
2288     * <p>
2289     * Prints a warning if the sub-element was null.
2290     * </p>
2291     *
2292     * <p>Use for variable-size arrays since this does not check the array size.</p>
2293     *
2294     * @param key Metadata key to look up
2295     * @param element A non-negative index value.
2296     * @return The array sub-element, or null if the checking failed.
2297     */
2298    private <T> T getArrayElementNonNull(Key<?> key, int element) {
2299        return getArrayElementCheckRangeNonNull(key, element, IGNORE_SIZE_CHECK);
2300    }
2301
2302    /**
2303     * Fetch an array sub-element from an array value given by a key.
2304     *
2305     * <p>
2306     * Prints a warning if the array size does not match the size, or if the sub-element was null.
2307     * </p>
2308     *
2309     * @param key Metadata key to look up
2310     * @param element The index in [0,size)
2311     * @param size A positive size value or otherwise {@value #IGNORE_SIZE_CHECK}
2312     * @return The array sub-element, or null if the checking failed.
2313     */
2314    private <T> T getArrayElementCheckRangeNonNull(Key<?> key, int element, int size) {
2315        Object array = getValueFromKeyNonNull(key);
2316
2317        if (array == null) {
2318            // Warning already printed
2319            return null;
2320        }
2321
2322        if (size != IGNORE_SIZE_CHECK) {
2323            int actualLength = Array.getLength(array);
2324            if (actualLength != size) {
2325                failKeyCheck(key,
2326                        String.format("had the wrong number of elements (%d), expected (%d)",
2327                                actualLength, size));
2328                return null;
2329            }
2330        }
2331
2332        @SuppressWarnings("unchecked")
2333        T val = (T) Array.get(array, element);
2334
2335        if (val == null) {
2336            failKeyCheck(key, "had a null element at index" + element);
2337            return null;
2338        }
2339
2340        return val;
2341    }
2342
2343    /**
2344     * Gets the key, logging warnings for null values.
2345     */
2346    public <T> T getValueFromKeyNonNull(Key<T> key) {
2347        if (key == null) {
2348            throw new IllegalArgumentException("key was null");
2349        }
2350
2351        T value = mCharacteristics.get(key);
2352
2353        if (value == null) {
2354            failKeyCheck(key, "was null");
2355        }
2356
2357        return value;
2358    }
2359
2360    private void checkArrayValuesInRange(Key<int[]> key, int[] array, int min, int max) {
2361        for (int value : array) {
2362            checkTrueForKey(key, String.format(" value is out of range [%d, %d]", min, max),
2363                    value <= max && value >= min);
2364        }
2365    }
2366
2367    private void checkArrayValuesInRange(Key<byte[]> key, byte[] array, byte min, byte max) {
2368        for (byte value : array) {
2369            checkTrueForKey(key, String.format(" value is out of range [%d, %d]", min, max),
2370                    value <= max && value >= min);
2371        }
2372    }
2373
2374    /**
2375     * Check the uniqueness of the values in a list.
2376     *
2377     * @param key The key to be checked
2378     * @param list The list contains the value of the key
2379     */
2380    private <U, T> void checkElementDistinct(Key<U> key, List<T> list) {
2381        // Each size must be distinct.
2382        Set<T> sizeSet = new HashSet<T>(list);
2383        checkTrueForKey(key, "Each size must be distinct", sizeSet.size() == list.size());
2384    }
2385
2386    private <T> void checkTrueForKey(Key<T> key, String message, boolean condition) {
2387        if (!condition) {
2388            failKeyCheck(key, message);
2389        }
2390    }
2391
2392    /* Helper function to check if the coupled modes are either all present or all non-present */
2393    private <T> boolean containsAllOrNone(Collection<T> observedModes, Collection<T> coupledModes) {
2394        if (observedModes.containsAll(coupledModes)) {
2395            return true;
2396        }
2397        for (T mode : coupledModes) {
2398            if (observedModes.contains(mode)) {
2399                return false;
2400            }
2401        }
2402        return true;
2403    }
2404
2405    private <T> void failKeyCheck(Key<T> key, String message) {
2406        // TODO: Consider only warning once per key/message combination if it's too spammy.
2407        // TODO: Consider offering other options such as throwing an assertion exception
2408        String failureCause = String.format("The static info key '%s' %s", key.getName(), message);
2409        switch (mLevel) {
2410            case WARN:
2411                Log.w(TAG, failureCause);
2412                break;
2413            case COLLECT:
2414                mCollector.addMessage(failureCause);
2415                break;
2416            case ASSERT:
2417                Assert.fail(failureCause);
2418            default:
2419                throw new UnsupportedOperationException("Unhandled level " + mLevel);
2420        }
2421    }
2422}
2423