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