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.Camera;
     24 import android.hardware.camera2.CameraCharacteristics;
     25 import android.hardware.camera2.CameraCharacteristics.Key;
     26 import android.hardware.camera2.CameraDevice;
     27 import android.hardware.camera2.CameraManager;
     28 import android.hardware.camera2.CameraMetadata;
     29 import android.hardware.camera2.CaptureRequest;
     30 import android.hardware.camera2.CaptureResult;
     31 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
     32 import android.hardware.camera2.cts.helpers.StaticMetadata;
     33 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
     34 import android.hardware.camera2.params.BlackLevelPattern;
     35 import android.hardware.camera2.params.ColorSpaceTransform;
     36 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
     37 import android.hardware.camera2.params.StreamConfigurationMap;
     38 import android.media.CamcorderProfile;
     39 import android.media.ImageReader;
     40 import android.os.Build;
     41 import android.util.DisplayMetrics;
     42 import android.util.Log;
     43 import android.util.Rational;
     44 import android.util.Range;
     45 import android.util.Size;
     46 import android.util.Pair;
     47 import android.util.Patterns;
     48 import android.view.Display;
     49 import android.view.Surface;
     50 import android.view.WindowManager;
     51 
     52 import com.android.compatibility.common.util.CddTest;
     53 
     54 import java.util.ArrayList;
     55 import java.util.Arrays;
     56 import java.util.List;
     57 import java.util.Objects;
     58 import java.util.regex.Matcher;
     59 import java.util.regex.Pattern;
     60 import java.util.Set;
     61 
     62 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
     63 import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
     64 
     65 import static org.mockito.Mockito.*;
     66 
     67 /**
     68  * Extended tests for static camera characteristics.
     69  */
     70 public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase {
     71     private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
     72     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     73 
     74     private static final String PREFIX_ANDROID = "android";
     75 
     76     /*
     77      * Constants for static RAW metadata.
     78      */
     79     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
     80 
     81     private List<CameraCharacteristics> mCharacteristics;
     82 
     83     private static final Size FULLHD = new Size(1920, 1080);
     84     private static final Size FULLHD_ALT = new Size(1920, 1088);
     85     private static final Size HD = new Size(1280, 720);
     86     private static final Size VGA = new Size(640, 480);
     87     private static final Size QVGA = new Size(320, 240);
     88 
     89     private static final long MIN_BACK_SENSOR_RESOLUTION = 2000000;
     90     private static final long MIN_FRONT_SENSOR_RESOLUTION = VGA.getHeight() * VGA.getWidth();
     91     private static final long LOW_LATENCY_THRESHOLD_MS = 200;
     92     private static final float LATENCY_TOLERANCE_FACTOR = 1.1f; // 10% tolerance
     93     private static final float FOCAL_LENGTH_TOLERANCE = .01f;
     94     private static final int MAX_NUM_IMAGES = 5;
     95     private static final long PREVIEW_RUN_MS = 500;
     96     private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
     97     /*
     98      * HW Levels short hand
     99      */
    100     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
    101     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
    102     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
    103     private static final int LEVEL_3 = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3;
    104     private static final int EXTERNAL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
    105     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
    106 
    107     /*
    108      * Capabilities short hand
    109      */
    110     private static final int NONE = -1;
    111     private static final int BC =
    112             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
    113     private static final int MANUAL_SENSOR =
    114             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
    115     private static final int MANUAL_POSTPROC =
    116             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING;
    117     private static final int RAW =
    118             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
    119     private static final int YUV_REPROCESS =
    120             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
    121     private static final int OPAQUE_REPROCESS =
    122             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
    123     private static final int CONSTRAINED_HIGH_SPEED =
    124             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO;
    125     private static final int MONOCHROME =
    126             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME;
    127     private static final int HIGH_SPEED_FPS_LOWER_MIN = 30;
    128     private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
    129 
    130     @Override
    131     protected void setUp() throws Exception {
    132         super.setUp();
    133         mCharacteristics = new ArrayList<>();
    134         for (int i = 0; i < mAllCameraIds.length; i++) {
    135             mCharacteristics.add(mAllStaticInfo.get(mAllCameraIds[i]).getCharacteristics());
    136         }
    137     }
    138 
    139     @Override
    140     protected void tearDown() throws Exception {
    141         super.tearDown();
    142         mCharacteristics = null;
    143     }
    144 
    145     /**
    146      * Test that the available stream configurations contain a few required formats and sizes.
    147      */
    148     public void testAvailableStreamConfigs() throws Exception {
    149         int counter = 0;
    150         for (String id : mAllCameraIds) {
    151             CameraCharacteristics c = mAllStaticInfo.get(id).getCharacteristics();
    152             StreamConfigurationMap config =
    153                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    154             assertNotNull(String.format("No stream configuration map found for: ID %s",
    155                     mAllCameraIds[counter]), config);
    156             int[] outputFormats = config.getOutputFormats();
    157 
    158             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    159             assertNotNull("android.request.availableCapabilities must never be null",
    160                     actualCapabilities);
    161 
    162             // Check required formats exist (JPEG, and YUV_420_888).
    163             if (!arrayContains(actualCapabilities, BC)) {
    164                 Log.i(TAG, "Camera " + mAllCameraIds[counter] +
    165                     ": BACKWARD_COMPATIBLE capability not supported, skipping test");
    166                 continue;
    167             }
    168 
    169             boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
    170                     && arrayContains(outputFormats, ImageFormat.Y8);
    171             boolean isHiddenPhysicalCamera = !arrayContains(mCameraIds, id);
    172             boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
    173 
    174             assertArrayContains(
    175                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
    176                             mAllCameraIds[counter]), outputFormats, ImageFormat.YUV_420_888);
    177             if (isMonochromeWithY8) {
    178                 assertArrayContains(
    179                         String.format("No valid Y8 preview formats found for: ID %s",
    180                                 mAllCameraIds[counter]), outputFormats, ImageFormat.Y8);
    181             }
    182             assertArrayContains(String.format("No JPEG image format for: ID %s",
    183                     mAllCameraIds[counter]), outputFormats, ImageFormat.JPEG);
    184 
    185             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
    186             Size[] y8Sizes = config.getOutputSizes(ImageFormat.Y8);
    187             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
    188             Size[] heicSizes = config.getOutputSizes(ImageFormat.HEIC);
    189             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
    190 
    191             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
    192                     String.format("No sizes for preview format %x for: ID %s",
    193                             ImageFormat.YUV_420_888, mAllCameraIds[counter]));
    194             if (isMonochromeWithY8) {
    195                 CameraTestUtils.assertArrayNotEmpty(y8Sizes,
    196                     String.format("No sizes for preview format %x for: ID %s",
    197                             ImageFormat.Y8, mAllCameraIds[counter]));
    198             }
    199 
    200             Rect activeRect = CameraTestUtils.getValueNotNull(
    201                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    202             Size pixelArraySize = CameraTestUtils.getValueNotNull(
    203                     c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
    204 
    205             int activeArrayHeight = activeRect.height();
    206             int activeArrayWidth = activeRect.width();
    207             long sensorResolution = pixelArraySize.getHeight() * pixelArraySize.getWidth() ;
    208             Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
    209             assertNotNull("Can't get lens facing info for camera id: " + mAllCameraIds[counter], lensFacing);
    210 
    211             // Check that the sensor sizes are atleast what the CDD specifies
    212             switch(lensFacing) {
    213                 case CameraCharacteristics.LENS_FACING_FRONT:
    214                     assertTrue("Front Sensor resolution should be at least " +
    215                             MIN_FRONT_SENSOR_RESOLUTION + " pixels, is "+ sensorResolution,
    216                             sensorResolution >= MIN_FRONT_SENSOR_RESOLUTION);
    217                     break;
    218                 case CameraCharacteristics.LENS_FACING_BACK:
    219                     assertTrue("Back Sensor resolution should be at least "
    220                             + MIN_BACK_SENSOR_RESOLUTION +
    221                             " pixels, is "+ sensorResolution,
    222                             sensorResolution >= MIN_BACK_SENSOR_RESOLUTION);
    223                     break;
    224                 default:
    225                     break;
    226             }
    227 
    228             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
    229 
    230             if (activeArrayWidth >= FULLHD.getWidth() &&
    231                     activeArrayHeight >= FULLHD.getHeight()) {
    232                 assertArrayContainsAnyOf(String.format(
    233                         "Required FULLHD size not found for format %x for: ID %s",
    234                         ImageFormat.JPEG, mAllCameraIds[counter]), jpegSizes,
    235                         new Size[] {FULLHD, FULLHD_ALT});
    236                 if (supportHeic) {
    237                     assertArrayContainsAnyOf(String.format(
    238                             "Required FULLHD size not found for format %x for: ID %s",
    239                             ImageFormat.HEIC, mAllCameraIds[counter]), heicSizes,
    240                             new Size[] {FULLHD, FULLHD_ALT});
    241                 }
    242             }
    243 
    244             if (activeArrayWidth >= HD.getWidth() &&
    245                     activeArrayHeight >= HD.getHeight()) {
    246                 assertArrayContains(String.format(
    247                         "Required HD size not found for format %x for: ID %s",
    248                         ImageFormat.JPEG, mAllCameraIds[counter]), jpegSizes, HD);
    249                 if (supportHeic) {
    250                     assertArrayContains(String.format(
    251                             "Required HD size not found for format %x for: ID %s",
    252                             ImageFormat.HEIC, mAllCameraIds[counter]), heicSizes, HD);
    253                 }
    254             }
    255 
    256             if (activeArrayWidth >= VGA.getWidth() &&
    257                     activeArrayHeight >= VGA.getHeight()) {
    258                 assertArrayContains(String.format(
    259                         "Required VGA size not found for format %x for: ID %s",
    260                         ImageFormat.JPEG, mAllCameraIds[counter]), jpegSizes, VGA);
    261                 if (supportHeic) {
    262                     assertArrayContains(String.format(
    263                             "Required VGA size not found for format %x for: ID %s",
    264                             ImageFormat.HEIC, mAllCameraIds[counter]), heicSizes, VGA);
    265                 }
    266             }
    267 
    268             if (activeArrayWidth >= QVGA.getWidth() &&
    269                     activeArrayHeight >= QVGA.getHeight()) {
    270                 assertArrayContains(String.format(
    271                         "Required QVGA size not found for format %x for: ID %s",
    272                         ImageFormat.JPEG, mAllCameraIds[counter]), jpegSizes, QVGA);
    273                 if (supportHeic) {
    274                     assertArrayContains(String.format(
    275                             "Required QVGA size not found for format %x for: ID %s",
    276                             ImageFormat.HEIC, mAllCameraIds[counter]), heicSizes, QVGA);
    277                 }
    278 
    279             }
    280 
    281             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
    282             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
    283             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
    284             boolean isExternalCamera = (hwLevel ==
    285                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
    286             Size maxVideoSize = null;
    287             if (isExternalCamera || isHiddenPhysicalCamera) {
    288                 // TODO: for now, use FULLHD 30 as largest possible video size for external camera.
    289                 // For hidden physical camera, since we don't require CamcorderProfile to be
    290                 // available, use FULLHD 30 as maximum video size as well.
    291                 List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(
    292                         mAllCameraIds[counter], mCameraManager, FULLHD);
    293                 for (Size sz : videoSizes) {
    294                     long minFrameDuration = config.getOutputMinFrameDuration(
    295                             android.media.MediaRecorder.class, sz);
    296                     // Give some margin for rounding error
    297                     if (minFrameDuration < (1e9 / 29.9)) {
    298                         maxVideoSize = sz;
    299                         break;
    300                     }
    301                 }
    302             } else {
    303                 int cameraId = Integer.valueOf(mAllCameraIds[counter]);
    304                 CamcorderProfile maxVideoProfile = CamcorderProfile.get(
    305                         cameraId, CamcorderProfile.QUALITY_HIGH);
    306                 maxVideoSize = new Size(
    307                         maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
    308             }
    309             if (maxVideoSize == null) {
    310                 fail("Camera " + mAllCameraIds[counter] + " does not support any 30fps video output");
    311             }
    312 
    313             // Handle FullHD special case first
    314             if (jpegSizesList.contains(FULLHD)) {
    315                 if (compareHardwareLevel(hwLevel, LEVEL_3) >= 0 || hwLevel == FULL ||
    316                         (hwLevel == LIMITED &&
    317                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
    318                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
    319                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
    320                             yuvSizesList.contains(FULLHD_ALT);
    321                     boolean privateSupportFullHD = privateSizesList.contains(FULLHD) ||
    322                             privateSizesList.contains(FULLHD_ALT);
    323                     assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD);
    324                     assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD);
    325 
    326                     if (isMonochromeWithY8) {
    327                         ArrayList<Size> y8SizesList = new ArrayList<>(Arrays.asList(y8Sizes));
    328                         boolean y8SupportFullHD = y8SizesList.contains(FULLHD) ||
    329                                 y8SizesList.contains(FULLHD_ALT);
    330                         assertTrue("Full device FullHD Y8 size not found", y8SupportFullHD);
    331                     }
    332                 }
    333                 // remove all FullHD or FullHD_Alt sizes for the remaining of the test
    334                 jpegSizesList.remove(FULLHD);
    335                 jpegSizesList.remove(FULLHD_ALT);
    336             }
    337 
    338             // Check all sizes other than FullHD
    339             if (hwLevel == LIMITED) {
    340                 // Remove all jpeg sizes larger than max video size
    341                 ArrayList<Size> toBeRemoved = new ArrayList<>();
    342                 for (Size size : jpegSizesList) {
    343                     if (size.getWidth() >= maxVideoSize.getWidth() &&
    344                             size.getHeight() >= maxVideoSize.getHeight()) {
    345                         toBeRemoved.add(size);
    346                     }
    347                 }
    348                 jpegSizesList.removeAll(toBeRemoved);
    349             }
    350 
    351             if (compareHardwareLevel(hwLevel, LEVEL_3) >= 0 || hwLevel == FULL ||
    352                     hwLevel == LIMITED) {
    353                 if (!yuvSizesList.containsAll(jpegSizesList)) {
    354                     for (Size s : jpegSizesList) {
    355                         if (!yuvSizesList.contains(s)) {
    356                             fail("Size " + s + " not found in YUV format");
    357                         }
    358                     }
    359                 }
    360 
    361                 if (isMonochromeWithY8) {
    362                     ArrayList<Size> y8SizesList = new ArrayList<>(Arrays.asList(y8Sizes));
    363                     if (!y8SizesList.containsAll(jpegSizesList)) {
    364                         for (Size s : jpegSizesList) {
    365                             if (!y8SizesList.contains(s)) {
    366                                 fail("Size " + s + " not found in Y8 format");
    367                             }
    368                         }
    369                     }
    370                 }
    371             }
    372 
    373             if (!privateSizesList.containsAll(yuvSizesList)) {
    374                 for (Size s : yuvSizesList) {
    375                     if (!privateSizesList.contains(s)) {
    376                         fail("Size " + s + " not found in PRIVATE format");
    377                     }
    378                 }
    379             }
    380 
    381             counter++;
    382         }
    383     }
    384 
    385     private void verifyCommonRecommendedConfiguration(String id, CameraCharacteristics c,
    386             RecommendedStreamConfigurationMap config, boolean checkNoInput,
    387             boolean checkNoHighRes, boolean checkNoHighSpeed, boolean checkNoPrivate,
    388             boolean checkNoDepth) {
    389         StreamConfigurationMap fullConfig = c.get(
    390                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    391         assertNotNull(String.format("No stream configuration map found for ID: %s!", id),
    392                 fullConfig);
    393 
    394         Set<Integer> recommendedOutputFormats = config.getOutputFormats();
    395 
    396         if (checkNoInput) {
    397             Set<Integer> inputFormats = config.getInputFormats();
    398             assertTrue(String.format("Recommended configuration must not include any input " +
    399                     "streams for ID: %s", id),
    400                     ((inputFormats == null) || (inputFormats.size() == 0)));
    401         }
    402 
    403         if (checkNoHighRes) {
    404             for (int format : recommendedOutputFormats) {
    405                 Set<Size> highResSizes = config.getHighResolutionOutputSizes(format);
    406                 assertTrue(String.format("Recommended configuration should not include any " +
    407                         "high resolution sizes, which cannot operate at full " +
    408                         "BURST_CAPTURE rate for ID: %s", id),
    409                         ((highResSizes == null) || (highResSizes.size() == 0)));
    410             }
    411         }
    412 
    413         if (checkNoHighSpeed) {
    414             Set<Size> highSpeedSizes = config.getHighSpeedVideoSizes();
    415             assertTrue(String.format("Recommended configuration must not include any high " +
    416                     "speed configurations for ID: %s", id),
    417                     ((highSpeedSizes == null) || (highSpeedSizes.size() == 0)));
    418         }
    419 
    420         int[] exhaustiveOutputFormats = fullConfig.getOutputFormats();
    421         for (Integer formatInteger : recommendedOutputFormats) {
    422             int format = formatInteger.intValue();
    423             assertArrayContains(String.format("Unsupported recommended output format: %d for " +
    424                     "ID: %s ", format, id), exhaustiveOutputFormats, format);
    425             Set<Size> recommendedSizes = config.getOutputSizes(format);
    426 
    427             switch (format) {
    428                 case ImageFormat.PRIVATE:
    429                     if (checkNoPrivate) {
    430                         fail(String.format("Recommended configuration must not include " +
    431                                 "PRIVATE format entries for ID: %s", id));
    432                     }
    433 
    434                     Set<Size> classOutputSizes = config.getOutputSizes(ImageReader.class);
    435                     assertCollectionContainsAnyOf(String.format("Recommended output sizes for " +
    436                             "ImageReader class don't match the output sizes for the " +
    437                             "corresponding format for ID: %s", id), classOutputSizes,
    438                             recommendedSizes);
    439                     break;
    440                 case ImageFormat.DEPTH16:
    441                 case ImageFormat.DEPTH_POINT_CLOUD:
    442                     if (checkNoDepth) {
    443                         fail(String.format("Recommended configuration must not include any DEPTH " +
    444                                 "formats for ID: %s", id));
    445                     }
    446                     break;
    447                 default:
    448             }
    449             Size [] exhaustiveSizes = fullConfig.getOutputSizes(format);
    450             for (Size sz : recommendedSizes) {
    451                 assertArrayContains(String.format("Unsupported recommended size %s for " +
    452                         "format: %d for ID: %s", sz.toString(), format, id),
    453                         exhaustiveSizes, sz);
    454 
    455                 long recommendedMinDuration = config.getOutputMinFrameDuration(format, sz);
    456                 long availableMinDuration = fullConfig.getOutputMinFrameDuration(format, sz);
    457                 assertTrue(String.format("Recommended minimum frame duration %d for size " +
    458                         "%s format: %d doesn't match with currently available minimum" +
    459                         " frame duration of %d for ID: %s", recommendedMinDuration,
    460                         sz.toString(), format, availableMinDuration, id),
    461                         (recommendedMinDuration == availableMinDuration));
    462                 long recommendedStallDuration = config.getOutputStallDuration(format, sz);
    463                 long availableStallDuration = fullConfig.getOutputStallDuration(format, sz);
    464                 assertTrue(String.format("Recommended stall duration %d for size %s" +
    465                         " format: %d doesn't match with currently available stall " +
    466                         "duration of %d for ID: %s", recommendedStallDuration,
    467                         sz.toString(), format, availableStallDuration, id),
    468                         (recommendedStallDuration == availableStallDuration));
    469 
    470                 ImageReader reader = ImageReader.newInstance(sz.getWidth(), sz.getHeight(), format,
    471                         /*maxImages*/1);
    472                 Surface readerSurface = reader.getSurface();
    473                 assertTrue(String.format("ImageReader surface using format %d and size %s is not" +
    474                         " supported for ID: %s", format, sz.toString(), id),
    475                         config.isOutputSupportedFor(readerSurface));
    476                 if (format == ImageFormat.PRIVATE) {
    477                     long classMinDuration = config.getOutputMinFrameDuration(ImageReader.class, sz);
    478                     assertTrue(String.format("Recommended minimum frame duration %d for size " +
    479                             "%s format: %d doesn't match with the duration %d for " +
    480                             "ImageReader class of the same size", recommendedMinDuration,
    481                             sz.toString(), format, classMinDuration),
    482                             classMinDuration == recommendedMinDuration);
    483                     long classStallDuration = config.getOutputStallDuration(ImageReader.class, sz);
    484                     assertTrue(String.format("Recommended stall duration %d for size " +
    485                             "%s format: %d doesn't match with the stall duration %d for " +
    486                             "ImageReader class of the same size", recommendedStallDuration,
    487                             sz.toString(), format, classStallDuration),
    488                             classStallDuration == recommendedStallDuration);
    489                 }
    490             }
    491         }
    492     }
    493 
    494     private void verifyRecommendedPreviewConfiguration(String cameraId, CameraCharacteristics c,
    495             RecommendedStreamConfigurationMap previewConfig) {
    496         verifyCommonRecommendedConfiguration(cameraId, c, previewConfig, /*checkNoInput*/ true,
    497                 /*checkNoHighRes*/ true, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
    498                 /*checkNoDepth*/ true);
    499 
    500         Set<Integer> outputFormats = previewConfig.getOutputFormats();
    501         assertTrue(String.format("No valid YUV_420_888 and PRIVATE preview " +
    502                 "formats found in recommended preview configuration for ID: %s", cameraId),
    503                 outputFormats.containsAll(Arrays.asList(new Integer(ImageFormat.YUV_420_888),
    504                         new Integer(ImageFormat.PRIVATE))));
    505     }
    506 
    507     private void verifyRecommendedVideoConfiguration(String cameraId, CameraCharacteristics c,
    508             RecommendedStreamConfigurationMap videoConfig) {
    509         verifyCommonRecommendedConfiguration(cameraId, c, videoConfig, /*checkNoInput*/ true,
    510                 /*checkNoHighRes*/ true, /*checkNoHighSpeed*/ false, /*checkNoPrivate*/false,
    511                 /*checkNoDepth*/ true);
    512 
    513         Set<Size> highSpeedSizes = videoConfig.getHighSpeedVideoSizes();
    514         StreamConfigurationMap fullConfig = c.get(
    515                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    516         assertNotNull("No stream configuration map found!", fullConfig);
    517         Size [] availableHighSpeedSizes = fullConfig.getHighSpeedVideoSizes();
    518         if ((highSpeedSizes != null) && (highSpeedSizes.size() > 0)) {
    519             for (Size sz : highSpeedSizes) {
    520                 assertArrayContains(String.format("Recommended video configuration includes " +
    521                         "unsupported high speed configuration with size %s for ID: %s",
    522                         sz.toString(), cameraId), availableHighSpeedSizes, sz);
    523                 Set<Range<Integer>>  highSpeedFpsRanges =
    524                     videoConfig.getHighSpeedVideoFpsRangesFor(sz);
    525                 Range<Integer> [] availableHighSpeedFpsRanges =
    526                     fullConfig.getHighSpeedVideoFpsRangesFor(sz);
    527                 for (Range<Integer> fpsRange : highSpeedFpsRanges) {
    528                     assertArrayContains(String.format("Recommended video configuration includes " +
    529                             "unsupported high speed fps range [%d %d] for ID: %s",
    530                             fpsRange.getLower().intValue(), fpsRange.getUpper().intValue(),
    531                             cameraId), availableHighSpeedFpsRanges, fpsRange);
    532                 }
    533             }
    534         }
    535 
    536         final int[] profileList = {
    537             CamcorderProfile.QUALITY_2160P,
    538             CamcorderProfile.QUALITY_1080P,
    539             CamcorderProfile.QUALITY_480P,
    540             CamcorderProfile.QUALITY_720P,
    541             CamcorderProfile.QUALITY_CIF,
    542             CamcorderProfile.QUALITY_HIGH,
    543             CamcorderProfile.QUALITY_LOW,
    544             CamcorderProfile.QUALITY_QCIF,
    545             CamcorderProfile.QUALITY_QVGA,
    546         };
    547         Set<Size> privateSizeSet = videoConfig.getOutputSizes(ImageFormat.PRIVATE);
    548         for (int profile : profileList) {
    549             int idx = Integer.valueOf(cameraId);
    550             if (CamcorderProfile.hasProfile(idx, profile)) {
    551                 CamcorderProfile videoProfile = CamcorderProfile.get(idx, profile);
    552                 Size profileSize  = new Size(videoProfile.videoFrameWidth,
    553                         videoProfile.videoFrameHeight);
    554                 assertCollectionContainsAnyOf(String.format("Recommended video configuration " +
    555                         "doesn't include supported video profile size %s with Private format " +
    556                         "for ID: %s", profileSize.toString(), cameraId), privateSizeSet,
    557                         Arrays.asList(profileSize));
    558             }
    559         }
    560     }
    561 
    562     private Pair<Boolean, Size> isSizeWithinSensorMargin(Size sz, Size sensorSize) {
    563         final float SIZE_ERROR_MARGIN = 0.03f;
    564         float croppedWidth = (float)sensorSize.getWidth();
    565         float croppedHeight = (float)sensorSize.getHeight();
    566         float sensorAspectRatio = (float)sensorSize.getWidth() / (float)sensorSize.getHeight();
    567         float maxAspectRatio = (float)sz.getWidth() / (float)sz.getHeight();
    568         if (sensorAspectRatio < maxAspectRatio) {
    569             croppedHeight = (float)sensorSize.getWidth() / maxAspectRatio;
    570         } else if (sensorAspectRatio > maxAspectRatio) {
    571             croppedWidth = (float)sensorSize.getHeight() * maxAspectRatio;
    572         }
    573         Size croppedSensorSize = new Size((int)croppedWidth, (int)croppedHeight);
    574 
    575         Boolean match = new Boolean(
    576             (sz.getWidth() <= croppedSensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
    577              sz.getWidth() >= croppedSensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
    578              sz.getHeight() <= croppedSensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
    579              sz.getHeight() >= croppedSensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN)));
    580 
    581         return Pair.create(match, croppedSensorSize);
    582     }
    583 
    584     private void verifyRecommendedSnapshotConfiguration(String cameraId, CameraCharacteristics c,
    585             RecommendedStreamConfigurationMap snapshotConfig) {
    586         verifyCommonRecommendedConfiguration(cameraId, c, snapshotConfig, /*checkNoInput*/ true,
    587                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/false,
    588                 /*checkNoDepth*/ false);
    589         Rect activeRect = CameraTestUtils.getValueNotNull(
    590                 c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    591         Size arraySize = new Size(activeRect.width(), activeRect.height());
    592 
    593         Set<Size> snapshotSizeSet = snapshotConfig.getOutputSizes(ImageFormat.JPEG);
    594         Size[] snapshotSizes = new Size[snapshotSizeSet.size()];
    595         snapshotSizes = snapshotSizeSet.toArray(snapshotSizes);
    596         Size maxJpegSize = CameraTestUtils.getMaxSize(snapshotSizes);
    597         assertTrue(String.format("Maximum recommended Jpeg size %s should be within 3 percent " +
    598                 "of the area of the advertised array size %s for ID: %s",
    599                 maxJpegSize.toString(), arraySize.toString(), cameraId),
    600                 isSizeWithinSensorMargin(maxJpegSize, arraySize).first.booleanValue());
    601     }
    602 
    603     private void verifyRecommendedVideoSnapshotConfiguration(String cameraId,
    604             CameraCharacteristics c,
    605             RecommendedStreamConfigurationMap videoSnapshotConfig,
    606             RecommendedStreamConfigurationMap videoConfig) {
    607         verifyCommonRecommendedConfiguration(cameraId, c, videoSnapshotConfig,
    608                 /*checkNoInput*/ true, /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true,
    609                 /*checkNoPrivate*/ true, /*checkNoDepth*/ true);
    610 
    611         Set<Integer> outputFormats = videoSnapshotConfig.getOutputFormats();
    612         assertCollectionContainsAnyOf(String.format("No valid JPEG format found " +
    613                 "in recommended video snapshot configuration for ID: %s", cameraId),
    614                 outputFormats, Arrays.asList(new Integer(ImageFormat.JPEG)));
    615         assertTrue(String.format("Recommended video snapshot configuration must only advertise " +
    616                 "JPEG format for ID: %s", cameraId), outputFormats.size() == 1);
    617 
    618         Set<Size> privateVideoSizeSet = videoConfig.getOutputSizes(ImageFormat.PRIVATE);
    619         Size[] privateVideoSizes = new Size[privateVideoSizeSet.size()];
    620         privateVideoSizes = privateVideoSizeSet.toArray(privateVideoSizes);
    621         Size maxVideoSize = CameraTestUtils.getMaxSize(privateVideoSizes);
    622         Set<Size> outputSizes = videoSnapshotConfig.getOutputSizes(ImageFormat.JPEG);
    623         assertCollectionContainsAnyOf(String.format("The maximum recommended video size %s " +
    624                 "should be present in the recommended video snapshot configurations for ID: %s",
    625                 maxVideoSize.toString(), cameraId), outputSizes, Arrays.asList(maxVideoSize));
    626     }
    627 
    628     private void verifyRecommendedRawConfiguration(String cameraId,
    629             CameraCharacteristics c, RecommendedStreamConfigurationMap rawConfig) {
    630         verifyCommonRecommendedConfiguration(cameraId, c, rawConfig, /*checkNoInput*/ true,
    631                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ true,
    632                 /*checkNoDepth*/ true);
    633 
    634         Set<Integer> outputFormats = rawConfig.getOutputFormats();
    635         for (Integer outputFormatInteger : outputFormats) {
    636             int outputFormat = outputFormatInteger.intValue();
    637             switch (outputFormat) {
    638                 case ImageFormat.RAW10:
    639                 case ImageFormat.RAW12:
    640                 case ImageFormat.RAW_PRIVATE:
    641                 case ImageFormat.RAW_SENSOR:
    642                     break;
    643                 default:
    644                     fail(String.format("Recommended raw configuration map must not contain " +
    645                             " non-RAW formats like: %d for ID: %s", outputFormat, cameraId));
    646 
    647             }
    648         }
    649     }
    650 
    651     private void verifyRecommendedZSLConfiguration(String cameraId, CameraCharacteristics c,
    652             RecommendedStreamConfigurationMap zslConfig) {
    653         verifyCommonRecommendedConfiguration(cameraId, c, zslConfig, /*checkNoInput*/ false,
    654                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
    655                 /*checkNoDepth*/ false);
    656 
    657         StreamConfigurationMap fullConfig =
    658             c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    659         assertNotNull(String.format("No stream configuration map found for ID: %s!", cameraId),
    660                 fullConfig);
    661         Set<Integer> inputFormats = zslConfig.getInputFormats();
    662         int [] availableInputFormats = fullConfig.getInputFormats();
    663         for (Integer inputFormatInteger : inputFormats) {
    664             int inputFormat = inputFormatInteger.intValue();
    665             assertArrayContains(String.format("Recommended ZSL configuration includes " +
    666                     "unsupported input format %d for ID: %s", inputFormat, cameraId),
    667                     availableInputFormats, inputFormat);
    668 
    669             Set<Size> inputSizes = zslConfig.getInputSizes(inputFormat);
    670             Size [] availableInputSizes = fullConfig.getInputSizes(inputFormat);
    671             assertTrue(String.format("Recommended ZSL configuration input format %d includes " +
    672                     "invalid input sizes for ID: %s", inputFormat, cameraId),
    673                     ((inputSizes != null) && (inputSizes.size() > 0)));
    674             for (Size inputSize : inputSizes) {
    675                 assertArrayContains(String.format("Recommended ZSL configuration includes " +
    676                         "unsupported input format %d with size %s ID: %s", inputFormat,
    677                         inputSize.toString(), cameraId), availableInputSizes, inputSize);
    678             }
    679             Set<Integer> validOutputFormats = zslConfig.getValidOutputFormatsForInput(inputFormat);
    680             int [] availableValidOutputFormats = fullConfig.getValidOutputFormatsForInput(
    681                     inputFormat);
    682             for (Integer outputFormatInteger : validOutputFormats) {
    683                 int outputFormat = outputFormatInteger.intValue();
    684                 assertArrayContains(String.format("Recommended ZSL configuration includes " +
    685                         "unsupported output format %d for input %s ID: %s", outputFormat,
    686                         inputFormat, cameraId), availableValidOutputFormats, outputFormat);
    687             }
    688         }
    689     }
    690 
    691     private void checkFormatLatency(int format, long latencyThresholdMs,
    692             RecommendedStreamConfigurationMap configMap) throws Exception {
    693         Set<Size> availableSizes = configMap.getOutputSizes(format);
    694         assertNotNull(String.format("No available sizes for output format: %d", format),
    695                 availableSizes);
    696 
    697         ImageReader previewReader = null;
    698         long threshold = (long) (latencyThresholdMs * LATENCY_TOLERANCE_FACTOR);
    699         // for each resolution, check that the end-to-end latency doesn't exceed the given threshold
    700         for (Size sz : availableSizes) {
    701             try {
    702                 // Create ImageReaders, capture session and requests
    703                 final ImageReader.OnImageAvailableListener mockListener = mock(
    704                         ImageReader.OnImageAvailableListener.class);
    705                 createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mockListener);
    706                 Size previewSize = mOrderedPreviewSizes.get(0);
    707                 previewReader = createImageReader(previewSize, ImageFormat.YUV_420_888,
    708                         MAX_NUM_IMAGES, new CameraTestUtils.ImageDropperListener());
    709                 Surface previewSurface = previewReader.getSurface();
    710                 List<Surface> surfaces = new ArrayList<Surface>();
    711                 surfaces.add(previewSurface);
    712                 surfaces.add(mReaderSurface);
    713                 createSession(surfaces);
    714                 CaptureRequest.Builder captureBuilder =
    715                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    716                 captureBuilder.addTarget(previewSurface);
    717                 CaptureRequest request = captureBuilder.build();
    718 
    719                 // Let preview run for a while
    720                 startCapture(request, /*repeating*/ true, new SimpleCaptureCallback(), mHandler);
    721                 Thread.sleep(PREVIEW_RUN_MS);
    722 
    723                 // Start capture.
    724                 captureBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    725                 captureBuilder.addTarget(mReaderSurface);
    726                 request = captureBuilder.build();
    727 
    728                 for (int i = 0; i < MAX_NUM_IMAGES; i++) {
    729                     startCapture(request, /*repeating*/ false, new SimpleCaptureCallback(),
    730                             mHandler);
    731                     verify(mockListener, timeout(threshold).times(1)).onImageAvailable(
    732                             any(ImageReader.class));
    733                     reset(mockListener);
    734                 }
    735 
    736                 // stop capture.
    737                 stopCapture(/*fast*/ false);
    738             } finally {
    739                 closeDefaultImageReader();
    740 
    741                 if (previewReader != null) {
    742                     previewReader.close();
    743                     previewReader = null;
    744                 }
    745             }
    746 
    747         }
    748     }
    749 
    750     private void verifyRecommendedLowLatencyConfiguration(String cameraId, CameraCharacteristics c,
    751             RecommendedStreamConfigurationMap lowLatencyConfig) throws Exception {
    752         verifyCommonRecommendedConfiguration(cameraId, c, lowLatencyConfig, /*checkNoInput*/ true,
    753                 /*checkNoHighRes*/ false, /*checkNoHighSpeed*/ true, /*checkNoPrivate*/ false,
    754                 /*checkNoDepth*/ true);
    755 
    756         try {
    757             openDevice(cameraId);
    758 
    759             Set<Integer> formats = lowLatencyConfig.getOutputFormats();
    760             for (Integer format : formats) {
    761                 checkFormatLatency(format.intValue(), LOW_LATENCY_THRESHOLD_MS, lowLatencyConfig);
    762             }
    763         } finally {
    764             closeDevice(cameraId);
    765         }
    766 
    767     }
    768 
    769     public void testRecommendedStreamConfigurations() throws Exception {
    770         int counter = 0;
    771         for (CameraCharacteristics c : mCharacteristics) {
    772             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    773             assertNotNull("android.request.availableCapabilities must never be null",
    774                     actualCapabilities);
    775 
    776             if (!arrayContains(actualCapabilities, BC)) {
    777                 Log.i(TAG, "Camera " + mAllCameraIds[counter] +
    778                         ": BACKWARD_COMPATIBLE capability not supported, skipping test");
    779                 continue;
    780             }
    781 
    782             try {
    783                 RecommendedStreamConfigurationMap map = c.getRecommendedStreamConfigurationMap(
    784                         RecommendedStreamConfigurationMap.USECASE_PREVIEW - 1);
    785                 fail("Recommended configuration map shouldn't be available for invalid " +
    786                         "use case!");
    787             } catch (IllegalArgumentException e) {
    788                 //Expected continue
    789             }
    790 
    791             try {
    792                 RecommendedStreamConfigurationMap map = c.getRecommendedStreamConfigurationMap(
    793                         RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT + 1);
    794                 fail("Recommended configuration map shouldn't be available for invalid " +
    795                         "use case!");
    796             } catch (IllegalArgumentException e) {
    797                 //Expected continue
    798             }
    799 
    800             RecommendedStreamConfigurationMap previewConfig =
    801                     c.getRecommendedStreamConfigurationMap(
    802                     RecommendedStreamConfigurationMap.USECASE_PREVIEW);
    803             RecommendedStreamConfigurationMap videoRecordingConfig =
    804                     c.getRecommendedStreamConfigurationMap(
    805                     RecommendedStreamConfigurationMap.USECASE_RECORD);
    806             RecommendedStreamConfigurationMap videoSnapshotConfig =
    807                     c.getRecommendedStreamConfigurationMap(
    808                     RecommendedStreamConfigurationMap.USECASE_VIDEO_SNAPSHOT);
    809             RecommendedStreamConfigurationMap snapshotConfig =
    810                     c.getRecommendedStreamConfigurationMap(
    811                     RecommendedStreamConfigurationMap.USECASE_SNAPSHOT);
    812             RecommendedStreamConfigurationMap rawConfig =
    813                     c.getRecommendedStreamConfigurationMap(
    814                     RecommendedStreamConfigurationMap.USECASE_RAW);
    815             RecommendedStreamConfigurationMap zslConfig =
    816                     c.getRecommendedStreamConfigurationMap(
    817                     RecommendedStreamConfigurationMap.USECASE_ZSL);
    818             RecommendedStreamConfigurationMap lowLatencyConfig =
    819                     c.getRecommendedStreamConfigurationMap(
    820                     RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT);
    821             if ((previewConfig == null) && (videoRecordingConfig == null) &&
    822                     (videoSnapshotConfig == null) && (snapshotConfig == null) &&
    823                     (rawConfig == null) && (zslConfig == null) && (lowLatencyConfig == null)) {
    824                 Log.i(TAG, "Camera " + mAllCameraIds[counter] +
    825                         " doesn't support recommended configurations, skipping test");
    826                 continue;
    827             }
    828 
    829             assertNotNull(String.format("Mandatory recommended preview configuration map not " +
    830                     "found for: ID %s", mAllCameraIds[counter]), previewConfig);
    831             verifyRecommendedPreviewConfiguration(mAllCameraIds[counter], c, previewConfig);
    832 
    833             assertNotNull(String.format("Mandatory recommended video recording configuration map " +
    834                     "not found for: ID %s", mAllCameraIds[counter]), videoRecordingConfig);
    835             verifyRecommendedVideoConfiguration(mAllCameraIds[counter], c, videoRecordingConfig);
    836 
    837             assertNotNull(String.format("Mandatory recommended video snapshot configuration map " +
    838                     "not found for: ID %s", mAllCameraIds[counter]), videoSnapshotConfig);
    839             verifyRecommendedVideoSnapshotConfiguration(mAllCameraIds[counter], c, videoSnapshotConfig,
    840                     videoRecordingConfig);
    841 
    842             assertNotNull(String.format("Mandatory recommended snapshot configuration map not " +
    843                     "found for: ID %s", mAllCameraIds[counter]), snapshotConfig);
    844             verifyRecommendedSnapshotConfiguration(mAllCameraIds[counter], c, snapshotConfig);
    845 
    846             if (arrayContains(actualCapabilities, RAW)) {
    847                 assertNotNull(String.format("Mandatory recommended raw configuration map not " +
    848                         "found for: ID %s", mAllCameraIds[counter]), rawConfig);
    849                 verifyRecommendedRawConfiguration(mAllCameraIds[counter], c, rawConfig);
    850             }
    851 
    852             if (arrayContains(actualCapabilities, OPAQUE_REPROCESS) ||
    853                     arrayContains(actualCapabilities, YUV_REPROCESS)) {
    854                 assertNotNull(String.format("Mandatory recommended ZSL configuration map not " +
    855                         "found for: ID %s", mAllCameraIds[counter]), zslConfig);
    856                 verifyRecommendedZSLConfiguration(mAllCameraIds[counter], c, zslConfig);
    857             }
    858 
    859             if (lowLatencyConfig != null) {
    860                 verifyRecommendedLowLatencyConfiguration(mAllCameraIds[counter], c, lowLatencyConfig);
    861             }
    862 
    863             counter++;
    864         }
    865     }
    866 
    867     /**
    868      * Test {@link CameraCharacteristics#getKeys}
    869      */
    870     public void testKeys() {
    871         int counter = 0;
    872         for (CameraCharacteristics c : mCharacteristics) {
    873             mCollector.setCameraId(mAllCameraIds[counter]);
    874 
    875             if (VERBOSE) {
    876                 Log.v(TAG, "testKeys - testing characteristics for camera " + mAllCameraIds[counter]);
    877             }
    878 
    879             List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
    880             assertNotNull("Camera characteristics keys must not be null", allKeys);
    881             assertFalse("Camera characteristics keys must have at least 1 key",
    882                     allKeys.isEmpty());
    883 
    884             for (CameraCharacteristics.Key<?> key : allKeys) {
    885                 assertKeyPrefixValid(key.getName());
    886 
    887                 // All characteristics keys listed must never be null
    888                 mCollector.expectKeyValueNotNull(c, key);
    889 
    890                 // TODO: add a check that key must not be @hide
    891             }
    892 
    893             /*
    894              * List of keys that must be present in camera characteristics (not null).
    895              *
    896              * Keys for LIMITED, FULL devices might be available despite lacking either
    897              * the hardware level or the capability. This is *OK*. This only lists the
    898              * *minimal* requirements for a key to be listed.
    899              *
    900              * LEGACY devices are a bit special since they map to api1 devices, so we know
    901              * for a fact most keys are going to be illegal there so they should never be
    902              * available.
    903              *
    904              * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to
    905              * do the actual checking.
    906              */
    907             {
    908                 //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
    909                 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES     , OPT      ,   BC                   );
    910                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES                         , OPT      ,   BC                   );
    911                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , OPT      ,   BC                   );
    912                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , OPT      ,   BC                   );
    913                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , OPT      ,   BC                   );
    914                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , OPT      ,   BC                   );
    915                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , OPT      ,   BC                   );
    916                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE                       , OPT      ,   BC                   );
    917                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , OPT      ,   BC                   );
    918                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , OPT      ,   BC                   );
    919                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , OPT      ,   BC                   );
    920                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , OPT      ,   BC                   );
    921                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , OPT      ,   BC                   );
    922                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE                      , OPT      ,   BC                   );
    923                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , OPT      ,   BC                   );
    924                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , OPT      ,   BC                   );
    925                 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , OPT      ,   BC                   );
    926                 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
    927                 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , OPT      ,   BC                   );
    928                 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES             , OPT      ,   RAW                  );
    929                 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , OPT      ,   BC                   );
    930                 expectKeyAvailable(c, CameraCharacteristics.INFO_VERSION                                    , OPT      ,   NONE                 );
    931                 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , OPT      ,   BC                   );
    932                 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , OPT      ,   BC                   );
    933                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
    934                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
    935                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   BC                   );
    936                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
    937                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   BC                   );
    938                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   BC                   );
    939                 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT      ,   BC                   );
    940                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , OPT      ,   BC                   );
    941                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS                   , OPT      ,   YUV_REPROCESS, OPAQUE_REPROCESS);
    942                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   CONSTRAINED_HIGH_SPEED);
    943                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , OPT      ,   BC                   );
    944                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , OPT      ,   BC                   );
    945                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , OPT      ,   BC                   );
    946                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , OPT      ,   BC                   );
    947                 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , OPT      ,   BC                   );
    948                 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , OPT      ,   BC                   );
    949                 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , OPT      ,   BC                   );
    950                 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , OPT      ,   BC                   );
    951                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   RAW                  );
    952                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , OPT      ,   BC, RAW              );
    953                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
    954                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
    955                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
    956                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , OPT      ,   BC                   );
    957                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
    958                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL                         , OPT      ,   RAW                  );
    959                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE                    , OPT      ,   BC                   );
    960                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
    961                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , OPT      ,   BC                   );
    962                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
    963                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , OPT      ,   BC                   );
    964                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
    965                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED  ,   RAW                  );
    966                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , OPT      ,   BC                   );
    967                 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , OPT      ,   BC                   );
    968                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_POSTPROC      );
    969                 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_POSTPROC      );
    970 
    971                 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
    972 
    973                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
    974             }
    975 
    976             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    977             assertNotNull("android.request.availableCapabilities must never be null",
    978                     actualCapabilities);
    979             boolean isMonochrome = arrayContains(actualCapabilities,
    980                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
    981             if (!isMonochrome) {
    982                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
    983                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
    984                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
    985                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
    986 
    987 
    988                 // Only check for these if the second reference illuminant is included
    989                 if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
    990                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
    991                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
    992                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
    993                     expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
    994                 }
    995             }
    996 
    997             // Required key if any of RAW format output is supported
    998             StreamConfigurationMap config =
    999                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1000             assertNotNull(String.format("No stream configuration map found for: ID %s",
   1001                     mAllCameraIds[counter]), config);
   1002             if (config.isOutputSupportedFor(ImageFormat.RAW_SENSOR) ||
   1003                     config.isOutputSupportedFor(ImageFormat.RAW10)  ||
   1004                     config.isOutputSupportedFor(ImageFormat.RAW12)  ||
   1005                     config.isOutputSupportedFor(ImageFormat.RAW_PRIVATE)) {
   1006                 expectKeyAvailable(c,
   1007                         CameraCharacteristics.CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, OPT, BC);
   1008             }
   1009 
   1010             // External Camera exceptional keys
   1011             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
   1012             boolean isExternalCamera = (hwLevel ==
   1013                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
   1014             if (!isExternalCamera) {
   1015                 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , OPT      ,   BC                   );
   1016                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , OPT      ,   BC                   );
   1017                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , OPT      ,   BC                   );
   1018             }
   1019 
   1020 
   1021             // Verify version is a short text string.
   1022             if (allKeys.contains(CameraCharacteristics.INFO_VERSION)) {
   1023                 final String TEXT_REGEX = "[\\p{Alnum}\\p{Punct}\\p{Space}]*";
   1024                 final int MAX_VERSION_LENGTH = 256;
   1025 
   1026                 String version = c.get(CameraCharacteristics.INFO_VERSION);
   1027                 mCollector.expectTrue("Version contains non-text characters: " + version,
   1028                         version.matches(TEXT_REGEX));
   1029                 mCollector.expectLessOrEqual("Version too long: " + version, MAX_VERSION_LENGTH,
   1030                         version.length());
   1031             }
   1032 
   1033             counter++;
   1034         }
   1035     }
   1036 
   1037     /**
   1038      * Test values for static metadata used by the RAW capability.
   1039      */
   1040     public void testStaticRawCharacteristics() {
   1041         int counter = 0;
   1042         for (CameraCharacteristics c : mCharacteristics) {
   1043             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1044             assertNotNull("android.request.availableCapabilities must never be null",
   1045                     actualCapabilities);
   1046             if (!arrayContains(actualCapabilities,
   1047                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
   1048                 Log.i(TAG, "RAW capability is not supported in camera " + counter++ +
   1049                         ". Skip the test.");
   1050                 continue;
   1051             }
   1052 
   1053             Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
   1054             if (actualHwLevel != null && actualHwLevel == FULL) {
   1055                 mCollector.expectKeyValueContains(c,
   1056                         CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES,
   1057                         CameraCharacteristics.HOT_PIXEL_MODE_FAST);
   1058             }
   1059             mCollector.expectKeyValueContains(c,
   1060                     CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false);
   1061             mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL,
   1062                     MIN_ALLOWABLE_WHITELEVEL);
   1063 
   1064 
   1065             boolean isMonochrome = arrayContains(actualCapabilities,
   1066                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME);
   1067             if (!isMonochrome) {
   1068                 mCollector.expectKeyValueIsIn(c,
   1069                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
   1070                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB,
   1071                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG,
   1072                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG,
   1073                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR);
   1074                 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
   1075 
   1076                 mCollector.expectKeyValueInRange(c,
   1077                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1,
   1078                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
   1079                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
   1080                 // Only check the range if the second reference illuminant is avaliable
   1081                 if (c.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2) != null) {
   1082                         mCollector.expectKeyValueInRange(c,
   1083                         CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2,
   1084                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT,
   1085                         (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN);
   1086                 }
   1087 
   1088                 Rational[] zeroes = new Rational[9];
   1089                 Arrays.fill(zeroes, Rational.ZERO);
   1090 
   1091                 ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes);
   1092                 mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed,
   1093                         c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
   1094                 mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed,
   1095                         c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
   1096                 mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.",
   1097                         zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
   1098                 mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.",
   1099                         zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
   1100                 mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.",
   1101                         zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
   1102                 mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.",
   1103                         zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
   1104             } else {
   1105                 mCollector.expectKeyValueIsIn(c,
   1106                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
   1107                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO,
   1108                         CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR);
   1109                 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet.
   1110             }
   1111 
   1112             BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
   1113                     CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
   1114             if (blackLevel != null) {
   1115                 String blackLevelPatternString = blackLevel.toString();
   1116                 if (VERBOSE) {
   1117                     Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
   1118                 }
   1119                 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
   1120                 blackLevel.copyTo(blackLevelPattern, /*offset*/0);
   1121                 if (isMonochrome) {
   1122                     for (int index = 1; index < BlackLevelPattern.COUNT; index++) {
   1123                         mCollector.expectEquals(
   1124                                 "Monochrome camera 2x2 channels blacklevel value must be the same.",
   1125                                 blackLevelPattern[index], blackLevelPattern[0]);
   1126                     }
   1127                 }
   1128 
   1129                 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
   1130                 if (whitelevel != null) {
   1131                     mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0,
   1132                             whitelevel);
   1133                 } else {
   1134                     mCollector.addMessage(
   1135                             "No WhiteLevel available, cannot check BlackLevelPattern range.");
   1136                 }
   1137             }
   1138 
   1139             // TODO: profileHueSatMap, and profileToneCurve aren't supported yet.
   1140             counter++;
   1141         }
   1142     }
   1143 
   1144     /**
   1145      * Test values for the available session keys.
   1146      */
   1147     public void testStaticSessionKeys() throws Exception {
   1148         for (CameraCharacteristics c : mCharacteristics) {
   1149             List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
   1150             if (availableSessionKeys == null) {
   1151                 continue;
   1152             }
   1153             List<CaptureRequest.Key<?>> availableRequestKeys = c.getAvailableCaptureRequestKeys();
   1154 
   1155             //Every session key should be part of the available request keys
   1156             for (CaptureRequest.Key<?> key : availableSessionKeys) {
   1157                 assertTrue("Session key:" + key.getName() + " not present in the available capture "
   1158                         + "request keys!", availableRequestKeys.contains(key));
   1159             }
   1160         }
   1161     }
   1162 
   1163     /**
   1164      * Test values for static metadata used by the BURST capability.
   1165      */
   1166     public void testStaticBurstCharacteristics() throws Exception {
   1167         int counter = 0;
   1168         for (CameraCharacteristics c : mCharacteristics) {
   1169             int[] actualCapabilities = CameraTestUtils.getValueNotNull(
   1170                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1171 
   1172             // Check if the burst capability is defined
   1173             boolean haveBurstCapability = arrayContains(actualCapabilities,
   1174                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
   1175             boolean haveBC = arrayContains(actualCapabilities,
   1176                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
   1177 
   1178             if(haveBurstCapability && !haveBC) {
   1179                 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined");
   1180             }
   1181 
   1182             if (!haveBC) continue;
   1183 
   1184             StreamConfigurationMap config =
   1185                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1186             assertNotNull(String.format("No stream configuration map found for: ID %s",
   1187                     mAllCameraIds[counter]), config);
   1188             Rect activeRect = CameraTestUtils.getValueNotNull(
   1189                     c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
   1190             Size sensorSize = new Size(activeRect.width(), activeRect.height());
   1191 
   1192             // Ensure that max YUV size matches max JPEG size
   1193             Size maxYuvSize = CameraTestUtils.getMaxSize(
   1194                     config.getOutputSizes(ImageFormat.YUV_420_888));
   1195             Size maxFastYuvSize = maxYuvSize;
   1196 
   1197             Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
   1198             Size maxSlowYuvSizeLessThan24M = null;
   1199             if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) {
   1200                 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
   1201                 final int SIZE_24MP_BOUND = 24000000;
   1202                 maxSlowYuvSizeLessThan24M =
   1203                         CameraTestUtils.getMaxSizeWithBound(slowYuvSizes, SIZE_24MP_BOUND);
   1204                 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
   1205             }
   1206 
   1207             Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat(
   1208                     ImageFormat.JPEG, mAllCameraIds[counter], mCameraManager));
   1209 
   1210             boolean haveMaxYuv = maxYuvSize != null ?
   1211                 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
   1212                         maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
   1213 
   1214             Pair<Boolean, Size> maxYuvMatchSensorPair = isSizeWithinSensorMargin(maxYuvSize,
   1215                     sensorSize);
   1216 
   1217             // No need to do null check since framework will generate the key if HAL don't supply
   1218             boolean haveAeLock = CameraTestUtils.getValueNotNull(
   1219                     c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
   1220             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
   1221                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
   1222 
   1223             // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
   1224 
   1225             long maxFastYuvRate =
   1226                     config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
   1227             final long MIN_8MP_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
   1228             boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
   1229 
   1230             final int SIZE_8MP_BOUND = 8000000;
   1231             boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
   1232                     SIZE_8MP_BOUND;
   1233 
   1234             // Ensure that max YUV output smaller than 24MP is fast enough
   1235             // - needs to be at least 10 fps
   1236             final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
   1237             long maxYuvRate = maxFastYuvRate;
   1238             if (maxSlowYuvSizeLessThan24M != null) {
   1239                 maxYuvRate = config.getOutputMinFrameDuration(
   1240                         ImageFormat.YUV_420_888, maxSlowYuvSizeLessThan24M);
   1241             }
   1242             boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
   1243 
   1244             // Ensure that there's an FPS range that's fast enough to capture at above
   1245             // minFrameDuration, for full-auto bursts at the fast resolutions
   1246             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
   1247                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
   1248             float minYuvFps = 1.f / maxFastYuvRate;
   1249 
   1250             boolean haveFastAeTargetFps = false;
   1251             for (Range<Integer> r : fpsRanges) {
   1252                 if (r.getLower() >= minYuvFps) {
   1253                     haveFastAeTargetFps = true;
   1254                     break;
   1255                 }
   1256             }
   1257 
   1258             // Ensure that maximum sync latency is small enough for fast setting changes, even if
   1259             // it's not quite per-frame
   1260 
   1261             Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
   1262             assertNotNull(String.format("No sync latency declared for ID %s", mAllCameraIds[counter]),
   1263                     maxSyncLatencyValue);
   1264 
   1265             int maxSyncLatency = maxSyncLatencyValue;
   1266             final long MAX_LATENCY_BOUND = 4;
   1267             boolean haveFastSyncLatency =
   1268                 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
   1269 
   1270             if (haveBurstCapability) {
   1271                 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!",
   1272                         slowYuvSizes != null);
   1273                 assertTrue(
   1274                         String.format("BURST-capable camera device %s does not have maximum YUV " +
   1275                                 "size that is at least max JPEG size",
   1276                                 mAllCameraIds[counter]),
   1277                         haveMaxYuv);
   1278                 assertTrue(
   1279                         String.format("BURST-capable camera device %s max-resolution " +
   1280                                 "YUV frame rate is too slow" +
   1281                                 "(%d ns min frame duration reported, less than %d ns expected)",
   1282                                 mAllCameraIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
   1283                         haveMaxYuvRate);
   1284                 assertTrue(
   1285                         String.format("BURST-capable camera device %s >= 8MP YUV output " +
   1286                                 "frame rate is too slow" +
   1287                                 "(%d ns min frame duration reported, less than %d ns expected)",
   1288                                 mAllCameraIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
   1289                         haveFastYuvRate);
   1290                 assertTrue(
   1291                         String.format("BURST-capable camera device %s does not list an AE target " +
   1292                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
   1293                                 mAllCameraIds[counter], minYuvFps),
   1294                         haveFastAeTargetFps);
   1295                 assertTrue(
   1296                         String.format("BURST-capable camera device %s YUV sync latency is too long" +
   1297                                 "(%d frames reported, [0, %d] frames expected)",
   1298                                 mAllCameraIds[counter], maxSyncLatency, MAX_LATENCY_BOUND),
   1299                         haveFastSyncLatency);
   1300                 assertTrue(
   1301                         String.format("BURST-capable camera device %s max YUV size %s should be" +
   1302                                 "close to active array size %s or cropped active array size %s",
   1303                                 mAllCameraIds[counter], maxYuvSize.toString(), sensorSize.toString(),
   1304                                 maxYuvMatchSensorPair.second.toString()),
   1305                         maxYuvMatchSensorPair.first.booleanValue());
   1306                 assertTrue(
   1307                         String.format("BURST-capable camera device %s does not support AE lock",
   1308                                 mAllCameraIds[counter]),
   1309                         haveAeLock);
   1310                 assertTrue(
   1311                         String.format("BURST-capable camera device %s does not support AWB lock",
   1312                                 mAllCameraIds[counter]),
   1313                         haveAwbLock);
   1314             } else {
   1315                 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!",
   1316                         slowYuvSizes == null);
   1317                 assertTrue(
   1318                         String.format("Camera device %s has all the requirements for BURST" +
   1319                                 " capability but does not report it!", mAllCameraIds[counter]),
   1320                         !(haveMaxYuv && haveMaxYuvRate && haveFastYuvRate && haveFastAeTargetFps &&
   1321                                 haveFastSyncLatency && maxYuvMatchSensorPair.first.booleanValue() &&
   1322                                 haveAeLock && haveAwbLock));
   1323             }
   1324 
   1325             counter++;
   1326         }
   1327     }
   1328 
   1329     /**
   1330      * Check reprocessing capabilities.
   1331      */
   1332     public void testReprocessingCharacteristics() {
   1333         int counter = 0;
   1334 
   1335         for (CameraCharacteristics c : mCharacteristics) {
   1336             Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mAllCameraIds[counter]);
   1337 
   1338             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1339             assertNotNull("android.request.availableCapabilities must never be null",
   1340                     capabilities);
   1341             boolean supportYUV = arrayContains(capabilities,
   1342                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
   1343             boolean supportOpaque = arrayContains(capabilities,
   1344                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
   1345             StreamConfigurationMap configs =
   1346                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1347             Integer maxNumInputStreams =
   1348                     c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS);
   1349             int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
   1350             int[] availableNoiseReductionModes = c.get(
   1351                     CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
   1352 
   1353             int[] inputFormats = configs.getInputFormats();
   1354             int[] outputFormats = configs.getOutputFormats();
   1355             boolean isMonochromeWithY8 = arrayContains(capabilities, MONOCHROME)
   1356                     && arrayContains(outputFormats, ImageFormat.Y8);
   1357 
   1358             boolean supportZslEdgeMode = false;
   1359             boolean supportZslNoiseReductionMode = false;
   1360             boolean supportHiQNoiseReductionMode = false;
   1361             boolean supportHiQEdgeMode = false;
   1362 
   1363             if (availableEdgeModes != null) {
   1364                 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
   1365                         contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG);
   1366                 supportHiQEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)).
   1367                         contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY);
   1368             }
   1369 
   1370             if (availableNoiseReductionModes != null) {
   1371                 supportZslNoiseReductionMode = Arrays.asList(
   1372                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
   1373                         CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
   1374                 supportHiQNoiseReductionMode = Arrays.asList(
   1375                         CameraTestUtils.toObject(availableNoiseReductionModes)).contains(
   1376                         CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
   1377             }
   1378 
   1379             if (supportYUV || supportOpaque) {
   1380                 mCollector.expectTrue("Support reprocessing but max number of input stream is " +
   1381                         maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0);
   1382                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " +
   1383                         "not supported", supportZslEdgeMode);
   1384                 mCollector.expectTrue("Support reprocessing but " +
   1385                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported",
   1386                         supportZslNoiseReductionMode);
   1387 
   1388                 // For reprocessing, if we only require OFF and ZSL mode, it will be just like jpeg
   1389                 // encoding. We implicitly require FAST to make reprocessing meaningful, which means
   1390                 // that we also require HIGH_QUALITY.
   1391                 mCollector.expectTrue("Support reprocessing but EDGE_MODE_HIGH_QUALITY is " +
   1392                         "not supported", supportHiQEdgeMode);
   1393                 mCollector.expectTrue("Support reprocessing but " +
   1394                         "NOISE_REDUCTION_MODE_HIGH_QUALITY is not supported",
   1395                         supportHiQNoiseReductionMode);
   1396 
   1397                 // Verify mandatory input formats are supported
   1398                 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing",
   1399                         !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888));
   1400                 mCollector.expectTrue("Y8 input must be supported for YUV reprocessing on " +
   1401                         "MONOCHROME devices with Y8 support", !supportYUV || !isMonochromeWithY8
   1402                         || arrayContains(inputFormats, ImageFormat.Y8));
   1403                 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing",
   1404                         !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE));
   1405 
   1406                 // max capture stall must be reported if one of the reprocessing is supported.
   1407                 final int MAX_ALLOWED_STALL_FRAMES = 4;
   1408                 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL);
   1409                 mCollector.expectTrue("max capture stall must be non-null and no larger than "
   1410                         + MAX_ALLOWED_STALL_FRAMES,
   1411                         maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES);
   1412 
   1413                 for (int input : inputFormats) {
   1414                     // Verify mandatory output formats are supported
   1415                     int[] outputFormatsForInput = configs.getValidOutputFormatsForInput(input);
   1416                     mCollector.expectTrue(
   1417                         "YUV_420_888 output must be supported for reprocessing",
   1418                         input == ImageFormat.Y8
   1419                         || arrayContains(outputFormatsForInput, ImageFormat.YUV_420_888));
   1420                     mCollector.expectTrue(
   1421                         "Y8 output must be supported for reprocessing on MONOCHROME devices with"
   1422                         + " Y8 support", !isMonochromeWithY8 || input == ImageFormat.YUV_420_888
   1423                         || arrayContains(outputFormatsForInput, ImageFormat.Y8));
   1424                     mCollector.expectTrue("JPEG output must be supported for reprocessing",
   1425                             arrayContains(outputFormatsForInput, ImageFormat.JPEG));
   1426 
   1427                     // Verify camera can output the reprocess input formats and sizes.
   1428                     Size[] inputSizes = configs.getInputSizes(input);
   1429                     Size[] outputSizes = configs.getOutputSizes(input);
   1430                     Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
   1431                     mCollector.expectTrue("no input size supported for format " + input,
   1432                             inputSizes.length > 0);
   1433                     mCollector.expectTrue("no output size supported for format " + input,
   1434                             outputSizes.length > 0);
   1435 
   1436                     for (Size inputSize : inputSizes) {
   1437                         mCollector.expectTrue("Camera must be able to output the supported " +
   1438                                 "reprocessing input size",
   1439                                 arrayContains(outputSizes, inputSize) ||
   1440                                 arrayContains(highResOutputSizes, inputSize));
   1441                     }
   1442                 }
   1443             } else {
   1444                 mCollector.expectTrue("Doesn't support reprocessing but report input format: " +
   1445                         Arrays.toString(inputFormats), inputFormats.length == 0);
   1446                 mCollector.expectTrue("Doesn't support reprocessing but max number of input " +
   1447                         "stream is " + maxNumInputStreams,
   1448                         maxNumInputStreams == null || maxNumInputStreams == 0);
   1449                 mCollector.expectTrue("Doesn't support reprocessing but " +
   1450                         "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode);
   1451                 mCollector.expectTrue("Doesn't support reprocessing but " +
   1452                         "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported",
   1453                         !supportZslNoiseReductionMode);
   1454             }
   1455             counter++;
   1456         }
   1457     }
   1458 
   1459     /**
   1460      * Check depth output capability
   1461      */
   1462     public void testDepthOutputCharacteristics() {
   1463         int counter = 0;
   1464 
   1465         for (CameraCharacteristics c : mCharacteristics) {
   1466             Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mAllCameraIds[counter]);
   1467 
   1468             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1469             assertNotNull("android.request.availableCapabilities must never be null",
   1470                     capabilities);
   1471             boolean supportDepth = arrayContains(capabilities,
   1472                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
   1473             StreamConfigurationMap configs =
   1474                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1475 
   1476             int[] outputFormats = configs.getOutputFormats();
   1477             boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16);
   1478 
   1479             Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE);
   1480 
   1481             float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION);
   1482             float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
   1483             Integer poseReference = c.get(CameraCharacteristics.LENS_POSE_REFERENCE);
   1484             float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
   1485             float[] distortion = getLensDistortion(c);
   1486             Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
   1487             Rect precorrectionArray = c.get(
   1488                 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
   1489             Rect activeArray = c.get(
   1490                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
   1491             float jpegAspectRatioThreshold = .01f;
   1492             boolean jpegSizeMatch = false;
   1493 
   1494             // Verify pre-correction array encloses active array
   1495             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
   1496                     precorrectionArray.top + ", " + precorrectionArray.right + ", " +
   1497                     precorrectionArray.bottom + "] does not enclose activeArray[" +
   1498                     activeArray.left + ", " + activeArray.top + ", " + activeArray.right +
   1499                     ", " + activeArray.bottom,
   1500                     precorrectionArray.contains(activeArray.left, activeArray.top) &&
   1501                     precorrectionArray.contains(activeArray.right-1, activeArray.bottom-1));
   1502 
   1503             // Verify pixel array encloses pre-correction array
   1504             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
   1505                     precorrectionArray.top + ", " + precorrectionArray.right + ", " +
   1506                     precorrectionArray.bottom + "] isn't enclosed by pixelArray[" +
   1507                     pixelArraySize.getWidth() + ", " + pixelArraySize.getHeight() + "]",
   1508                     precorrectionArray.left >= 0 &&
   1509                     precorrectionArray.left < pixelArraySize.getWidth() &&
   1510                     precorrectionArray.right > 0 &&
   1511                     precorrectionArray.right <= pixelArraySize.getWidth() &&
   1512                     precorrectionArray.top >= 0 &&
   1513                     precorrectionArray.top < pixelArraySize.getHeight() &&
   1514                     precorrectionArray.bottom > 0 &&
   1515                     precorrectionArray.bottom <= pixelArraySize.getHeight());
   1516 
   1517             if (supportDepth) {
   1518                 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16",
   1519                         hasDepth16);
   1520                 if (hasDepth16) {
   1521                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
   1522                     Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
   1523                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
   1524                             depthSizes != null && depthSizes.length > 0);
   1525                     if (depthSizes != null) {
   1526                         for (Size depthSize : depthSizes) {
   1527                             mCollector.expectTrue("All depth16 sizes must be positive",
   1528                                     depthSize.getWidth() > 0 && depthSize.getHeight() > 0);
   1529                             long minFrameDuration = configs.getOutputMinFrameDuration(
   1530                                     ImageFormat.DEPTH16, depthSize);
   1531                             mCollector.expectTrue("Non-negative min frame duration for depth size "
   1532                                     + depthSize + " expected, got " + minFrameDuration,
   1533                                     minFrameDuration >= 0);
   1534                             long stallDuration = configs.getOutputStallDuration(
   1535                                     ImageFormat.DEPTH16, depthSize);
   1536                             mCollector.expectTrue("Non-negative stall duration for depth size "
   1537                                     + depthSize + " expected, got " + stallDuration,
   1538                                     stallDuration >= 0);
   1539                             if ((jpegSizes != null) && (!jpegSizeMatch)) {
   1540                                 for (Size jpegSize : jpegSizes) {
   1541                                     if (jpegSize.equals(depthSize)) {
   1542                                         jpegSizeMatch = true;
   1543                                         break;
   1544                                     } else {
   1545                                         float depthAR = (float) depthSize.getWidth() /
   1546                                                 (float) depthSize.getHeight();
   1547                                         float jpegAR = (float) jpegSize.getWidth() /
   1548                                                 (float) jpegSize.getHeight();
   1549                                         if (Math.abs(depthAR - jpegAR) <=
   1550                                                 jpegAspectRatioThreshold) {
   1551                                             jpegSizeMatch = true;
   1552                                             break;
   1553                                         }
   1554                                     }
   1555                                 }
   1556                             }
   1557                         }
   1558                     }
   1559                 }
   1560                 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) {
   1561                     Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD);
   1562                     mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " +
   1563                             "but no sizes for DEPTH_POINT_CLOUD supported!",
   1564                             depthCloudSizes != null && depthCloudSizes.length > 0);
   1565                     if (depthCloudSizes != null) {
   1566                         for (Size depthCloudSize : depthCloudSizes) {
   1567                             mCollector.expectTrue("All depth point cloud sizes must be nonzero",
   1568                                     depthCloudSize.getWidth() > 0);
   1569                             mCollector.expectTrue("All depth point cloud sizes must be N x 1",
   1570                                     depthCloudSize.getHeight() == 1);
   1571                             long minFrameDuration = configs.getOutputMinFrameDuration(
   1572                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
   1573                             mCollector.expectTrue("Non-negative min frame duration for depth size "
   1574                                     + depthCloudSize + " expected, got " + minFrameDuration,
   1575                                     minFrameDuration >= 0);
   1576                             long stallDuration = configs.getOutputStallDuration(
   1577                                     ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize);
   1578                             mCollector.expectTrue("Non-negative stall duration for depth size "
   1579                                     + depthCloudSize + " expected, got " + stallDuration,
   1580                                     stallDuration >= 0);
   1581                         }
   1582                     }
   1583                 }
   1584                 if (arrayContains(outputFormats, ImageFormat.DEPTH_JPEG)) {
   1585                     mCollector.expectTrue("Supports DEPTH_JPEG but has no DEPTH16 support!",
   1586                             hasDepth16);
   1587                     mCollector.expectTrue("Supports DEPTH_JPEG but DEPTH_IS_EXCLUSIVE is not " +
   1588                             "defined", depthIsExclusive != null);
   1589                     mCollector.expectTrue("Supports DEPTH_JPEG but DEPTH_IS_EXCLUSIVE is true",
   1590                             !depthIsExclusive.booleanValue());
   1591                     Size[] depthJpegSizes = configs.getOutputSizes(ImageFormat.DEPTH_JPEG);
   1592                     mCollector.expectTrue("Supports DEPTH_JPEG " +
   1593                             "but no sizes for DEPTH_JPEG supported!",
   1594                             depthJpegSizes != null && depthJpegSizes.length > 0);
   1595                     mCollector.expectTrue("Supports DEPTH_JPEG but there are no JPEG sizes with" +
   1596                             " matching DEPTH16 aspect ratio", jpegSizeMatch);
   1597                     if (depthJpegSizes != null) {
   1598                         for (Size depthJpegSize : depthJpegSizes) {
   1599                             mCollector.expectTrue("All depth jpeg sizes must be nonzero",
   1600                                     depthJpegSize.getWidth() > 0 && depthJpegSize.getHeight() > 0);
   1601                             long minFrameDuration = configs.getOutputMinFrameDuration(
   1602                                     ImageFormat.DEPTH_JPEG, depthJpegSize);
   1603                             mCollector.expectTrue("Non-negative min frame duration for depth jpeg" +
   1604                                    " size " + depthJpegSize + " expected, got " + minFrameDuration,
   1605                                     minFrameDuration >= 0);
   1606                             long stallDuration = configs.getOutputStallDuration(
   1607                                     ImageFormat.DEPTH_JPEG, depthJpegSize);
   1608                             mCollector.expectTrue("Non-negative stall duration for depth jpeg size "
   1609                                     + depthJpegSize + " expected, got " + stallDuration,
   1610                                     stallDuration >= 0);
   1611                         }
   1612                     }
   1613                 } else {
   1614                     boolean canSupportDynamicDepth = jpegSizeMatch && !depthIsExclusive;
   1615                     mCollector.expectTrue("Device must support DEPTH_JPEG, please check whether " +
   1616                             "library libdepthphoto.so is part of the device PRODUCT_PACKAGES",
   1617                             !canSupportDynamicDepth);
   1618                 }
   1619 
   1620 
   1621                 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined",
   1622                         depthIsExclusive != null);
   1623 
   1624                 verifyLensCalibration(poseRotation, poseTranslation, poseReference,
   1625                         cameraIntrinsics, distortion, precorrectionArray);
   1626 
   1627             } else {
   1628                 boolean hasFields =
   1629                     hasDepth16 && (poseTranslation != null) &&
   1630                     (poseRotation != null) && (cameraIntrinsics != null) &&
   1631                     (distortion != null) && (depthIsExclusive != null);
   1632 
   1633                 mCollector.expectTrue(
   1634                         "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed",
   1635                         !hasFields);
   1636 
   1637                 boolean reportCalibration = poseTranslation != null ||
   1638                         poseRotation != null || cameraIntrinsics !=null;
   1639                 // Verify calibration keys are co-existing
   1640                 if (reportCalibration) {
   1641                     mCollector.expectTrue(
   1642                             "Calibration keys must be co-existing",
   1643                             poseTranslation != null && poseRotation != null &&
   1644                             cameraIntrinsics !=null);
   1645                 }
   1646 
   1647                 boolean reportDistortion = distortion != null;
   1648                 if (reportDistortion) {
   1649                     mCollector.expectTrue(
   1650                             "Calibration keys must present where distortion is reported",
   1651                             reportCalibration);
   1652                 }
   1653             }
   1654             counter++;
   1655         }
   1656     }
   1657 
   1658     private void verifyLensCalibration(float[] poseRotation, float[] poseTranslation,
   1659             Integer poseReference, float[] cameraIntrinsics, float[] distortion,
   1660             Rect precorrectionArray) {
   1661 
   1662         mCollector.expectTrue(
   1663             "LENS_POSE_ROTATION not right size",
   1664             poseRotation != null && poseRotation.length == 4);
   1665         mCollector.expectTrue(
   1666             "LENS_POSE_TRANSLATION not right size",
   1667             poseTranslation != null && poseTranslation.length == 3);
   1668         mCollector.expectTrue(
   1669             "LENS_POSE_REFERENCE is not defined",
   1670             poseReference != null);
   1671         mCollector.expectTrue(
   1672             "LENS_INTRINSIC_CALIBRATION not right size",
   1673             cameraIntrinsics != null && cameraIntrinsics.length == 5);
   1674         mCollector.expectTrue(
   1675             "LENS_DISTORTION not right size",
   1676             distortion != null && distortion.length == 6);
   1677 
   1678         if (poseRotation != null && poseRotation.length == 4) {
   1679             float normSq =
   1680                     poseRotation[0] * poseRotation[0] +
   1681                     poseRotation[1] * poseRotation[1] +
   1682                     poseRotation[2] * poseRotation[2] +
   1683                     poseRotation[3] * poseRotation[3];
   1684             mCollector.expectTrue(
   1685                 "LENS_POSE_ROTATION quarternion must be unit-length",
   1686                 0.9999f < normSq && normSq < 1.0001f);
   1687 
   1688             // TODO: Cross-validate orientation/facing and poseRotation
   1689         }
   1690 
   1691         if (poseTranslation != null && poseTranslation.length == 3) {
   1692             float normSq =
   1693                     poseTranslation[0] * poseTranslation[0] +
   1694                     poseTranslation[1] * poseTranslation[1] +
   1695                     poseTranslation[2] * poseTranslation[2];
   1696             mCollector.expectTrue("Pose translation is larger than 1 m",
   1697                     normSq < 1.f);
   1698         }
   1699 
   1700         if (poseReference != null) {
   1701             int ref = poseReference;
   1702             boolean validReference = false;
   1703             switch (ref) {
   1704                 case CameraCharacteristics.LENS_POSE_REFERENCE_PRIMARY_CAMERA:
   1705                 case CameraCharacteristics.LENS_POSE_REFERENCE_GYROSCOPE:
   1706                     // Allowed values
   1707                     validReference = true;
   1708                     break;
   1709                 default:
   1710             }
   1711             mCollector.expectTrue("POSE_REFERENCE has unknown value", validReference);
   1712         }
   1713 
   1714         mCollector.expectTrue("Does not have precorrection active array defined",
   1715                 precorrectionArray != null);
   1716 
   1717         if (cameraIntrinsics != null && precorrectionArray != null) {
   1718             float fx = cameraIntrinsics[0];
   1719             float fy = cameraIntrinsics[1];
   1720             float cx = cameraIntrinsics[2];
   1721             float cy = cameraIntrinsics[3];
   1722             float s = cameraIntrinsics[4];
   1723             mCollector.expectTrue("Optical center expected to be within precorrection array",
   1724                     0 <= cx && cx < precorrectionArray.width() &&
   1725                     0 <= cy && cy < precorrectionArray.height());
   1726 
   1727             // TODO: Verify focal lengths and skew are reasonable
   1728         }
   1729 
   1730         if (distortion != null) {
   1731             // TODO: Verify radial distortion
   1732         }
   1733 
   1734     }
   1735 
   1736     /**
   1737      * Cross-check StreamConfigurationMap output
   1738      */
   1739     public void testStreamConfigurationMap() throws Exception {
   1740         int counter = 0;
   1741         for (CameraCharacteristics c : mCharacteristics) {
   1742             Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mAllCameraIds[counter]);
   1743             StreamConfigurationMap config =
   1744                     c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1745             assertNotNull(String.format("No stream configuration map found for: ID %s",
   1746                             mAllCameraIds[counter]), config);
   1747 
   1748             int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1749             assertNotNull("android.request.availableCapabilities must never be null",
   1750                     actualCapabilities);
   1751 
   1752             if (arrayContains(actualCapabilities, BC)) {
   1753                 assertTrue("ImageReader must be supported",
   1754                     config.isOutputSupportedFor(android.media.ImageReader.class));
   1755                 assertTrue("MediaRecorder must be supported",
   1756                     config.isOutputSupportedFor(android.media.MediaRecorder.class));
   1757                 assertTrue("MediaCodec must be supported",
   1758                     config.isOutputSupportedFor(android.media.MediaCodec.class));
   1759                 assertTrue("Allocation must be supported",
   1760                     config.isOutputSupportedFor(android.renderscript.Allocation.class));
   1761                 assertTrue("SurfaceHolder must be supported",
   1762                     config.isOutputSupportedFor(android.view.SurfaceHolder.class));
   1763                 assertTrue("SurfaceTexture must be supported",
   1764                     config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
   1765 
   1766                 assertTrue("YUV_420_888 must be supported",
   1767                     config.isOutputSupportedFor(ImageFormat.YUV_420_888));
   1768                 assertTrue("JPEG must be supported",
   1769                     config.isOutputSupportedFor(ImageFormat.JPEG));
   1770             } else {
   1771                 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed",
   1772                     !config.isOutputSupportedFor(ImageFormat.YUV_420_888));
   1773                 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed",
   1774                     !config.isOutputSupportedFor(ImageFormat.JPEG));
   1775             }
   1776 
   1777             // Check RAW
   1778 
   1779             if (arrayContains(actualCapabilities,
   1780                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
   1781                 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
   1782                     config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
   1783             }
   1784 
   1785             // Cross check public formats and sizes
   1786 
   1787             int[] supportedFormats = config.getOutputFormats();
   1788             for (int format : supportedFormats) {
   1789                 assertTrue("Format " + format + " fails cross check",
   1790                         config.isOutputSupportedFor(format));
   1791                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
   1792                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
   1793                 if (arrayContains(actualCapabilities,
   1794                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
   1795                     supportedSizes.addAll(
   1796                         Arrays.asList(config.getHighResolutionOutputSizes(format)));
   1797                     supportedSizes = CameraTestUtils.getAscendingOrderSizes(
   1798                         supportedSizes, /*ascending*/true);
   1799                 }
   1800                 assertTrue("Supported format " + format + " has no sizes listed",
   1801                         supportedSizes.size() > 0);
   1802                 for (int i = 0; i < supportedSizes.size(); i++) {
   1803                     Size size = supportedSizes.get(i);
   1804                     if (VERBOSE) {
   1805                         Log.v(TAG,
   1806                                 String.format("Testing camera %s, format %d, size %s",
   1807                                         mAllCameraIds[counter], format, size.toString()));
   1808                     }
   1809 
   1810                     long stallDuration = config.getOutputStallDuration(format, size);
   1811                     switch(format) {
   1812                         case ImageFormat.YUV_420_888:
   1813                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
   1814                                     stallDuration == 0);
   1815                             break;
   1816                         case ImageFormat.JPEG:
   1817                         case ImageFormat.RAW_SENSOR:
   1818                             final float TOLERANCE_FACTOR = 2.0f;
   1819                             long prevDuration = 0;
   1820                             if (i > 0) {
   1821                                 prevDuration = config.getOutputStallDuration(
   1822                                         format, supportedSizes.get(i - 1));
   1823                             }
   1824                             long nextDuration = Long.MAX_VALUE;
   1825                             if (i < (supportedSizes.size() - 1)) {
   1826                                 nextDuration = config.getOutputStallDuration(
   1827                                         format, supportedSizes.get(i + 1));
   1828                             }
   1829                             long curStallDuration = config.getOutputStallDuration(format, size);
   1830                             // Stall duration should be in a reasonable range: larger size should
   1831                             // normally have larger stall duration.
   1832                             mCollector.expectInRange("Stall duration (format " + format +
   1833                                     " and size " + size + ") is not in the right range",
   1834                                     curStallDuration,
   1835                                     (long) (prevDuration / TOLERANCE_FACTOR),
   1836                                     (long) (nextDuration * TOLERANCE_FACTOR));
   1837                             break;
   1838                         default:
   1839                             assertTrue("Negative stall duration for format " + format,
   1840                                     stallDuration >= 0);
   1841                             break;
   1842                     }
   1843                     long minDuration = config.getOutputMinFrameDuration(format, size);
   1844                     if (arrayContains(actualCapabilities,
   1845                             CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
   1846                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
   1847                                 + "format " + format + " for size " + size + " minDuration " +
   1848                                 minDuration,
   1849                                 minDuration > 0);
   1850                     } else {
   1851                         assertTrue("Need non-negative min frame duration for format " + format,
   1852                                 minDuration >= 0);
   1853                     }
   1854 
   1855                     // todo: test opaque image reader when it's supported.
   1856                     if (format != ImageFormat.PRIVATE) {
   1857                         ImageReader testReader = ImageReader.newInstance(
   1858                             size.getWidth(),
   1859                             size.getHeight(),
   1860                             format,
   1861                             1);
   1862                         Surface testSurface = testReader.getSurface();
   1863 
   1864                         assertTrue(
   1865                             String.format("isOutputSupportedFor fails for config %s, format %d",
   1866                                     size.toString(), format),
   1867                             config.isOutputSupportedFor(testSurface));
   1868 
   1869                         testReader.close();
   1870                     }
   1871                 } // sizes
   1872 
   1873                 // Try an invalid size in this format, should round
   1874                 Size invalidSize = findInvalidSize(supportedSizes);
   1875                 int MAX_ROUNDING_WIDTH = 1920;
   1876                 // todo: test opaque image reader when it's supported.
   1877                 if (format != ImageFormat.PRIVATE &&
   1878                         invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
   1879                     ImageReader testReader = ImageReader.newInstance(
   1880                                                                      invalidSize.getWidth(),
   1881                                                                      invalidSize.getHeight(),
   1882                                                                      format,
   1883                                                                      1);
   1884                     Surface testSurface = testReader.getSurface();
   1885 
   1886                     assertTrue(
   1887                                String.format("isOutputSupportedFor fails for config %s, %d",
   1888                                        invalidSize.toString(), format),
   1889                                config.isOutputSupportedFor(testSurface));
   1890 
   1891                     testReader.close();
   1892                 }
   1893             } // formats
   1894 
   1895             // Cross-check opaque format and sizes
   1896             if (arrayContains(actualCapabilities, BC)) {
   1897                 SurfaceTexture st = new SurfaceTexture(1);
   1898                 Surface surf = new Surface(st);
   1899 
   1900                 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class,
   1901                         mAllCameraIds[counter], mCameraManager);
   1902                 assertTrue("Opaque format has no sizes listed",
   1903                         opaqueSizes.length > 0);
   1904                 for (Size size : opaqueSizes) {
   1905                     long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
   1906                     assertTrue("Opaque output may not have a non-zero stall duration",
   1907                             stallDuration == 0);
   1908 
   1909                     long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
   1910                     if (arrayContains(actualCapabilities,
   1911                                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
   1912                         assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
   1913                                 + "opaque format",
   1914                                 minDuration > 0);
   1915                     } else {
   1916                         assertTrue("Need non-negative min frame duration for opaque format ",
   1917                                 minDuration >= 0);
   1918                     }
   1919                     st.setDefaultBufferSize(size.getWidth(), size.getHeight());
   1920 
   1921                     assertTrue(
   1922                             String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
   1923                                     size.toString()),
   1924                             config.isOutputSupportedFor(surf));
   1925 
   1926                 } // opaque sizes
   1927 
   1928                 // Try invalid opaque size, should get rounded
   1929                 Size invalidSize = findInvalidSize(opaqueSizes);
   1930                 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
   1931                 assertTrue(
   1932                         String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
   1933                                 invalidSize.toString()),
   1934                         config.isOutputSupportedFor(surf));
   1935 
   1936             }
   1937             counter++;
   1938         } // mCharacteristics
   1939     }
   1940 
   1941     /**
   1942      * Test high speed capability and cross-check the high speed sizes and fps ranges from
   1943      * the StreamConfigurationMap.
   1944      */
   1945     public void testConstrainedHighSpeedCapability() throws Exception {
   1946         int counter = 0;
   1947         for (CameraCharacteristics c : mCharacteristics) {
   1948             int[] capabilities = CameraTestUtils.getValueNotNull(
   1949                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   1950             boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED);
   1951             if (supportHighSpeed) {
   1952                 StreamConfigurationMap config =
   1953                         CameraTestUtils.getValueNotNull(
   1954                                 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1955                 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
   1956                 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0);
   1957                 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE,
   1958                         mAllCameraIds[counter], mCameraManager);
   1959                 assertTrue("Normal size for PRIVATE format shouldn't be null or empty",
   1960                         allSizes != null && allSizes.length > 0);
   1961                 for (Size size: highSpeedSizes) {
   1962                     // The sizes must be a subset of the normal sizes
   1963                     assertTrue("High speed size " + size +
   1964                             " must be part of normal sizes " + Arrays.toString(allSizes),
   1965                             Arrays.asList(allSizes).contains(size));
   1966 
   1967                     // Sanitize the high speed FPS ranges for each size
   1968                     List<Range<Integer>> ranges =
   1969                             Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size));
   1970                     for (Range<Integer> range : ranges) {
   1971                         assertTrue("The range " + range + " doesn't satisfy the"
   1972                                 + " min/max boundary requirements.",
   1973                                 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN &&
   1974                                 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN);
   1975                         assertTrue("The range " + range + " should be multiple of 30fps",
   1976                                 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0);
   1977                         // If the range is fixed high speed range, it should contain the
   1978                         // [30, fps_max] in the high speed range list; if it's variable FPS range,
   1979                         // the corresponding fixed FPS Range must be included in the range list.
   1980                         if (range.getLower() == range.getUpper()) {
   1981                             Range<Integer> variableRange = new Range<Integer>(30, range.getUpper());
   1982                             assertTrue("The variable FPS range " + variableRange +
   1983                                     " shoould be included in the high speed ranges for size " +
   1984                                     size, ranges.contains(variableRange));
   1985                         } else {
   1986                             Range<Integer> fixedRange =
   1987                                     new Range<Integer>(range.getUpper(), range.getUpper());
   1988                             assertTrue("The fixed FPS range " + fixedRange +
   1989                                     " shoould be included in the high speed ranges for size " +
   1990                                     size, ranges.contains(fixedRange));
   1991                         }
   1992                     }
   1993                 }
   1994                 // If the device advertise some high speed profiles, the sizes and FPS ranges
   1995                 // should be advertise by the camera.
   1996                 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
   1997                         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
   1998                     int cameraId = Integer.valueOf(mAllCameraIds[counter]);
   1999                     if (CamcorderProfile.hasProfile(cameraId, quality)) {
   2000                         CamcorderProfile profile = CamcorderProfile.get(cameraId, quality);
   2001                         Size camcorderProfileSize =
   2002                                 new Size(profile.videoFrameWidth, profile.videoFrameHeight);
   2003                         assertTrue("CamcorderPrfile size " + camcorderProfileSize +
   2004                                 " must be included in the high speed sizes " +
   2005                                 Arrays.toString(highSpeedSizes.toArray()),
   2006                                 highSpeedSizes.contains(camcorderProfileSize));
   2007                         Range<Integer> camcorderFpsRange =
   2008                                 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate);
   2009                         List<Range<Integer>> allRanges =
   2010                                 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(
   2011                                         camcorderProfileSize));
   2012                         assertTrue("Camcorder fps range " + camcorderFpsRange +
   2013                                 " should be included by high speed fps ranges " +
   2014                                 Arrays.toString(allRanges.toArray()),
   2015                                 allRanges.contains(camcorderFpsRange));
   2016                     }
   2017                 }
   2018             }
   2019             counter++;
   2020         }
   2021     }
   2022 
   2023     /**
   2024      * Sanity check of optical black regions.
   2025      */
   2026     public void testOpticalBlackRegions() {
   2027         int counter = 0;
   2028         for (CameraCharacteristics c : mCharacteristics) {
   2029             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
   2030             boolean hasDynamicBlackLevel =
   2031                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
   2032             boolean hasDynamicWhiteLevel =
   2033                     resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
   2034             boolean hasFixedBlackLevel =
   2035                     c.getKeys().contains(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
   2036             boolean hasFixedWhiteLevel =
   2037                     c.getKeys().contains(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
   2038             // The black and white levels should be either all supported or none of them is
   2039             // supported.
   2040             mCollector.expectTrue("Dynamic black and white level should be all or none of them"
   2041                     + " be supported", hasDynamicWhiteLevel == hasDynamicBlackLevel);
   2042             mCollector.expectTrue("Fixed black and white level should be all or none of them"
   2043                     + " be supported", hasFixedBlackLevel == hasFixedWhiteLevel);
   2044             mCollector.expectTrue("Fixed black level should be supported if dynamic black"
   2045                     + " level is supported", !hasDynamicBlackLevel || hasFixedBlackLevel);
   2046 
   2047             if (c.getKeys().contains(CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS)) {
   2048                 // Regions shouldn't be null or empty.
   2049                 Rect[] regions = CameraTestUtils.getValueNotNull(c,
   2050                         CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS);
   2051                 CameraTestUtils.assertArrayNotEmpty(regions, "Optical back region arrays must not"
   2052                         + " be empty");
   2053 
   2054                 // Dynamic black level should be supported if the optical black region is
   2055                 // advertised.
   2056                 mCollector.expectTrue("Dynamic black and white level keys should be advertised in "
   2057                         + "available capture result key list", hasDynamicWhiteLevel);
   2058 
   2059                 // Range check.
   2060                 for (Rect region : regions) {
   2061                     mCollector.expectTrue("Camera " + mAllCameraIds[counter] + ": optical black region" +
   2062                             " shouldn't be empty!", !region.isEmpty());
   2063                     mCollector.expectGreaterOrEqual("Optical black region left", 0/*expected*/,
   2064                             region.left/*actual*/);
   2065                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
   2066                             region.top/*actual*/);
   2067                     mCollector.expectTrue("Optical black region left/right/width/height must be"
   2068                             + " even number, otherwise, the bayer CFA pattern in this region will"
   2069                             + " be messed up",
   2070                             region.left % 2 == 0 && region.top % 2 == 0 &&
   2071                             region.width() % 2 == 0 && region.height() % 2 == 0);
   2072                     mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/,
   2073                             region.top/*actual*/);
   2074                     Size size = CameraTestUtils.getValueNotNull(c,
   2075                             CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
   2076                     mCollector.expectLessOrEqual("Optical black region width",
   2077                             size.getWidth()/*expected*/, region.width());
   2078                     mCollector.expectLessOrEqual("Optical black region height",
   2079                             size.getHeight()/*expected*/, region.height());
   2080                     Rect activeArray = CameraTestUtils.getValueNotNull(c,
   2081                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
   2082                     mCollector.expectTrue("Optical black region" + region + " should be outside of"
   2083                             + " active array " + activeArray,
   2084                             !region.intersect(activeArray));
   2085                     // Region need to be disjoint:
   2086                     for (Rect region2 : regions) {
   2087                         mCollector.expectTrue("Optical black region" + region + " should have no "
   2088                                 + "overlap with " + region2,
   2089                                 region == region2 || !region.intersect(region2));
   2090                     }
   2091                 }
   2092             } else {
   2093                 Log.i(TAG, "Camera " + mAllCameraIds[counter] + " doesn't support optical black regions,"
   2094                         + " skip the region test");
   2095             }
   2096             counter++;
   2097         }
   2098     }
   2099 
   2100     /**
   2101      * Check Logical camera capability
   2102      */
   2103     public void testLogicalCameraCharacteristics() throws Exception {
   2104         int counter = 0;
   2105         String[] publicIds = mCameraManager.getCameraIdList();
   2106 
   2107         for (CameraCharacteristics c : mCharacteristics) {
   2108             int[] capabilities = CameraTestUtils.getValueNotNull(
   2109                     c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   2110             boolean supportLogicalCamera = arrayContains(capabilities,
   2111                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
   2112             if (supportLogicalCamera) {
   2113                 Set<String> physicalCameraIds = c.getPhysicalCameraIds();
   2114                 assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null",
   2115                     physicalCameraIds);
   2116                 assertTrue("Logical camera must contain at least 2 physical camera ids",
   2117                     physicalCameraIds.size() >= 2);
   2118 
   2119                 mCollector.expectKeyValueInRange(c,
   2120                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
   2121                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
   2122                         CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED);
   2123 
   2124                 Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
   2125                 for (String physicalCameraId : physicalCameraIds) {
   2126                     assertNotNull("Physical camera id shouldn't be null", physicalCameraId);
   2127                     assertTrue(
   2128                             String.format("Physical camera id %s shouldn't be the same as logical"
   2129                                     + " camera id %s", physicalCameraId, mAllCameraIds[counter]),
   2130                             physicalCameraId != mAllCameraIds[counter]);
   2131 
   2132                     //validation for depth static metadata of physical cameras
   2133                     CameraCharacteristics pc =
   2134                             mCameraManager.getCameraCharacteristics(physicalCameraId);
   2135 
   2136                     float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION);
   2137                     float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
   2138                     Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE);
   2139                     float[] cameraIntrinsics = pc.get(
   2140                             CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
   2141                     float[] distortion = getLensDistortion(pc);
   2142                     Rect precorrectionArray = pc.get(
   2143                             CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
   2144 
   2145                     verifyLensCalibration(poseRotation, poseTranslation, poseReference,
   2146                             cameraIntrinsics, distortion, precorrectionArray);
   2147 
   2148                     Integer timestampSourcePhysical =
   2149                             pc.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
   2150                     mCollector.expectEquals("Logical camera and physical cameras must have same " +
   2151                             "timestamp source", timestampSource, timestampSourcePhysical);
   2152                 }
   2153             }
   2154 
   2155             // Verify that if multiple focal lengths or apertures are supported, they are in
   2156             // ascending order.
   2157             Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
   2158             boolean isExternalCamera = (hwLevel ==
   2159                     CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
   2160             if (!isExternalCamera) {
   2161                 float[] focalLengths = c.get(
   2162                         CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
   2163                 for (int i = 0; i < focalLengths.length-1; i++) {
   2164                     mCollector.expectTrue("Camera's available focal lengths must be ascending!",
   2165                             focalLengths[i] < focalLengths[i+1]);
   2166                 }
   2167                 float[] apertures = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
   2168                 for (int i = 0; i < apertures.length-1; i++) {
   2169                     mCollector.expectTrue("Camera's available apertures must be ascending!",
   2170                             apertures[i] < apertures[i+1]);
   2171                 }
   2172             }
   2173             counter++;
   2174         }
   2175     }
   2176 
   2177     /**
   2178      * Check monochrome camera capability
   2179      */
   2180     public void testMonochromeCharacteristics() {
   2181         int counter = 0;
   2182 
   2183         for (CameraCharacteristics c : mCharacteristics) {
   2184             Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + mAllCameraIds[counter]);
   2185 
   2186             int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   2187             assertNotNull("android.request.availableCapabilities must never be null",
   2188                     capabilities);
   2189             boolean supportMonochrome = arrayContains(capabilities, MONOCHROME);
   2190 
   2191             if (!supportMonochrome) {
   2192                 continue;
   2193             }
   2194 
   2195             List<Key<?>> allKeys = c.getKeys();
   2196             List<CaptureRequest.Key<?>> requestKeys = c.getAvailableCaptureRequestKeys();
   2197             List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys();
   2198 
   2199             assertTrue("Monochrome camera must have BACKWARD_COMPATIBLE capability",
   2200                     arrayContains(capabilities, BC));
   2201             int colorFilterArrangement = c.get(
   2202                     CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
   2203             assertTrue("Monochrome camera must have either MONO or NIR color filter pattern",
   2204                     colorFilterArrangement ==
   2205                             CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO
   2206                     || colorFilterArrangement ==
   2207                             CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR);
   2208 
   2209             assertFalse("Monochrome camera must not contain SENSOR_CALIBRATION_TRANSFORM1 key",
   2210                     allKeys.contains(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
   2211             assertFalse("Monochrome camera must not contain SENSOR_COLOR_TRANSFORM1 key",
   2212                     allKeys.contains(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
   2213             assertFalse("Monochrome camera must not contain SENSOR_FORWARD_MATRIX1 key",
   2214                     allKeys.contains(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
   2215             assertFalse("Monochrome camera must not contain SENSOR_REFERENCE_ILLUMINANT1 key",
   2216                     allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
   2217             assertFalse("Monochrome camera must not contain SENSOR_CALIBRATION_TRANSFORM2 key",
   2218                     allKeys.contains(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
   2219             assertFalse("Monochrome camera must not contain SENSOR_COLOR_TRANSFORM2 key",
   2220                     allKeys.contains(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
   2221             assertFalse("Monochrome camera must not contain SENSOR_FORWARD_MATRIX2 key",
   2222                     allKeys.contains(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
   2223             assertFalse("Monochrome camera must not contain SENSOR_REFERENCE_ILLUMINANT2 key",
   2224                     allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
   2225 
   2226             assertFalse(
   2227                     "Monochrome capture result must not contain SENSOR_NEUTRAL_COLOR_POINT key",
   2228                     resultKeys.contains(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT));
   2229             assertFalse("Monochrome capture result must not contain SENSOR_GREEN_SPLIT key",
   2230                     resultKeys.contains(CaptureResult.SENSOR_GREEN_SPLIT));
   2231 
   2232             // Check that color correction tags are not available for monochrome cameras
   2233             assertTrue("Monochrome camera must not have MANUAL_POST_PROCESSING capability",
   2234                     !arrayContains(capabilities, MANUAL_POSTPROC));
   2235             assertTrue("Monochrome camera must not have COLOR_CORRECTION_MODE in request keys",
   2236                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_MODE));
   2237             assertTrue("Monochrome camera must not have COLOR_CORRECTION_MODE in result keys",
   2238                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_MODE));
   2239             assertTrue("Monochrome camera must not have COLOR_CORRECTION_TRANSFORM in request keys",
   2240                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_TRANSFORM));
   2241             assertTrue("Monochrome camera must not have COLOR_CORRECTION_TRANSFORM in result keys",
   2242                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_TRANSFORM));
   2243             assertTrue("Monochrome camera must not have COLOR_CORRECTION_GAINS in request keys",
   2244                     !requestKeys.contains(CaptureRequest.COLOR_CORRECTION_GAINS));
   2245             assertTrue("Monochrome camera must not have COLOR_CORRECTION_GAINS in result keys",
   2246                     !resultKeys.contains(CaptureResult.COLOR_CORRECTION_GAINS));
   2247 
   2248             // Check that awbSupportedModes only contains AUTO
   2249             int[] awbAvailableModes = c.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
   2250             assertTrue("availableAwbModes must not be null", awbAvailableModes != null);
   2251             assertTrue("availableAwbModes must contain only AUTO", awbAvailableModes.length == 1 &&
   2252                     awbAvailableModes[0] == CaptureRequest.CONTROL_AWB_MODE_AUTO);
   2253         }
   2254     }
   2255 
   2256     private boolean matchParametersToCharacteritics(Camera.Parameters params,
   2257             Camera.CameraInfo info, CameraCharacteristics ch) {
   2258         Integer facing = ch.get(CameraCharacteristics.LENS_FACING);
   2259         switch (facing.intValue()) {
   2260             case CameraMetadata.LENS_FACING_EXTERNAL:
   2261             case CameraMetadata.LENS_FACING_FRONT:
   2262                 if (info.facing != Camera.CameraInfo.CAMERA_FACING_FRONT) {
   2263                     return false;
   2264                 }
   2265                 break;
   2266             case CameraMetadata.LENS_FACING_BACK:
   2267                 if (info.facing != Camera.CameraInfo.CAMERA_FACING_BACK) {
   2268                     return false;
   2269                 }
   2270                 break;
   2271             default:
   2272                 return false;
   2273         }
   2274 
   2275         Integer orientation = ch.get(CameraCharacteristics.SENSOR_ORIENTATION);
   2276         if (orientation.intValue() != info.orientation) {
   2277             return false;
   2278         }
   2279 
   2280         StaticMetadata staticMeta = new StaticMetadata(ch);
   2281         boolean legacyHasFlash = params.getSupportedFlashModes() != null;
   2282         if (staticMeta.hasFlash() != legacyHasFlash) {
   2283             return false;
   2284         }
   2285 
   2286         List<String> legacyFocusModes = params.getSupportedFocusModes();
   2287         boolean legacyHasFocuser = !((legacyFocusModes.size() == 1) &&
   2288                 (legacyFocusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED)));
   2289         if (staticMeta.hasFocuser() != legacyHasFocuser) {
   2290             return false;
   2291         }
   2292 
   2293         if (staticMeta.isVideoStabilizationSupported() != params.isVideoStabilizationSupported()) {
   2294             return false;
   2295         }
   2296 
   2297         float legacyFocalLength = params.getFocalLength();
   2298         float [] focalLengths = staticMeta.getAvailableFocalLengthsChecked();
   2299         boolean found = false;
   2300         for (float focalLength : focalLengths) {
   2301             if (Math.abs(focalLength - legacyFocalLength) <= FOCAL_LENGTH_TOLERANCE) {
   2302                 found = true;
   2303                 break;
   2304             }
   2305         }
   2306 
   2307         return found;
   2308     }
   2309 
   2310     /**
   2311      * Check that all devices available through the legacy API are also
   2312      * accessible via Camera2.
   2313      */
   2314     @CddTest(requirement="7.5.4/C-0-11")
   2315     public void testLegacyCameraDeviceParity() {
   2316         int legacyDeviceCount = Camera.getNumberOfCameras();
   2317         assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
   2318                 mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
   2319 
   2320         ArrayList<CameraCharacteristics> chars = new ArrayList<> (mCharacteristics);
   2321         for (int i = 0; i < legacyDeviceCount; i++) {
   2322             Camera camera = null;
   2323             Camera.Parameters legacyParams = null;
   2324             Camera.CameraInfo legacyInfo = new Camera.CameraInfo();
   2325             try {
   2326                 Camera.getCameraInfo(i, legacyInfo);
   2327                 camera = Camera.open(i);
   2328                 legacyParams = camera.getParameters();
   2329 
   2330                 assertNotNull("Camera parameters for device: " + i + "  must not be null",
   2331                         legacyParams);
   2332             } finally {
   2333                 if (camera != null) {
   2334                     camera.release();
   2335                 }
   2336             }
   2337 
   2338             // Camera Ids between legacy devices and Camera2 device could be
   2339             // different try to match devices by using other common traits.
   2340             CameraCharacteristics found = null;
   2341             for (CameraCharacteristics ch : chars) {
   2342                 if (matchParametersToCharacteritics(legacyParams, legacyInfo, ch)) {
   2343                     found = ch;
   2344                     break;
   2345                 }
   2346             }
   2347             assertNotNull("No matching Camera2 device for legacy device id: " + i, found);
   2348 
   2349             chars.remove(found);
   2350         }
   2351     }
   2352 
   2353     /**
   2354      * Check camera orientation against device orientation
   2355      */
   2356     @CddTest(requirement="7.5.5/C-1-1")
   2357     public void testCameraOrientationAlignedWithDevice() {
   2358         WindowManager windowManager =
   2359                 (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
   2360         Display display = windowManager.getDefaultDisplay();
   2361         DisplayMetrics metrics = new DisplayMetrics();
   2362         display.getMetrics(metrics);
   2363 
   2364         // For square screen, test is guaranteed to pass
   2365         if (metrics.widthPixels == metrics.heightPixels) {
   2366             return;
   2367         }
   2368 
   2369         // Handle display rotation
   2370         int displayRotation = display.getRotation();
   2371         if (displayRotation == Surface.ROTATION_90 || displayRotation == Surface.ROTATION_270) {
   2372             int tmp = metrics.widthPixels;
   2373             metrics.widthPixels = metrics.heightPixels;
   2374             metrics.heightPixels = tmp;
   2375         }
   2376         boolean isDevicePortrait = metrics.widthPixels < metrics.heightPixels;
   2377 
   2378         int counter = 0;
   2379         for (CameraCharacteristics c : mCharacteristics) {
   2380             // Camera size
   2381             Size pixelArraySize = c.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
   2382             // Camera orientation
   2383             int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
   2384 
   2385             // For square sensor, test is guaranteed to pass
   2386             if (pixelArraySize.getWidth() == pixelArraySize.getHeight()) {
   2387                 counter++;
   2388                 continue;
   2389             }
   2390 
   2391             // Camera size adjusted for device native orientation.
   2392             Size adjustedSensorSize;
   2393             if (sensorOrientation == 90 || sensorOrientation == 270) {
   2394                 adjustedSensorSize = new Size(
   2395                         pixelArraySize.getHeight(), pixelArraySize.getWidth());
   2396             } else {
   2397                 adjustedSensorSize = pixelArraySize;
   2398             }
   2399 
   2400             boolean isCameraPortrait =
   2401                     adjustedSensorSize.getWidth() < adjustedSensorSize.getHeight();
   2402             assertFalse("Camera " + mAllCameraIds[counter] + "'s long dimension must "
   2403                     + "align with screen's long dimension", isDevicePortrait^isCameraPortrait);
   2404             counter++;
   2405         }
   2406     }
   2407 
   2408     /**
   2409      * Get lens distortion coefficients, as a list of 6 floats; returns null if no valid
   2410      * distortion field is available
   2411      */
   2412     private float[] getLensDistortion(CameraCharacteristics c) {
   2413         float[] distortion = null;
   2414         float[] newDistortion = c.get(CameraCharacteristics.LENS_DISTORTION);
   2415         if (Build.VERSION.FIRST_SDK_INT > Build.VERSION_CODES.O_MR1 || newDistortion != null) {
   2416             // New devices need to use fixed radial distortion definition; old devices can
   2417             // opt-in to it
   2418             if (newDistortion != null && newDistortion.length == 5) {
   2419                 distortion = new float[6];
   2420                 distortion[0] = 1.0f;
   2421                 for (int i = 1; i < 6; i++) {
   2422                     distortion[i] = newDistortion[i-1];
   2423                 }
   2424             }
   2425         } else {
   2426             // Select old field only if on older first SDK and new definition not available
   2427             distortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
   2428         }
   2429         return distortion;
   2430     }
   2431 
   2432     /**
   2433      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
   2434      */
   2435     private Size findInvalidSize(Size[] goodSizes) {
   2436         return findInvalidSize(Arrays.asList(goodSizes));
   2437     }
   2438 
   2439     /**
   2440      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
   2441      */
   2442     private Size findInvalidSize(List<Size> goodSizes) {
   2443         Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
   2444         while(goodSizes.contains(invalidSize)) {
   2445             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
   2446         }
   2447         return invalidSize;
   2448     }
   2449 
   2450     /**
   2451      * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
   2452      * check that the key is present if the actual capabilities are one of {@code capabilities}.
   2453      *
   2454      * @return value of the {@code key} from {@code c}
   2455      */
   2456     private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
   2457             int hwLevel, int... capabilities) {
   2458 
   2459         Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
   2460         assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
   2461 
   2462         int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
   2463         assertNotNull("android.request.availableCapabilities must never be null",
   2464                 actualCapabilities);
   2465 
   2466         List<Key<?>> allKeys = c.getKeys();
   2467 
   2468         T value = c.get(key);
   2469 
   2470         // For LIMITED-level targeted keys, rely on capability check, not level
   2471         if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) {
   2472             mCollector.expectTrue(
   2473                     String.format("Key (%s) must be in characteristics for this hardware level " +
   2474                             "(required minimal HW level %s, actual HW level %s)",
   2475                             key.getName(), toStringHardwareLevel(hwLevel),
   2476                             toStringHardwareLevel(actualHwLevel)),
   2477                     value != null);
   2478             mCollector.expectTrue(
   2479                     String.format("Key (%s) must be in characteristics list of keys for this " +
   2480                             "hardware level (required minimal HW level %s, actual HW level %s)",
   2481                             key.getName(), toStringHardwareLevel(hwLevel),
   2482                             toStringHardwareLevel(actualHwLevel)),
   2483                     allKeys.contains(key));
   2484         } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
   2485             if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) {
   2486                 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined
   2487                 mCollector.expectTrue(
   2488                     String.format("Key (%s) must be in characteristics for these capabilities " +
   2489                             "(required capabilities %s, actual capabilities %s)",
   2490                             key.getName(), Arrays.toString(capabilities),
   2491                             Arrays.toString(actualCapabilities)),
   2492                     value != null);
   2493                 mCollector.expectTrue(
   2494                     String.format("Key (%s) must be in characteristics list of keys for " +
   2495                             "these capabilities (required capabilities %s, actual capabilities %s)",
   2496                             key.getName(), Arrays.toString(capabilities),
   2497                             Arrays.toString(actualCapabilities)),
   2498                     allKeys.contains(key));
   2499             }
   2500         } else {
   2501             if (actualHwLevel == LEGACY && hwLevel != OPT) {
   2502                 if (value != null || allKeys.contains(key)) {
   2503                     Log.w(TAG, String.format(
   2504                             "Key (%s) is not required for LEGACY devices but still appears",
   2505                             key.getName()));
   2506                 }
   2507             }
   2508             // OK: Key may or may not be present.
   2509         }
   2510         return value;
   2511     }
   2512 
   2513     private static boolean arrayContains(int[] arr, int needle) {
   2514         if (arr == null) {
   2515             return false;
   2516         }
   2517 
   2518         for (int elem : arr) {
   2519             if (elem == needle) {
   2520                 return true;
   2521             }
   2522         }
   2523 
   2524         return false;
   2525     }
   2526 
   2527     private static <T> boolean arrayContains(T[] arr, T needle) {
   2528         if (arr == null) {
   2529             return false;
   2530         }
   2531 
   2532         for (T elem : arr) {
   2533             if (elem.equals(needle)) {
   2534                 return true;
   2535             }
   2536         }
   2537 
   2538         return false;
   2539     }
   2540 
   2541     private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
   2542         for (int needle : needles) {
   2543             if (arrayContains(arr, needle)) {
   2544                 return true;
   2545             }
   2546         }
   2547         return false;
   2548     }
   2549 
   2550     /**
   2551      * The key name has a prefix of either "android." or a valid TLD; other prefixes are not valid.
   2552      */
   2553     private static void assertKeyPrefixValid(String keyName) {
   2554         assertStartsWithAndroidOrTLD(
   2555                 "All metadata keys must start with 'android.' (built-in keys) " +
   2556                 "or valid TLD (vendor-extended keys)", keyName);
   2557     }
   2558 
   2559     private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
   2560             boolean actual) {
   2561         assertTrue(msg + " (key = '" + key.getName() + "')", actual);
   2562     }
   2563 
   2564     private static <T> void assertOneOf(String msg, T[] expected, T actual) {
   2565         for (int i = 0; i < expected.length; ++i) {
   2566             if (Objects.equals(expected[i], actual)) {
   2567                 return;
   2568             }
   2569         }
   2570 
   2571         fail(String.format("%s: (expected one of %s, actual %s)",
   2572                 msg, Arrays.toString(expected), actual));
   2573     }
   2574 
   2575     private static <T> void assertStartsWithAndroidOrTLD(String msg, String keyName) {
   2576         String delimiter = ".";
   2577         if (keyName.startsWith(PREFIX_ANDROID + delimiter)) {
   2578             return;
   2579         }
   2580         Pattern tldPattern = Pattern.compile(Patterns.TOP_LEVEL_DOMAIN_STR);
   2581         Matcher match = tldPattern.matcher(keyName);
   2582         if (match.find(0) && (0 == match.start()) && (!match.hitEnd())) {
   2583             if (keyName.regionMatches(match.end(), delimiter, 0, delimiter.length())) {
   2584                 return;
   2585             }
   2586         }
   2587 
   2588         fail(String.format("%s: (expected to start with %s or valid TLD, but value was %s)",
   2589                 msg, PREFIX_ANDROID + delimiter, keyName));
   2590     }
   2591 
   2592     /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
   2593     private static int compareHardwareLevel(int left, int right) {
   2594         return remapHardwareLevel(left) - remapHardwareLevel(right);
   2595     }
   2596 
   2597     /** Remap HW levels worst<->best, 0 = LEGACY, 1 = LIMITED, 2 = FULL, ..., N = LEVEL_N */
   2598     private static int remapHardwareLevel(int level) {
   2599         switch (level) {
   2600             case OPT:
   2601                 return Integer.MAX_VALUE;
   2602             case LEGACY:
   2603                 return 0; // lowest
   2604             case EXTERNAL:
   2605                 return 1; // second lowest
   2606             case LIMITED:
   2607                 return 2;
   2608             case FULL:
   2609                 return 3; // good
   2610             case LEVEL_3:
   2611                 return 4;
   2612             default:
   2613                 fail("Unknown HW level: " + level);
   2614         }
   2615         return -1;
   2616     }
   2617 
   2618     private static String toStringHardwareLevel(int level) {
   2619         switch (level) {
   2620             case LEGACY:
   2621                 return "LEGACY";
   2622             case LIMITED:
   2623                 return "LIMITED";
   2624             case FULL:
   2625                 return "FULL";
   2626             case EXTERNAL:
   2627                 return "EXTERNAL";
   2628             default:
   2629                 if (level >= LEVEL_3) {
   2630                     return String.format("LEVEL_%d", level);
   2631                 }
   2632         }
   2633 
   2634         // unknown
   2635         Log.w(TAG, "Unknown hardware level " + level);
   2636         return Integer.toString(level);
   2637     }
   2638 }
   2639