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