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