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