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