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