Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.hardware.camera2.cts;
     18 
     19 import android.content.Context;
     20 import android.graphics.ImageFormat;
     21 import android.graphics.Rect;
     22 import android.graphics.SurfaceTexture;
     23 import android.hardware.camera2.CameraCharacteristics;
     24 import android.hardware.camera2.CameraCharacteristics.Key;
     25 import android.hardware.camera2.CameraManager;
     26 import android.hardware.camera2.CaptureRequest;
     27 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
     28 import android.hardware.camera2.params.BlackLevelPattern;
     29 import android.hardware.camera2.params.ColorSpaceTransform;
     30 import android.hardware.camera2.params.StreamConfigurationMap;
     31 import android.media.CamcorderProfile;
     32 import android.media.ImageReader;
     33 import android.test.AndroidTestCase;
     34 import android.util.Log;
     35 import android.util.Rational;
     36 import android.util.Range;
     37 import android.util.Size;
     38 import android.view.Surface;
     39 
     40 import java.util.ArrayList;
     41 import java.util.Arrays;
     42 import java.util.List;
     43 import java.util.Objects;
     44 
     45 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
     46 
     47 /**
     48  * Extended tests for static camera characteristics.
     49  */
     50 public class ExtendedCameraCharacteristicsTest extends AndroidTestCase {
     51     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
     52     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     53 
     54     private static final String PREFIX_ANDROID = "android";
     55     private static final String PREFIX_VENDOR = "com";
     56 
     57     /*
     58      * Constants for static RAW metadata.
     59      */
     60     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
     61 
     62     private CameraManager mCameraManager;
     63     private List<CameraCharacteristics> mCharacteristics;
     64     private String[] mIds;
     65     private CameraErrorCollector mCollector;
     66 
     67     private static final Size FULLHD = new Size(1920, 1080);
     68     private static final Size FULLHD_ALT = new Size(1920, 1088);
     69     private static final Size HD = new Size(1280, 720);
     70     private static final Size VGA = new Size(640, 480);
     71     private static final Size QVGA = new Size(320, 240);
     72 
     73     /*
     74      * HW Levels short hand
     75      */
     76     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
     77     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
     78     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
     79     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
     80 
     81     /*
     82      * Capabilities short hand
     83      */
     84     private static final int NONE = -1;
     85     private static final int BC =
     86             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
     87     private static final int MANUAL_SENSOR =
     88             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
     89     private static final int MANUAL_POSTPROC =
     90             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING;
     91     private static final int RAW =
     92             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
     93     private static final int YUV_REPROCESS =
     94             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
     95     private static final int OPAQUE_REPROCESS =
     96             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
     97     private static final int CONSTRAINED_HIGH_SPEED =
     98             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO;
     99     private static final int HIGH_SPEED_FPS_LOWER_MIN = 30;
    100     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
    101 
    102     @Override
    103     public void setContext(Context context) {
    104         super.setContext(context);
    105         mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
    106         assertNotNull("Can't connect to camera manager", mCameraManager);
    107     }
    108 
    109     @Override
    110     protected void setUp() throws Exception {
    111         super.setUp();
    112         mIds = mCameraManager.getCameraIdList();
    113         mCharacteristics = new ArrayList<>();
    114         mCollector = new CameraErrorCollector();
    115         for (int i = 0; i < mIds.length; i++) {
    116             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mIds[i]);
    117             assertNotNull(String.format("Can't get camera characteristics from: ID %s", mIds[i]),
    118                     props);
    119             mCharacteristics.add(props);
    120         }
    121     }
    122 
    123     @Override
    124     protected void tearDown() throws Exception {
    125         mCharacteristics = null;
    126 
    127         try {
    128             mCollector.verify();
    129         } catch (Throwable e) {
    130             // When new Exception(e) is used, exception info will be printed twice.
    131             throw new Exception(e.getMessage());
    132         } finally {
    133             super.tearDown();
    134         }
    135     }
    136 
    137     /**
    138      * Test that the available stream configurations contain a few required formats and sizes.
    139      */
    140     public void testAvailableStreamConfigs() {
    141         int counter = 0;
    142         for (CameraCharacteristics c : mCharacteristics) {
    143             StreamConfigurationMap config =
    144                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    145             assertNotNull(String.format("No stream configuration map found for: ID %s",
    146                     mIds[counter]), config);
    147             int[] outputFormats = config.getOutputFormats();
    148 
    149             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    150             assertNotNull("android.request.availableCapabilities must never be null",
    151                     actualCapabilities);
    152 
    153             // Check required formats exist (JPEG, and YUV_420_888).
    154             if (!arrayContains(actualCapabilities, BC)) {
    155                 Log.i(TAG, "Camera " + mIds[counter] +
    156                     ": BACKWARD_COMPATIBLE capability not supported, skipping test");
    157                 continue;
    158             }
    159 
    160             assertArrayContains(
    161                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
    162                             mIds[counter]), outputFormats, ImageFormat.YUV_420_888);
    163             assertArrayContains(String.format("No JPEG image format for: ID %s",
    164                     mIds[counter]), outputFormats, ImageFormat.JPEG);
    165 
    166             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
    167             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
    168             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
    169 
    170             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
    171                     String.format("No sizes for preview format %x for: ID %s",
    172                             ImageFormat.YUV_420_888, mIds[counter]));
    173 
    174             Rect activeRect = CameraTestUtils.getValueNotNull(
    175                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    176             Size activeArraySize = new Size(activeRect.width(), activeRect.height());
    177             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
    178 
    179             if (activeArraySize.getWidth() >= FULLHD.getWidth() &&
    180                     activeArraySize.getHeight() >= FULLHD.getHeight()) {
    181                 assertArrayContains(String.format(
    182                         "Required FULLHD size not found for format %x for: ID %s",
    183                         ImageFormat.JPEG, mIds[counter]), jpegSizes, FULLHD);
    184             }
    185 
    186             if (activeArraySize.getWidth() >= HD.getWidth() &&
    187                     activeArraySize.getHeight() >= HD.getHeight()) {
    188                 assertArrayContains(String.format(
    189                         "Required HD size not found for format %x for: ID %s",
    190                         ImageFormat.JPEG, mIds[counter]), jpegSizes, HD);
    191             }
    192 
    193             if (activeArraySize.getWidth() >= VGA.getWidth() &&
    194                     activeArraySize.getHeight() >= VGA.getHeight()) {
    195                 assertArrayContains(String.format(
    196                         "Required VGA size not found for format %x for: ID %s",
    197                         ImageFormat.JPEG, mIds[counter]), jpegSizes, VGA);
    198             }
    199 
    200             if (activeArraySize.getWidth() >= QVGA.getWidth() &&
    201                     activeArraySize.getHeight() >= QVGA.getHeight()) {
    202                 assertArrayContains(String.format(
    203                         "Required QVGA size not found for format %x for: ID %s",
    204                         ImageFormat.JPEG, mIds[counter]), jpegSizes, QVGA);
    205             }
    206 
    207             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
    208             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
    209             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
    210 
    211             CamcorderProfile maxVideoProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
    212             Size maxVideoSize = new Size(
    213                     maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
    214 
    215             // Handle FullHD special case first
    216             if (jpegSizesList.contains(FULLHD)) {
    217                 if (hwLevel == FULL || (hwLevel == LIMITED &&
    218                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
    219                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
    220                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
    221                             yuvSizesList.contains(FULLHD_ALT);
    222                     boolean privateSupportFullHD = privateSizesList.contains(FULLHD) ||
    223                             privateSizesList.contains(FULLHD_ALT);
    224                     assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD);
    225                     assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD);
    226                 }
    227                 // remove all FullHD or FullHD_Alt sizes for the remaining of the test
    228                 jpegSizesList.remove(FULLHD);
    229                 jpegSizesList.remove(FULLHD_ALT);
    230             }
    231 
    232             // Check all sizes other than FullHD
    233             if (hwLevel == LIMITED) {
    234                 // Remove all jpeg sizes larger than max video size
    235                 ArrayList<Size> toBeRemoved = new ArrayList<>();
    236                 for (Size size : jpegSizesList) {
    237                     if (size.getWidth() >= maxVideoSize.getWidth() &&
    238                             size.getHeight() >= maxVideoSize.getHeight()) {
    239                         toBeRemoved.add(size);
    240                     }
    241                 }
    242                 jpegSizesList.removeAll(toBeRemoved);
    243             }
    244 
    245             if (hwLevel == FULL || hwLevel == LIMITED) {
    246                 if (!yuvSizesList.containsAll(jpegSizesList)) {
    247                     for (Size s : jpegSizesList) {
    248                         if (!yuvSizesList.contains(s)) {
    249                             fail("Size " + s + " not found in YUV format");
    250                         }
    251                     }
    252                 }
    253             }
    254 
    255             if (!privateSizesList.containsAll(yuvSizesList)) {
    256                 for (Size s : yuvSizesList) {
    257                     if (!privateSizesList.contains(s)) {
    258                         fail("Size " + s + " not found in PRIVATE format");
    259                     }
    260                 }
    261             }
    262 
    263             counter++;
    264         }
    265     }
    266 
    267     /**
    268      * Test {@link CameraCharacteristics#getKeys}
    269      */
    270     public void testKeys() {
    271         int counter = 0;
    272         for (CameraCharacteristics c : mCharacteristics) {
    273             mCollector.setCameraId(mIds[counter]);
    274 
    275             if (VERBOSE) {
    276                 Log.v(TAG, "testKeys - testing characteristics for camera " + mIds[counter]);
    277             }
    278 
    279             List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
    280             assertNotNull("Camera characteristics keys must not be null", allKeys);
    281             assertFalse("Camera characteristics keys must have at least 1 key",
    282                     allKeys.isEmpty());
    283 
    284             for (CameraCharacteristics.Key<?> key : allKeys) {
    285                 assertKeyPrefixValid(key.getName());
    286 
    287                 // All characteristics keys listed must never be null
    288                 mCollector.expectKeyValueNotNull(c, key);
    289 
    290                 // TODO: add a check that key must not be @hide
    291             }
    292 
    293             /*
    294              * List of keys that must be present in camera characteristics (not null).
    295              *
    296              * Keys for LIMITED, FULL devices might be available despite lacking either
    297              * the hardware level or the capability. This is *OK*. This only lists the
    298              * *minimal* requirements for a key to be listed.
    299              *
    300              * LEGACY devices are a bit special since they map to api1 devices, so we know
    301              * for a fact most keys are going to be illegal there so they should never be
    302              * available.
    303              *
    304              * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to
    305              * do the actual checking.
    306              */
    307             {
    308                 //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
    309                 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES     , OPT      ,   BC                   );
    310                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES                         , OPT      ,   BC                   );
    311                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , OPT      ,   BC                   );
    312                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , OPT      ,   BC                   );
    313                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , OPT      ,   BC                   );
    314                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , OPT      ,   BC                   );
    315                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , OPT      ,   BC                   );
    316                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE                       , OPT      ,   BC                   );
    317                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , OPT      ,   BC                   );
    318                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , OPT      ,   BC                   );
    319                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , OPT      ,   BC                   );
    320                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , OPT      ,   BC                   );
    321                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , OPT      ,   BC                   );
    322                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      , OPT      ,   BC                   );
    323                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , OPT      ,   BC                   );
    324                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , OPT      ,   BC                   );
    325                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , OPT      ,   BC                   );
    326                 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
    327                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
    328                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
    329                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
    330                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
    331                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
    332                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
    333                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
    334                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , OPT      ,   BC                   );
    335                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   BC                   );
    336                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
    337                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   BC                   );
    338                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   BC                   );
    339                 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT      ,   BC                   );
    340                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , OPT      ,   BC                   );
    341                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS                   , OPT      ,   YUV_REPROCESS, OPAQUE_REPROCESS);
    342                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   CONSTRAINED_HIGH_SPEED);
    343                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , OPT      ,   BC                   );
    344                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , OPT      ,   BC                   );
    345                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , OPT      ,   BC                   );
    346                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , OPT      ,   BC                   );
    347                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , OPT      ,   BC                   );
    348                 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , OPT      ,   BC                   );
    349                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   BC                   );
    350                 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , OPT      ,   BC                   );
    351                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , OPT      ,   BC                   );
    352                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   MANUAL_SENSOR, RAW   );
    353                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
    354                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
    355                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
    356                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , OPT      ,   BC, RAW              );
    357                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
    358                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
    359                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
    360                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , OPT      ,   BC                   );
    361                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , OPT      ,   BC                   );
    362                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
    363                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL                         , OPT      ,   RAW                  );
    364                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    , OPT      ,   BC                   );
    365                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
    366                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , OPT      ,   BC                   );
    367                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
    368                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
    369                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , OPT      ,   BC                   );
    370                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
    371                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED  ,   RAW                  );
    372                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , OPT      ,   BC                   );
    373                 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , OPT      ,   BC                   );
    374                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_POSTPROC      );
    375                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_POSTPROC      );
    376 
    377                 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
    378 
    379                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
    380             }
    381 
    382             // Only check for these if the second reference illuminant is included
    383             if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
    384                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
    385                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
    386                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
    387                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
    388             }
    389 
    390             counter++;
    391         }
    392     }
    393 
    394     /**
    395      * Test values for static metadata used by the RAW capability.
    396      */
    397     public void testStaticRawCharacteristics() {
    398         int counter = 0;
    399         for (CameraCharacteristics c : mCharacteristics) {
    400             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    401             assertNotNull("android.request.availableCapabilities must never be null",
    402                     actualCapabilities);
    403             if (!arrayContains(actualCapabilities,
    404                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
    405                 Log.i(TAG, "RAW capability is not supported in camera " + counter++ +
    406                         ". Skip the test.");
    407                 continue;
    408             }
    409 
    410             Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
    411             if (actualHwLevel != null && actualHwLevel == FULL) {
    412                 mCollector.expectKeyValueContains(c,
    413                         CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
    414                         CameraCharacteristics.HOT_PIXEL_MODE_FAST);
    415             }
    416             mCollector.expectKeyValueContains(c,
    417                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false);
    418             mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL,
    419                     MIN_ALLOWABLE_WHITELEVEL);
    420 
    421             mCollector.expectKeyValueIsIn(c,
    422                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
    423                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB,
    424                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG,
    425                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG,
    426                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR);
    427             // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
    428 
    429             mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1,
    430                     CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
    431                     CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
    432             mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2,
    433                     (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
    434                     (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
    435 
    436             Rational[] zeroes = new Rational[9];
    437             Arrays.fill(zeroes, Rational.ZERO);
    438 
    439             ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes);
    440             mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed,
    441                     c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
    442             mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed,
    443                     c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
    444             mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.",
    445                     zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
    446             mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.",
    447                     zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
    448             mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.",
    449                     zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
    450             mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.",
    451                     zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
    452 
    453             BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
    454                     CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
    455             if (blackLevel != null) {
    456                 String blackLevelPatternString = blackLevel.toString();
    457                 if (VERBOSE) {
    458                     Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
    459                 }
    460                 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
    461                 blackLevel.copyTo(blackLevelPattern, /*offset*/0);
    462                 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
    463                 if (whitelevel != null) {
    464                     mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0,
    465                             whitelevel);
    466                 } else {
    467                     mCollector.addMessage(
    468                             "No WhiteLevel available, cannot check BlackLevelPattern range.");
    469                 }
    470             }
    471 
    472             // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
    473             counter++;
    474         }
    475     }
    476 
    477     /**
    478      * Test values for static metadata used by the BURST capability.
    479      */
    480     public void testStaticBurstCharacteristics() throws Exception {
    481         int counter = 0;
    482         final float SIZE_ERROR_MARGIN = 0.03f;
    483         for (CameraCharacteristics c : mCharacteristics) {
    484             int[] actualCapabilities = CameraTestUtils.getValueNotNull(
    485                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    486 
    487             // Check if the burst capability is defined
    488             boolean haveBurstCapability = arrayContains(actualCapabilities,
    489                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
    490             boolean haveBC = arrayContains(actualCapabilities,
    491                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
    492 
    493             if(haveBurstCapability && !haveBC) {
    494                 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined");
    495             }
    496 
    497             if (!haveBC) continue;
    498 
    499             StreamConfigurationMap config =
    500                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    501             assertNotNull(String.format("No stream configuration map found for: ID %s",
    502                     mIds[counter]), config);
    503             Rect activeRect = CameraTestUtils.getValueNotNull(
    504                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    505             Size sensorSize = new Size(activeRect.width(), activeRect.height());
    506 
    507             // Ensure that max YUV size matches max JPEG size
    508             Size maxYuvSize = CameraTestUtils.getMaxSize(
    509                     config.getOutputSizes(ImageFormat.YUV_420_888));
    510             Size maxFastYuvSize = maxYuvSize;
    511 
    512             Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
    513             if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
    514                 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
    515                 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
    516             }
    517 
    518             Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
    519                     ImageFormat.JPEG, mIds[counter], mCameraManager));
    520 
    521             boolean haveMaxYuv = maxYuvSize != null ?
    522                 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
    523                         maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
    524 
    525             boolean maxYuvMatchSensor =
    526                     (maxYuvSize.getWidth() <= sensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
    527                      maxYuvSize.getWidth() >= sensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
    528                      maxYuvSize.getHeight() <= sensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
    529                      maxYuvSize.getHeight() >= sensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN));
    530 
    531             // No need to do null check since framework will generate the key if HAL don't supply
    532             boolean haveAeLock = CameraTestUtils.getValueNotNull(
    533                     c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
    534             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
    535                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
    536 
    537             // Ensure that max YUV output is fast enough - needs to be at least 10 fps
    538 
    539             long maxYuvRate =
    540                 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
    541             final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
    542             boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
    543 
    544             // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
    545 
    546             long maxFastYuvRate =
    547                     config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
    548             final long MIN_8MP_DURATION_BOUND_NS = 200000000; // 50 ms, 20 fps
    549             boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
    550 
    551             final int SIZE_8MP_BOUND = 8000000;
    552             boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
    553                     SIZE_8MP_BOUND;
    554 
    555             // Ensure that there's an FPS range that's fast enough to capture at above
    556             // minFrameDuration, for full-auto bursts at the fast resolutions
    557             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
    558                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
    559             float minYuvFps = 1.f / maxFastYuvRate;
    560 
    561             boolean haveFastAeTargetFps = false;
    562             for (Range<Integer> r : fpsRanges) {
    563                 if (r.getLower() >= minYuvFps) {
    564                     haveFastAeTargetFps = true;
    565                     break;
    566                 }
    567             }
    568 
    569             // Ensure that maximum sync latency is small enough for fast setting changes, even if
    570             // it's not quite per-frame
    571 
    572             Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
    573             assertNotNull(String.format("No sync latency declared for ID %s", mIds[counter]),
    574                     maxSyncLatencyValue);
    575 
    576             int maxSyncLatency = maxSyncLatencyValue;
    577             final long MAX_LATENCY_BOUND = 4;
    578             boolean haveFastSyncLatency =
    579                 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
    580 
    581             if (haveBurstCapability) {
    582                 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!",
    583                         slowYuvSizes != null);
    584                 assertTrue(
    585                         String.format("BURST-capable camera device %s does not have maximum YUV " +
    586                                 "size that is at least max JPEG size",
    587                                 mIds[counter]),
    588                         haveMaxYuv);
    589                 assertTrue(
    590                         String.format("BURST-capable camera device %s max-resolution " +
    591                                 "YUV frame rate is too slow" +
    592                                 "(%d ns min frame duration reported, less than %d ns expected)",
    593                                 mIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
    594                         haveMaxYuvRate);
    595                 assertTrue(
    596                         String.format("BURST-capable camera device %s >= 8MP YUV output " +
    597                                 "frame rate is too slow" +
    598                                 "(%d ns min frame duration reported, less than %d ns expected)",
    599                                 mIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
    600                         haveFastYuvRate);
    601                 assertTrue(
    602                         String.format("BURST-capable camera device %s does not list an AE target " +
    603                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
    604                                 mIds[counter], minYuvFps),
    605                         haveFastAeTargetFps);
    606                 assertTrue(
    607                         String.format("BURST-capable camera device %s YUV sync latency is too long" +
    608                                 "(%d frames reported, [0, %d] frames expected)",
    609                                 mIds[counter], maxSyncLatency, MAX_LATENCY_BOUND),
    610                         haveFastSyncLatency);
    611                 assertTrue(
    612                         String.format("BURST-capable camera device %s max YUV size %s should be" +
    613                                 "close to active array size %s",
    614                                 mIds[counter], maxYuvSize.toString(), sensorSize.toString()),
    615                         maxYuvMatchSensor);
    616                 assertTrue(
    617                         String.format("BURST-capable camera device %s does not support AE lock",
    618                                 mIds[counter]),
    619                         haveAeLock);
    620                 assertTrue(
    621                         String.format("BURST-capable camera device %s does not support AWB lock",
    622                                 mIds[counter]),
    623                         haveAwbLock);
    624             } else {
    625                 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!",
    626                         slowYuvSizes == null);
    627                 assertTrue(
    628                         String.format("Camera device %s has all the requirements for BURST" +
    629                                 " capability but does not report it!", mIds[counter]),
    630                         !(haveMaxYuv && haveMaxYuvRate && haveFastAeTargetFps &&
    631                                 haveFastSyncLatency && maxYuvMatchSensor &&
    632                                 haveAeLock && haveAwbLock));
    633             }
    634 
    635             counter++;
    636         }
    637     }
    638 
    639     /**
    640      * Check reprocessing capabilities.
    641      */
    642     public void testReprocessingCharacteristics() {
    643         int counter = 0;
    644 
    645         for (CameraCharacteristics c : mCharacteristics) {
    646             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mIds[counter]);
    647 
    648             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    649             assertNotNull("android.request.availableCapabilities must never be null",
    650                     capabilities);
    651             boolean supportYUV = arrayContains(capabilities,
    652                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
    653             boolean supportOpaque = arrayContains(capabilities,
    654                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
    655             StreamConfigurationMap configs =
    656                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    657             Integer maxNumInputStreams =
    658                     c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
    659             int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
    660             int[] availableNoiseReductionModes = c.get(
    661                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
    662 
    663             int[] inputFormats = configs.getInputFormats();
    664 
    665             boolean supportZslEdgeMode = false;
    666             boolean supportZslNoiseReductionMode = false;
    667 
    668             if (availableEdgeModes != null) {
    669                 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
    670                         contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
    671             }
    672 
    673             if (availableNoiseReductionModes != null) {
    674                 supportZslNoiseReductionMode = Arrays.asList(
    675                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
    676                         CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
    677             }
    678 
    679             if (supportYUV || supportOpaque) {
    680                 mCollector.expectTrue("Support reprocessing but max number of input stream is " +
    681                         maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
    682                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " +
    683                         "not supported", supportZslEdgeMode);
    684                 mCollector.expectTrue("Support reprocessing but " +
    685                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported",
    686                         supportZslNoiseReductionMode);
    687 
    688                 // Verify mandatory input formats are supported
    689                 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing",
    690                         !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888));
    691                 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing",
    692                         !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE));
    693 
    694                 // max capture stall must be reported if one of the reprocessing is supported.
    695                 final int MAX_ALLOWED_STALL_FRAMES = 4;
    696                 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
    697                 mCollector.expectTrue("max capture stall must be non-null and no larger than "
    698                         + MAX_ALLOWED_STALL_FRAMES,
    699                         maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
    700 
    701                 for (int input : inputFormats) {
    702                     // Verify mandatory output formats are supported
    703                     int[] outputFormats = configs.getValidOutputFormatsForInput(input);
    704                     mCollector.expectTrue("YUV_420_888 output must be supported for reprocessing",
    705                             arrayContains(outputFormats, ImageFormat.YUV_420_888));
    706                     mCollector.expectTrue("JPEG output must be supported for reprocessing",
    707                             arrayContains(outputFormats, ImageFormat.JPEG));
    708 
    709                     // Verify camera can output the reprocess input formats and sizes.
    710                     Size[] inputSizes = configs.getInputSizes(input);
    711                     Size[] outputSizes = configs.getOutputSizes(input);
    712                     Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
    713                     mCollector.expectTrue("no input size supported for format " + input,
    714                             inputSizes.length > 0);
    715                     mCollector.expectTrue("no output size supported for format " + input,
    716                             outputSizes.length > 0);
    717 
    718                     for (Size inputSize : inputSizes) {
    719                         mCollector.expectTrue("Camera must be able to output the supported " +
    720                                 "reprocessing input size",
    721                                 arrayContains(outputSizes, inputSize) ||
    722                                 arrayContains(highResOutputSizes, inputSize));
    723                     }
    724                 }
    725             } else {
    726                 mCollector.expectTrue("Doesn't support reprocessing but report input format: " +
    727                         Arrays.toString(inputFormats), inputFormats.length == 0);
    728                 mCollector.expectTrue("Doesn't support reprocessing but max number of input " +
    729                         "stream is " + maxNumInputStreams,
    730                         maxNumInputStreams == null || maxNumInputStreams == 0);
    731                 mCollector.expectTrue("Doesn't support reprocessing but " +
    732                         "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode);
    733                 mCollector.expectTrue("Doesn't support reprocessing but " +
    734                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported",
    735                         !supportZslNoiseReductionMode);
    736             }
    737         }
    738     }
    739 
    740     /**
    741      * Check depth output capability
    742      */
    743     public void testDepthOutputCharacteristics() {
    744         int counter = 0;
    745 
    746         for (CameraCharacteristics c : mCharacteristics) {
    747             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mIds[counter]);
    748 
    749             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    750             assertNotNull("android.request.availableCapabilities must never be null",
    751                     capabilities);
    752             boolean supportDepth = arrayContains(capabilities,
    753                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
    754             StreamConfigurationMap configs =
    755                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    756 
    757             int[] outputFormats = configs.getOutputFormats();
    758             boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
    759 
    760             Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
    761 
    762             float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
    763             float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
    764             float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
    765             float[] radialDistortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
    766 
    767             if (supportDepth) {
    768                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
    769                         hasDepth16);
    770                 if (hasDepth16) {
    771                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
    772                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
    773                             depthSizes != null && depthSizes.length > 0);
    774                     if (depthSizes != null) {
    775                         for (Size depthSize : depthSizes) {
    776                             mCollector.expectTrue("All depth16 sizes must be positive",
    777                                     depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
    778                             long minFrameDuration = configs.getOutputMinFrameDuration(
    779                                     ImageFormat.DEPTH16, depthSize);
    780                             mCollector.expectTrue("Non-negative min frame duration for depth size "
    781                                     + depthSize + " expected, got " + minFrameDuration,
    782                                     minFrameDuration >= 0);
    783                             long stallDuration = configs.getOutputStallDuration(
    784                                     ImageFormat.DEPTH16, depthSize);
    785                             mCollector.expectTrue("Non-negative stall duration for depth size "
    786                                     + depthSize + " expected, got " + stallDuration,
    787                                     stallDuration >= 0);
    788                         }
    789                     }
    790                 }
    791                 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
    792                     Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
    793                     mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
    794                             "but no sizes for DEPTH_POINT_CLOUD supported!",
    795                             depthCloudSizes != null && depthCloudSizes.length > 0);
    796                     if (depthCloudSizes != null) {
    797                         for (Size depthCloudSize : depthCloudSizes) {
    798                             mCollector.expectTrue("All depth point cloud sizes must be nonzero",
    799                                     depthCloudSize.getWidth() > 0);
    800                             mCollector.expectTrue("All depth point cloud sizes must be N x 1",
    801                                     depthCloudSize.getHeight() == 1);
    802                             long minFrameDuration = configs.getOutputMinFrameDuration(
    803                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
    804                             mCollector.expectTrue("Non-negative min frame duration for depth size "
    805                                     + depthCloudSize + " expected, got " + minFrameDuration,
    806                                     minFrameDuration >= 0);
    807                             long stallDuration = configs.getOutputStallDuration(
    808                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
    809                             mCollector.expectTrue("Non-negative stall duration for depth size "
    810                                     + depthCloudSize + " expected, got " + stallDuration,
    811                                     stallDuration >= 0);
    812                         }
    813                     }
    814                 }
    815 
    816                 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
    817                         depthIsExclusive != null);
    818 
    819                 mCollector.expectTrue(
    820                         "Supports DEPTH_OUTPUT but LENS_POSE_ROTATION not right size",
    821                         poseRotation != null && poseRotation.length == 4);
    822                 mCollector.expectTrue(
    823                         "Supports DEPTH_OUTPUT but LENS_POSE_TRANSLATION not right size",
    824                         poseTranslation != null && poseTranslation.length == 3);
    825                 mCollector.expectTrue(
    826                         "Supports DEPTH_OUTPUT but LENS_INTRINSIC_CALIBRATION not right size",
    827                         cameraIntrinsics != null && cameraIntrinsics.length == 5);
    828                 mCollector.expectTrue(
    829                         "Supports DEPTH_OUTPUT but LENS_RADIAL_DISTORTION not right size",
    830                         radialDistortion != null && radialDistortion.length == 6);
    831 
    832                 if (poseRotation != null && poseRotation.length == 4) {
    833                     float normSq =
    834                         poseRotation[0] * poseRotation[0] +
    835                         poseRotation[1] * poseRotation[1] +
    836                         poseRotation[2] * poseRotation[2] +
    837                         poseRotation[3] * poseRotation[3];
    838                     mCollector.expectTrue(
    839                             "LENS_POSE_ROTATION quarternion must be unit-length",
    840                             0.9999f < normSq && normSq < 1.0001f);
    841 
    842                     // TODO: Cross-validate orientation/facing and poseRotation
    843                     Integer orientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
    844                     Integer facing = c.get(CameraCharacteristics.LENS_FACING);
    845                 }
    846 
    847                 if (poseTranslation != null && poseTranslation.length == 3) {
    848                     float normSq =
    849                         poseTranslation[0] * poseTranslation[0] +
    850                         poseTranslation[1] * poseTranslation[1] +
    851                         poseTranslation[2] * poseTranslation[2];
    852                     mCollector.expectTrue("Pose translation is larger than 1 m",
    853                             normSq < 1.f);
    854                 }
    855 
    856                 Rect precorrectionArray =
    857                     c.get(CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
    858                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not have " +
    859                         "precorrection active array defined", precorrectionArray != null);
    860 
    861                 if (cameraIntrinsics != null && precorrectionArray != null) {
    862                     float fx = cameraIntrinsics[0];
    863                     float fy = cameraIntrinsics[1];
    864                     float cx = cameraIntrinsics[2];
    865                     float cy = cameraIntrinsics[3];
    866                     float s = cameraIntrinsics[4];
    867                     mCollector.expectTrue("Optical center expected to be within precorrection array",
    868                             0 <= cx && cx < precorrectionArray.width() &&
    869                             0 <= cy && cy < precorrectionArray.height());
    870 
    871                     // TODO: Verify focal lengths and skew are reasonable
    872                 }
    873 
    874                 if (radialDistortion != null) {
    875                     // TODO: Verify radial distortion
    876                 }
    877 
    878             } else {
    879                 boolean hasFields =
    880                     hasDepth16 && (poseTranslation != null) &&
    881                     (poseRotation != null) && (cameraIntrinsics != null) &&
    882                     (radialDistortion != null) && (depthIsExclusive != null);
    883 
    884                 mCollector.expectTrue(
    885                         "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
    886                         !hasFields);
    887             }
    888         }
    889     }
    890 
    891     /**
    892      * Cross-check StreamConfigurationMap output
    893      */
    894     public void testStreamConfigurationMap() throws Exception {
    895         int counter = 0;
    896         for (CameraCharacteristics c : mCharacteristics) {
    897             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
    898             StreamConfigurationMap config =
    899                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    900             assertNotNull(String.format("No stream configuration map found for: ID %s",
    901                             mIds[counter]), config);
    902 
    903             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    904             assertNotNull("android.request.availableCapabilities must never be null",
    905                     actualCapabilities);
    906 
    907             if (arrayContains(actualCapabilities, BC)) {
    908                 assertTrue("ImageReader must be supported",
    909                     config.isOutputSupportedFor(android.media.ImageReader.class));
    910                 assertTrue("MediaRecorder must be supported",
    911                     config.isOutputSupportedFor(android.media.MediaRecorder.class));
    912                 assertTrue("MediaCodec must be supported",
    913                     config.isOutputSupportedFor(android.media.MediaCodec.class));
    914                 assertTrue("Allocation must be supported",
    915                     config.isOutputSupportedFor(android.renderscript.Allocation.class));
    916                 assertTrue("SurfaceHolder must be supported",
    917                     config.isOutputSupportedFor(android.view.SurfaceHolder.class));
    918                 assertTrue("SurfaceTexture must be supported",
    919                     config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
    920 
    921                 assertTrue("YUV_420_888 must be supported",
    922                     config.isOutputSupportedFor(ImageFormat.YUV_420_888));
    923                 assertTrue("JPEG must be supported",
    924                     config.isOutputSupportedFor(ImageFormat.JPEG));
    925             } else {
    926                 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed",
    927                     !config.isOutputSupportedFor(ImageFormat.YUV_420_888));
    928                 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed",
    929                     !config.isOutputSupportedFor(ImageFormat.JPEG));
    930             }
    931 
    932             // Legacy YUV formats should not be listed
    933             assertTrue("NV21 must not be supported",
    934                     !config.isOutputSupportedFor(ImageFormat.NV21));
    935 
    936             // Check RAW
    937 
    938             if (arrayContains(actualCapabilities,
    939                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
    940                 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
    941                     config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
    942             }
    943 
    944             // Cross check public formats and sizes
    945 
    946             int[] supportedFormats = config.getOutputFormats();
    947             for (int format : supportedFormats) {
    948                 assertTrue("Format " + format + " fails cross check",
    949                         config.isOutputSupportedFor(format));
    950                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
    951                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
    952                 if (arrayContains(actualCapabilities,
    953                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
    954                     supportedSizes.addAll(
    955                         Arrays.asList(config.getHighResolutionOutputSizes(format)));
    956                     supportedSizes = CameraTestUtils.getAscendingOrderSizes(
    957                         supportedSizes, /*ascending*/true);
    958                 }
    959                 assertTrue("Supported format " + format + " has no sizes listed",
    960                         supportedSizes.size() > 0);
    961                 for (int i = 0; i < supportedSizes.size(); i++) {
    962                     Size size = supportedSizes.get(i);
    963                     if (VERBOSE) {
    964                         Log.v(TAG,
    965                                 String.format("Testing camera %s, format %d, size %s",
    966                                         mIds[counter], format, size.toString()));
    967                     }
    968 
    969                     long stallDuration = config.getOutputStallDuration(format, size);
    970                     switch(format) {
    971                         case ImageFormat.YUV_420_888:
    972                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
    973                                     stallDuration == 0);
    974                             break;
    975                         case ImageFormat.JPEG:
    976                         case ImageFormat.RAW_SENSOR:
    977                             final float TOLERANCE_FACTOR = 2.0f;
    978                             long prevDuration = 0;
    979                             if (i > 0) {
    980                                 prevDuration = config.getOutputStallDuration(
    981                                         format, supportedSizes.get(i - 1));
    982                             }
    983                             long nextDuration = Long.MAX_VALUE;
    984                             if (i < (supportedSizes.size() - 1)) {
    985                                 nextDuration = config.getOutputStallDuration(
    986                                         format, supportedSizes.get(i + 1));
    987                             }
    988                             long curStallDuration = config.getOutputStallDuration(format, size);
    989                             // Stall duration should be in a reasonable range: larger size should
    990                             // normally have larger stall duration.
    991                             mCollector.expectInRange("Stall duration (format " + format +
    992                                     " and size " + size + ") is not in the right range",
    993                                     curStallDuration,
    994                                     (long) (prevDuration / TOLERANCE_FACTOR),
    995                                     (long) (nextDuration * TOLERANCE_FACTOR));
    996                             break;
    997                         default:
    998                             assertTrue("Negative stall duration for format " + format,
    999                                     stallDuration >= 0);
   1000                             break;
   1001                     }
   1002                     long minDuration = config.getOutputMinFrameDuration(format, size);
   1003                     if (arrayContains(actualCapabilities,
   1004                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
   1005                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
   1006                                 + "format " + format + " for size " + size + " minDuration " +
   1007                                 minDuration,
   1008                                 minDuration > 0);
   1009                     } else {
   1010                         assertTrue("Need non-negative min frame duration for format " + format,
   1011                                 minDuration >= 0);
   1012                     }
   1013 
   1014                     // todo: test opaque image reader when it's supported.
   1015                     if (format != ImageFormat.PRIVATE) {
   1016                         ImageReader testReader = ImageReader.newInstance(
   1017                             size.getWidth(),
   1018                             size.getHeight(),
   1019                             format,
   1020                             1);
   1021                         Surface testSurface = testReader.getSurface();
   1022 
   1023                         assertTrue(
   1024                             String.format("isOutputSupportedFor fails for config %s, format %d",
   1025                                     size.toString(), format),
   1026                             config.isOutputSupportedFor(testSurface));
   1027 
   1028                         testReader.close();
   1029                     }
   1030                 } // sizes
   1031 
   1032                 // Try an invalid size in this format, should round
   1033                 Size invalidSize = findInvalidSize(supportedSizes);
   1034                 int MAX_ROUNDING_WIDTH = 1920;
   1035                 // todo: test opaque image reader when it's supported.
   1036                 if (format != ImageFormat.PRIVATE &&
   1037                         invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
   1038                     ImageReader testReader = ImageReader.newInstance(
   1039                                                                      invalidSize.getWidth(),
   1040                                                                      invalidSize.getHeight(),
   1041                                                                      format,
   1042                                                                      1);
   1043                     Surface testSurface = testReader.getSurface();
   1044 
   1045                     assertTrue(
   1046                                String.format("isOutputSupportedFor fails for config %s, %d",
   1047                                        invalidSize.toString(), format),
   1048                                config.isOutputSupportedFor(testSurface));
   1049 
   1050                     testReader.close();
   1051                 }
   1052             } // formats
   1053 
   1054             // Cross-check opaque format and sizes
   1055             if (arrayContains(actualCapabilities, BC)) {
   1056                 SurfaceTexture st = new SurfaceTexture(1);
   1057                 Surface surf = new Surface(st);
   1058 
   1059                 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
   1060                         mIds[counter], mCameraManager);
   1061                 assertTrue("Opaque format has no sizes listed",
   1062                         opaqueSizes.length > 0);
   1063                 for (Size size : opaqueSizes) {
   1064                     long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
   1065                     assertTrue("Opaque output may not have a non-zero stall duration",
   1066                             stallDuration == 0);
   1067 
   1068                     long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
   1069                     if (arrayContains(actualCapabilities,
   1070                                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
   1071                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
   1072                                 + "opaque format",
   1073                                 minDuration > 0);
   1074                     } else {
   1075                         assertTrue("Need non-negative min frame duration for opaque format ",
   1076                                 minDuration >= 0);
   1077                     }
   1078                     st.setDefaultBufferSize(size.getWidth(), size.getHeight());
   1079 
   1080                     assertTrue(
   1081                             String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
   1082                                     size.toString()),
   1083                             config.isOutputSupportedFor(surf));
   1084 
   1085                 } // opaque sizes
   1086 
   1087                 // Try invalid opaque size, should get rounded
   1088                 Size invalidSize = findInvalidSize(opaqueSizes);
   1089                 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
   1090                 assertTrue(
   1091                         String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
   1092                                 invalidSize.toString()),
   1093                         config.isOutputSupportedFor(surf));
   1094 
   1095                 counter++;
   1096             }
   1097 
   1098         } // mCharacteristics
   1099     }
   1100 
   1101     /**
   1102      * Test high speed capability and cross-check the high speed sizes and fps ranges from
   1103      * the StreamConfigurationMap.
   1104      */
   1105     public void testConstrainedHighSpeedCapability() throws Exception {
   1106         int counter = 0;
   1107         for (CameraCharacteristics c : mCharacteristics) {
   1108             int[] capabilities = CameraTestUtils.getValueNotNull(
   1109                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1110             boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED);
   1111             if (supportHighSpeed) {
   1112                 StreamConfigurationMap config =
   1113                         CameraTestUtils.getValueNotNull(
   1114                                 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1115                 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
   1116                 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
   1117                 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
   1118                         mIds[counter], mCameraManager);
   1119                 assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
   1120                         allSizes != null && allSizes.length > 0);
   1121                 for (Size size: highSpeedSizes) {
   1122                     // The sizes must be a subset of the normal sizes
   1123                     assertTrue("High speed size " + size +
   1124                             " must be part of normal sizes " + Arrays.toString(allSizes),
   1125                             Arrays.asList(allSizes).contains(size));
   1126 
   1127                     // Sanitize the high speed FPS ranges for each size
   1128                     List<Range<Integer>> ranges =
   1129                             Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size));
   1130                     for (Range<Integer> range : ranges) {
   1131                         assertTrue("The range " + range + " doesn't satisfy the"
   1132                                 + " min/max boundary requirements.",
   1133                                 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN &&
   1134                                 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN);
   1135                         assertTrue("The range " + range + " should be multiple of 30fps",
   1136                                 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0);
   1137                         // If the range is fixed high speed range, it should contain the
   1138                         // [30, fps_max] in the high speed range list; if it's variable FPS range,
   1139                         // the corresponding fixed FPS Range must be included in the range list.
   1140                         if (range.getLower() == range.getUpper()) {
   1141                             Range<Integer> variableRange = new Range<Integer>(30, range.getUpper());
   1142                             assertTrue("The variable FPS range " + variableRange +
   1143                                     " shoould be included in the high speed ranges for size " +
   1144                                     size, ranges.contains(variableRange));
   1145                         } else {
   1146                             Range<Integer> fixedRange =
   1147                                     new Range<Integer>(range.getUpper(), range.getUpper());
   1148                             assertTrue("The fixed FPS range " + fixedRange +
   1149                                     " shoould be included in the high speed ranges for size " +
   1150                                     size, ranges.contains(fixedRange));
   1151                         }
   1152                     }
   1153                 }
   1154                 // If the device advertise some high speed profiles, the sizes and FPS ranges
   1155                 // should be advertise by the camera.
   1156                 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
   1157                         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
   1158                     if (CamcorderProfile.hasProfile(quality)) {
   1159                         CamcorderProfile profile = CamcorderProfile.get(quality);
   1160                         Size camcorderProfileSize =
   1161                                 new Size(profile.videoFrameWidth, profile.videoFrameHeight);
   1162                         assertTrue("CamcorderPrfile size " + camcorderProfileSize +
   1163                                 " must be included in the high speed sizes " +
   1164                                 Arrays.toString(highSpeedSizes.toArray()),
   1165                                 highSpeedSizes.contains(camcorderProfileSize));
   1166                         Range<Integer> camcorderFpsRange =
   1167                                 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate);
   1168                         List<Range<Integer>> allRanges =
   1169                                 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(
   1170                                         camcorderProfileSize));
   1171                         assertTrue("Camcorder fps range " + camcorderFpsRange +
   1172                                 " should be included by high speed fps ranges " +
   1173                                 Arrays.toString(allRanges.toArray()),
   1174                                 allRanges.contains(camcorderFpsRange));
   1175                     }
   1176                 }
   1177             }
   1178             counter++;
   1179         }
   1180     }
   1181 
   1182     /**
   1183      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
   1184      */
   1185     private Size findInvalidSize(Size[] goodSizes) {
   1186         return findInvalidSize(Arrays.asList(goodSizes));
   1187     }
   1188 
   1189     /**
   1190      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
   1191      */
   1192     private Size findInvalidSize(List<Size> goodSizes) {
   1193         Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
   1194         while(goodSizes.contains(invalidSize)) {
   1195             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
   1196         }
   1197         return invalidSize;
   1198     }
   1199 
   1200     /**
   1201      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
   1202      * check that the key is present if the actual capabilities are one of {@code capabilities}.
   1203      *
   1204      * @return value of the {@code key} from {@code c}
   1205      */
   1206     private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
   1207             int hwLevel, int... capabilities) {
   1208 
   1209         Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
   1210         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
   1211 
   1212         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1213         assertNotNull("android.request.availableCapabilities must never be null",
   1214                 actualCapabilities);
   1215 
   1216         List<Key<?>> allKeys = c.getKeys();
   1217 
   1218         T value = c.get(key);
   1219 
   1220         // For LIMITED-level targeted keys, rely on capability check, not level
   1221         if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) {
   1222             mCollector.expectTrue(
   1223                     String.format("Key (%s) must be in characteristics for this hardware level " +
   1224                             "(required minimal HW level %s, actual HW level %s)",
   1225                             key.getName(), toStringHardwareLevel(hwLevel),
   1226                             toStringHardwareLevel(actualHwLevel)),
   1227                     value != null);
   1228             mCollector.expectTrue(
   1229                     String.format("Key (%s) must be in characteristics list of keys for this " +
   1230                             "hardware level (required minimal HW level %s, actual HW level %s)",
   1231                             key.getName(), toStringHardwareLevel(hwLevel),
   1232                             toStringHardwareLevel(actualHwLevel)),
   1233                     allKeys.contains(key));
   1234         } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
   1235             if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) {
   1236                 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined
   1237                 mCollector.expectTrue(
   1238                     String.format("Key (%s) must be in characteristics for these capabilities " +
   1239                             "(required capabilities %s, actual capabilities %s)",
   1240                             key.getName(), Arrays.toString(capabilities),
   1241                             Arrays.toString(actualCapabilities)),
   1242                     value != null);
   1243                 mCollector.expectTrue(
   1244                     String.format("Key (%s) must be in characteristics list of keys for " +
   1245                             "these capabilities (required capabilities %s, actual capabilities %s)",
   1246                             key.getName(), Arrays.toString(capabilities),
   1247                             Arrays.toString(actualCapabilities)),
   1248                     allKeys.contains(key));
   1249             }
   1250         } else {
   1251             if (actualHwLevel == LEGACY && hwLevel != OPT) {
   1252                 if (value != null || allKeys.contains(key)) {
   1253                     Log.w(TAG, String.format(
   1254                             "Key (%s) is not required for LEGACY devices but still appears",
   1255                             key.getName()));
   1256                 }
   1257             }
   1258             // OK: Key may or may not be present.
   1259         }
   1260         return value;
   1261     }
   1262 
   1263     private static boolean arrayContains(int[] arr, int needle) {
   1264         if (arr == null) {
   1265             return false;
   1266         }
   1267 
   1268         for (int elem : arr) {
   1269             if (elem == needle) {
   1270                 return true;
   1271             }
   1272         }
   1273 
   1274         return false;
   1275     }
   1276 
   1277     private static <T> boolean arrayContains(T[] arr, T needle) {
   1278         if (arr == null) {
   1279             return false;
   1280         }
   1281 
   1282         for (T elem : arr) {
   1283             if (elem.equals(needle)) {
   1284                 return true;
   1285             }
   1286         }
   1287 
   1288         return false;
   1289     }
   1290 
   1291     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
   1292         for (int needle : needles) {
   1293             if (arrayContains(arr, needle)) {
   1294                 return true;
   1295             }
   1296         }
   1297         return false;
   1298     }
   1299 
   1300     /**
   1301      * The key name has a prefix of either "android." or "com."; other prefixes are not valid.
   1302      */
   1303     private static void assertKeyPrefixValid(String keyName) {
   1304         assertStartsWithAnyOf(
   1305                 "All metadata keys must start with 'android.' (built-in keys) " +
   1306                 "or 'com.' (vendor-extended keys)", new String[] {
   1307                         PREFIX_ANDROID + ".",
   1308                         PREFIX_VENDOR + ".",
   1309                 }, keyName);
   1310     }
   1311 
   1312     private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
   1313             boolean actual) {
   1314         assertTrue(msg + " (key = '" + key.getName() + "')", actual);
   1315     }
   1316 
   1317     private static <T> void assertOneOf(String msg, T[] expected, T actual) {
   1318         for (int i = 0; i < expected.length; ++i) {
   1319             if (Objects.equals(expected[i], actual)) {
   1320                 return;
   1321             }
   1322         }
   1323 
   1324         fail(String.format("%s: (expected one of %s, actual %s)",
   1325                 msg, Arrays.toString(expected), actual));
   1326     }
   1327 
   1328     private static <T> void assertStartsWithAnyOf(String msg, String[] expected, String actual) {
   1329         for (int i = 0; i < expected.length; ++i) {
   1330             if (actual.startsWith(expected[i])) {
   1331                 return;
   1332             }
   1333         }
   1334 
   1335         fail(String.format("%s: (expected to start with any of %s, but value was %s)",
   1336                 msg, Arrays.toString(expected), actual));
   1337     }
   1338 
   1339     /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
   1340     private static int compareHardwareLevel(int left, int right) {
   1341         return remapHardwareLevel(left) - remapHardwareLevel(right);
   1342     }
   1343 
   1344     /** Remap HW levels worst<->best, 0 = worst, 2 = best */
   1345     private static int remapHardwareLevel(int level) {
   1346         switch (level) {
   1347             case OPT:
   1348                 return Integer.MAX_VALUE;
   1349             case LEGACY:
   1350                 return 0; // lowest
   1351             case LIMITED:
   1352                 return 1; // second lowest
   1353             case FULL:
   1354                 return 2; // best
   1355         }
   1356 
   1357         fail("Unknown HW level: " + level);
   1358         return -1;
   1359     }
   1360 
   1361     private static String toStringHardwareLevel(int level) {
   1362         switch (level) {
   1363             case LEGACY:
   1364                 return "LEGACY";
   1365             case LIMITED:
   1366                 return "LIMITED";
   1367             case FULL:
   1368                 return "FULL";
   1369         }
   1370 
   1371         // unknown
   1372         Log.w(TAG, "Unknown hardware level " + level);
   1373         return Integer.toString(level);
   1374     }
   1375 }
   1376