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