1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.camera2.cts; 18 19 import android.content.Context; 20 import android.graphics.ImageFormat; 21 import android.graphics.Rect; 22 import android.graphics.SurfaceTexture; 23 import android.hardware.camera2.CameraCharacteristics; 24 import android.hardware.camera2.CameraCharacteristics.Key; 25 import android.hardware.camera2.CameraManager; 26 import android.hardware.camera2.CaptureRequest; 27 import android.hardware.camera2.CaptureResult; 28 import android.hardware.camera2.cts.helpers.CameraErrorCollector; 29 import android.hardware.camera2.params.BlackLevelPattern; 30 import android.hardware.camera2.params.ColorSpaceTransform; 31 import android.hardware.camera2.params.StreamConfigurationMap; 32 import android.media.CamcorderProfile; 33 import android.media.ImageReader; 34 import android.os.Build; 35 import android.platform.test.annotations.AppModeFull; 36 import android.test.AndroidTestCase; 37 import android.util.Log; 38 import android.util.Rational; 39 import android.util.Range; 40 import android.util.Size; 41 import android.util.Patterns; 42 import android.view.Surface; 43 import android.view.WindowManager; 44 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.List; 48 import java.util.Objects; 49 import java.util.regex.Matcher; 50 import java.util.regex.Pattern; 51 import java.util.Set; 52 53 import static android.hardware.camera2.cts.helpers.AssertHelpers.*; 54 55 /** 56 * Extended tests for static camera characteristics. 57 */ 58 @AppModeFull 59 public class ExtendedCameraCharacteristicsTest extends AndroidTestCase { 60 private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw 61 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 62 63 private static final String PREFIX_ANDROID = "android"; 64 65 /* 66 * Constants for static RAW metadata. 67 */ 68 private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5 69 70 private CameraManager mCameraManager; 71 private List<CameraCharacteristics> mCharacteristics; 72 private String[] mIds; 73 private CameraErrorCollector mCollector; 74 75 private static final Size FULLHD = new Size(1920, 1080); 76 private static final Size FULLHD_ALT = new Size(1920, 1088); 77 private static final Size HD = new Size(1280, 720); 78 private static final Size VGA = new Size(640, 480); 79 private static final Size QVGA = new Size(320, 240); 80 81 private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30; 82 /* 83 * HW Levels short hand 84 */ 85 private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; 86 private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED; 87 private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL; 88 private static final int LEVEL_3 = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3; 89 private static final int EXTERNAL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL; 90 private static final int OPT = Integer.MAX_VALUE; // For keys that are optional on all hardware levels. 91 92 /* 93 * Capabilities short hand 94 */ 95 private static final int NONE = -1; 96 private static final int BC = 97 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE; 98 private static final int MANUAL_SENSOR = 99 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR; 100 private static final int MANUAL_POSTPROC = 101 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING; 102 private static final int RAW = 103 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW; 104 private static final int YUV_REPROCESS = 105 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING; 106 private static final int OPAQUE_REPROCESS = 107 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; 108 private static final int CONSTRAINED_HIGH_SPEED = 109 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO; 110 private static final int MONOCHROME = 111 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME; 112 private static final int HIGH_SPEED_FPS_LOWER_MIN = 30; 113 private static final int HIGH_SPEED_FPS_UPPER_MIN = 120; 114 115 @Override setContext(Context context)116 public void setContext(Context context) { 117 super.setContext(context); 118 mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE); 119 assertNotNull("Can't connect to camera manager", mCameraManager); 120 } 121 122 @Override setUp()123 protected void setUp() throws Exception { 124 super.setUp(); 125 mIds = mCameraManager.getCameraIdList(); 126 mCharacteristics = new ArrayList<>(); 127 mCollector = new CameraErrorCollector(); 128 for (int i = 0; i < mIds.length; i++) { 129 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mIds[i]); 130 assertNotNull(String.format("Can't get camera characteristics from: ID %s", mIds[i]), 131 props); 132 mCharacteristics.add(props); 133 } 134 } 135 136 @Override tearDown()137 protected void tearDown() throws Exception { 138 mCharacteristics = null; 139 140 try { 141 mCollector.verify(); 142 } catch (Throwable e) { 143 // When new Exception(e) is used, exception info will be printed twice. 144 throw new Exception(e.getMessage()); 145 } finally { 146 super.tearDown(); 147 } 148 } 149 150 /** 151 * Test that the available stream configurations contain a few required formats and sizes. 152 */ testAvailableStreamConfigs()153 public void testAvailableStreamConfigs() throws Exception { 154 int counter = 0; 155 for (CameraCharacteristics c : mCharacteristics) { 156 StreamConfigurationMap config = 157 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 158 assertNotNull(String.format("No stream configuration map found for: ID %s", 159 mIds[counter]), config); 160 int[] outputFormats = config.getOutputFormats(); 161 162 int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 163 assertNotNull("android.request.availableCapabilities must never be null", 164 actualCapabilities); 165 166 // Check required formats exist (JPEG, and YUV_420_888). 167 if (!arrayContains(actualCapabilities, BC)) { 168 Log.i(TAG, "Camera " + mIds[counter] + 169 ": BACKWARD_COMPATIBLE capability not supported, skipping test"); 170 continue; 171 } 172 173 assertArrayContains( 174 String.format("No valid YUV_420_888 preview formats found for: ID %s", 175 mIds[counter]), outputFormats, ImageFormat.YUV_420_888); 176 assertArrayContains(String.format("No JPEG image format for: ID %s", 177 mIds[counter]), outputFormats, ImageFormat.JPEG); 178 179 Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888); 180 Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG); 181 Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE); 182 183 CameraTestUtils.assertArrayNotEmpty(yuvSizes, 184 String.format("No sizes for preview format %x for: ID %s", 185 ImageFormat.YUV_420_888, mIds[counter])); 186 187 Rect activeRect = CameraTestUtils.getValueNotNull( 188 c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 189 Size activeArraySize = new Size(activeRect.width(), activeRect.height()); 190 Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); 191 192 if (activeArraySize.getWidth() >= FULLHD.getWidth() && 193 activeArraySize.getHeight() >= FULLHD.getHeight()) { 194 assertArrayContainsAnyOf(String.format( 195 "Required FULLHD size not found for format %x for: ID %s", 196 ImageFormat.JPEG, mIds[counter]), jpegSizes, 197 new Size[] {FULLHD, FULLHD_ALT}); 198 } 199 200 if (activeArraySize.getWidth() >= HD.getWidth() && 201 activeArraySize.getHeight() >= HD.getHeight()) { 202 assertArrayContains(String.format( 203 "Required HD size not found for format %x for: ID %s", 204 ImageFormat.JPEG, mIds[counter]), jpegSizes, HD); 205 } 206 207 if (activeArraySize.getWidth() >= VGA.getWidth() && 208 activeArraySize.getHeight() >= VGA.getHeight()) { 209 assertArrayContains(String.format( 210 "Required VGA size not found for format %x for: ID %s", 211 ImageFormat.JPEG, mIds[counter]), jpegSizes, VGA); 212 } 213 214 if (activeArraySize.getWidth() >= QVGA.getWidth() && 215 activeArraySize.getHeight() >= QVGA.getHeight()) { 216 assertArrayContains(String.format( 217 "Required QVGA size not found for format %x for: ID %s", 218 ImageFormat.JPEG, mIds[counter]), jpegSizes, QVGA); 219 } 220 221 ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes)); 222 ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes)); 223 ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes)); 224 boolean isExternalCamera = (hwLevel == 225 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL); 226 Size maxVideoSize = null; 227 if (isExternalCamera) { 228 // TODO: for now, use FULLHD 30 as largest possible video size 229 List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes( 230 mIds[counter], mCameraManager, FULLHD); 231 for (Size sz : videoSizes) { 232 long minFrameDuration = config.getOutputMinFrameDuration( 233 android.media.MediaRecorder.class, sz); 234 // Give some margin for rounding error 235 if (minFrameDuration > (1e9 / 30.1)) { 236 maxVideoSize = sz; 237 break; 238 } 239 } 240 } else { 241 int cameraId = Integer.valueOf(mIds[counter]); 242 CamcorderProfile maxVideoProfile = CamcorderProfile.get( 243 cameraId, CamcorderProfile.QUALITY_HIGH); 244 maxVideoSize = new Size( 245 maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight); 246 } 247 if (maxVideoSize == null) { 248 fail("Camera " + mIds[counter] + " does not support any 30fps video output"); 249 } 250 251 // Handle FullHD special case first 252 if (jpegSizesList.contains(FULLHD)) { 253 if (hwLevel >= LEVEL_3 || hwLevel == FULL || (hwLevel == LIMITED && 254 maxVideoSize.getWidth() >= FULLHD.getWidth() && 255 maxVideoSize.getHeight() >= FULLHD.getHeight())) { 256 boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) || 257 yuvSizesList.contains(FULLHD_ALT); 258 boolean privateSupportFullHD = privateSizesList.contains(FULLHD) || 259 privateSizesList.contains(FULLHD_ALT); 260 assertTrue("Full device FullHD YUV size not found", yuvSupportFullHD); 261 assertTrue("Full device FullHD PRIVATE size not found", privateSupportFullHD); 262 } 263 // remove all FullHD or FullHD_Alt sizes for the remaining of the test 264 jpegSizesList.remove(FULLHD); 265 jpegSizesList.remove(FULLHD_ALT); 266 } 267 268 // Check all sizes other than FullHD 269 if (hwLevel == LIMITED) { 270 // Remove all jpeg sizes larger than max video size 271 ArrayList<Size> toBeRemoved = new ArrayList<>(); 272 for (Size size : jpegSizesList) { 273 if (size.getWidth() >= maxVideoSize.getWidth() && 274 size.getHeight() >= maxVideoSize.getHeight()) { 275 toBeRemoved.add(size); 276 } 277 } 278 jpegSizesList.removeAll(toBeRemoved); 279 } 280 281 if (hwLevel >= LEVEL_3 || hwLevel == FULL || hwLevel == LIMITED) { 282 if (!yuvSizesList.containsAll(jpegSizesList)) { 283 for (Size s : jpegSizesList) { 284 if (!yuvSizesList.contains(s)) { 285 fail("Size " + s + " not found in YUV format"); 286 } 287 } 288 } 289 } 290 291 if (!privateSizesList.containsAll(yuvSizesList)) { 292 for (Size s : yuvSizesList) { 293 if (!privateSizesList.contains(s)) { 294 fail("Size " + s + " not found in PRIVATE format"); 295 } 296 } 297 } 298 299 counter++; 300 } 301 } 302 303 /** 304 * Test {@link CameraCharacteristics#getKeys} 305 */ testKeys()306 public void testKeys() { 307 int counter = 0; 308 for (CameraCharacteristics c : mCharacteristics) { 309 mCollector.setCameraId(mIds[counter]); 310 311 if (VERBOSE) { 312 Log.v(TAG, "testKeys - testing characteristics for camera " + mIds[counter]); 313 } 314 315 List<CameraCharacteristics.Key<?>> allKeys = c.getKeys(); 316 assertNotNull("Camera characteristics keys must not be null", allKeys); 317 assertFalse("Camera characteristics keys must have at least 1 key", 318 allKeys.isEmpty()); 319 320 for (CameraCharacteristics.Key<?> key : allKeys) { 321 assertKeyPrefixValid(key.getName()); 322 323 // All characteristics keys listed must never be null 324 mCollector.expectKeyValueNotNull(c, key); 325 326 // TODO: add a check that key must not be @hide 327 } 328 329 /* 330 * List of keys that must be present in camera characteristics (not null). 331 * 332 * Keys for LIMITED, FULL devices might be available despite lacking either 333 * the hardware level or the capability. This is *OK*. This only lists the 334 * *minimal* requirements for a key to be listed. 335 * 336 * LEGACY devices are a bit special since they map to api1 devices, so we know 337 * for a fact most keys are going to be illegal there so they should never be 338 * available. 339 * 340 * For LIMITED-level keys, if the level is >= LIMITED, then the capabilities are used to 341 * do the actual checking. 342 */ 343 { 344 // (Key Name) (HW Level) (Capabilities <Var-Arg>) 345 expectKeyAvailable(c, CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES , OPT , BC ); 346 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_MODES , OPT , BC ); 347 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES , OPT , BC ); 348 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES , OPT , BC ); 349 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES , OPT , BC ); 350 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE , OPT , BC ); 351 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP , OPT , BC ); 352 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE , OPT , BC ); 353 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES , OPT , BC ); 354 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS , OPT , BC ); 355 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES , OPT , BC ); 356 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES , OPT , BC ); 357 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES , OPT , BC ); 358 expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE , OPT , BC ); 359 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE , OPT , BC ); 360 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF , OPT , BC ); 361 expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB , OPT , BC ); 362 expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES , FULL , NONE ); 363 expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE , OPT , BC ); 364 expectKeyAvailable(c, CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES , OPT , RAW ); 365 expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL , OPT , BC ); 366 expectKeyAvailable(c, CameraCharacteristics.INFO_VERSION , OPT , NONE ); 367 expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES , OPT , BC ); 368 expectKeyAvailable(c, CameraCharacteristics.LENS_FACING , OPT , BC ); 369 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES , FULL , MANUAL_SENSOR ); 370 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES , FULL , MANUAL_SENSOR ); 371 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION , LIMITED , BC ); 372 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION , LIMITED , MANUAL_SENSOR ); 373 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE , LIMITED , BC ); 374 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE , LIMITED , BC ); 375 expectKeyAvailable(c, CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES , OPT , BC ); 376 expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES , OPT , BC ); 377 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS , OPT , YUV_REPROCESS, OPAQUE_REPROCESS); 378 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP , OPT , CONSTRAINED_HIGH_SPEED); 379 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC , OPT , BC ); 380 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING , OPT , BC ); 381 expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW , OPT , BC ); 382 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT , OPT , BC ); 383 expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH , OPT , BC ); 384 expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM , OPT , BC ); 385 expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP , OPT , BC ); 386 expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE , OPT , BC ); 387 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN , FULL , RAW ); 388 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1 , OPT , RAW ); 389 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1 , OPT , RAW ); 390 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1 , OPT , RAW ); 391 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE , OPT , BC, RAW ); 392 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT , FULL , RAW ); 393 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE , FULL , MANUAL_SENSOR ); 394 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION , FULL , MANUAL_SENSOR ); 395 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE , OPT , BC ); 396 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE , FULL , MANUAL_SENSOR ); 397 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL , OPT , RAW ); 398 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE , OPT , BC ); 399 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY , FULL , MANUAL_SENSOR ); 400 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION , OPT , BC ); 401 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1 , OPT , RAW ); 402 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES , LIMITED , MANUAL_POSTPROC, RAW ); 403 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES , OPT , BC ); 404 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES , OPT , RAW ); 405 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, LIMITED , RAW ); 406 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT , OPT , BC ); 407 expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY , OPT , BC ); 408 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES , FULL , MANUAL_POSTPROC ); 409 expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS , FULL , MANUAL_POSTPROC ); 410 411 // Future: Use column editors for modifying above, ignore line length to keep 1 key per line 412 413 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list 414 } 415 416 // Only check for these if the second reference illuminant is included 417 if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) { 418 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2 , OPT , RAW ); 419 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2 , OPT , RAW ); 420 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2 , OPT , RAW ); 421 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2 , OPT , RAW ); 422 } 423 424 // Required key if any of RAW format output is supported 425 StreamConfigurationMap config = 426 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 427 assertNotNull(String.format("No stream configuration map found for: ID %s", 428 mIds[counter]), config); 429 if (config.isOutputSupportedFor(ImageFormat.RAW_SENSOR) || 430 config.isOutputSupportedFor(ImageFormat.RAW10) || 431 config.isOutputSupportedFor(ImageFormat.RAW12) || 432 config.isOutputSupportedFor(ImageFormat.RAW_PRIVATE)) { 433 expectKeyAvailable(c, 434 CameraCharacteristics.CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, OPT, BC); 435 } 436 437 // External Camera exceptional keys 438 Integer hwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); 439 boolean isExternalCamera = (hwLevel == 440 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL); 441 if (!isExternalCamera) { 442 expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS , OPT , BC ); 443 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES , OPT , BC ); 444 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE , OPT , BC ); 445 } 446 447 448 // Verify version is a short text string. 449 if (allKeys.contains(CameraCharacteristics.INFO_VERSION)) { 450 final String TEXT_REGEX = "[\\p{Alnum}\\p{Punct}\\p{Space}]*"; 451 final int MAX_VERSION_LENGTH = 256; 452 453 String version = c.get(CameraCharacteristics.INFO_VERSION); 454 mCollector.expectTrue("Version contains non-text characters: " + version, 455 version.matches(TEXT_REGEX)); 456 mCollector.expectLessOrEqual("Version too long: " + version, MAX_VERSION_LENGTH, 457 version.length()); 458 } 459 460 counter++; 461 } 462 } 463 464 /** 465 * Test values for static metadata used by the RAW capability. 466 */ testStaticRawCharacteristics()467 public void testStaticRawCharacteristics() { 468 int counter = 0; 469 for (CameraCharacteristics c : mCharacteristics) { 470 int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 471 assertNotNull("android.request.availableCapabilities must never be null", 472 actualCapabilities); 473 if (!arrayContains(actualCapabilities, 474 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 475 Log.i(TAG, "RAW capability is not supported in camera " + counter++ + 476 ". Skip the test."); 477 continue; 478 } 479 480 Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); 481 if (actualHwLevel != null && actualHwLevel == FULL) { 482 mCollector.expectKeyValueContains(c, 483 CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, 484 CameraCharacteristics.HOT_PIXEL_MODE_FAST); 485 } 486 mCollector.expectKeyValueContains(c, 487 CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, false); 488 mCollector.expectKeyValueGreaterThan(c, CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL, 489 MIN_ALLOWABLE_WHITELEVEL); 490 491 mCollector.expectKeyValueIsIn(c, 492 CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, 493 CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB, 494 CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG, 495 CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG, 496 CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR); 497 // TODO: SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB isn't supported yet. 498 499 mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1, 500 CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT, 501 CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN); 502 // Only check the range if the second reference illuminant is avaliable 503 if (c.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2) != null) { 504 mCollector.expectKeyValueInRange(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2, 505 (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT, 506 (byte) CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN); 507 } 508 509 Rational[] zeroes = new Rational[9]; 510 Arrays.fill(zeroes, Rational.ZERO); 511 512 ColorSpaceTransform zeroed = new ColorSpaceTransform(zeroes); 513 mCollector.expectNotEquals("Forward Matrix1 should not contain all zeroes.", zeroed, 514 c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1)); 515 mCollector.expectNotEquals("Forward Matrix2 should not contain all zeroes.", zeroed, 516 c.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2)); 517 mCollector.expectNotEquals("Calibration Transform1 should not contain all zeroes.", 518 zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1)); 519 mCollector.expectNotEquals("Calibration Transform2 should not contain all zeroes.", 520 zeroed, c.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2)); 521 mCollector.expectNotEquals("Color Transform1 should not contain all zeroes.", 522 zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1)); 523 mCollector.expectNotEquals("Color Transform2 should not contain all zeroes.", 524 zeroed, c.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2)); 525 526 BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c, 527 CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN); 528 if (blackLevel != null) { 529 String blackLevelPatternString = blackLevel.toString(); 530 if (VERBOSE) { 531 Log.v(TAG, "Black level pattern: " + blackLevelPatternString); 532 } 533 int[] blackLevelPattern = new int[BlackLevelPattern.COUNT]; 534 blackLevel.copyTo(blackLevelPattern, /*offset*/0); 535 Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL); 536 if (whitelevel != null) { 537 mCollector.expectValuesInRange("BlackLevelPattern", blackLevelPattern, 0, 538 whitelevel); 539 } else { 540 mCollector.addMessage( 541 "No WhiteLevel available, cannot check BlackLevelPattern range."); 542 } 543 } 544 545 // TODO: profileHueSatMap, and profileToneCurve aren't supported yet. 546 counter++; 547 } 548 } 549 550 /** 551 * Test values for the available session keys. 552 */ testStaticSessionKeys()553 public void testStaticSessionKeys() throws Exception { 554 for (CameraCharacteristics c : mCharacteristics) { 555 List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys(); 556 if (availableSessionKeys == null) { 557 continue; 558 } 559 List<CaptureRequest.Key<?>> availableRequestKeys = c.getAvailableCaptureRequestKeys(); 560 561 //Every session key should be part of the available request keys 562 for (CaptureRequest.Key<?> key : availableSessionKeys) { 563 assertTrue("Session key:" + key.getName() + " not present in the available capture " 564 + "request keys!", availableRequestKeys.contains(key)); 565 } 566 } 567 } 568 569 /** 570 * Test values for static metadata used by the BURST capability. 571 */ testStaticBurstCharacteristics()572 public void testStaticBurstCharacteristics() throws Exception { 573 int counter = 0; 574 final float SIZE_ERROR_MARGIN = 0.03f; 575 for (CameraCharacteristics c : mCharacteristics) { 576 int[] actualCapabilities = CameraTestUtils.getValueNotNull( 577 c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 578 579 // Check if the burst capability is defined 580 boolean haveBurstCapability = arrayContains(actualCapabilities, 581 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE); 582 boolean haveBC = arrayContains(actualCapabilities, 583 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE); 584 585 if(haveBurstCapability && !haveBC) { 586 fail("Must have BACKWARD_COMPATIBLE capability if BURST_CAPTURE capability is defined"); 587 } 588 589 if (!haveBC) continue; 590 591 StreamConfigurationMap config = 592 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 593 assertNotNull(String.format("No stream configuration map found for: ID %s", 594 mIds[counter]), config); 595 Rect activeRect = CameraTestUtils.getValueNotNull( 596 c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 597 Size sensorSize = new Size(activeRect.width(), activeRect.height()); 598 599 // Ensure that max YUV size matches max JPEG size 600 Size maxYuvSize = CameraTestUtils.getMaxSize( 601 config.getOutputSizes(ImageFormat.YUV_420_888)); 602 Size maxFastYuvSize = maxYuvSize; 603 604 Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888); 605 if (haveBurstCapability && slowYuvSizes != null && slowYuvSizes.length > 0) { 606 Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes); 607 maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize}); 608 } 609 610 Size maxJpegSize = CameraTestUtils.getMaxSize(CameraTestUtils.getSupportedSizeForFormat( 611 ImageFormat.JPEG, mIds[counter], mCameraManager)); 612 613 boolean haveMaxYuv = maxYuvSize != null ? 614 (maxJpegSize.getWidth() <= maxYuvSize.getWidth() && 615 maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false; 616 617 float croppedWidth = (float)sensorSize.getWidth(); 618 float croppedHeight = (float)sensorSize.getHeight(); 619 float sensorAspectRatio = (float)sensorSize.getWidth() / (float)sensorSize.getHeight(); 620 float maxYuvAspectRatio = (float)maxYuvSize.getWidth() / (float)maxYuvSize.getHeight(); 621 if (sensorAspectRatio < maxYuvAspectRatio) { 622 croppedHeight = (float)sensorSize.getWidth() / maxYuvAspectRatio; 623 } else if (sensorAspectRatio > maxYuvAspectRatio) { 624 croppedWidth = (float)sensorSize.getHeight() * maxYuvAspectRatio; 625 } 626 Size croppedSensorSize = new Size((int)croppedWidth, (int)croppedHeight); 627 628 boolean maxYuvMatchSensor = 629 (maxYuvSize.getWidth() <= croppedSensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) && 630 maxYuvSize.getWidth() >= croppedSensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) && 631 maxYuvSize.getHeight() <= croppedSensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) && 632 maxYuvSize.getHeight() >= croppedSensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN)); 633 634 // No need to do null check since framework will generate the key if HAL don't supply 635 boolean haveAeLock = CameraTestUtils.getValueNotNull( 636 c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE); 637 boolean haveAwbLock = CameraTestUtils.getValueNotNull( 638 c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE); 639 640 // Ensure that max YUV output is fast enough - needs to be at least 10 fps 641 642 long maxYuvRate = 643 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize); 644 final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps 645 boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS; 646 647 // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps 648 649 long maxFastYuvRate = 650 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize); 651 final long MIN_8MP_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps 652 boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS; 653 654 final int SIZE_8MP_BOUND = 8000000; 655 boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) > 656 SIZE_8MP_BOUND; 657 658 // Ensure that there's an FPS range that's fast enough to capture at above 659 // minFrameDuration, for full-auto bursts at the fast resolutions 660 Range[] fpsRanges = CameraTestUtils.getValueNotNull( 661 c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); 662 float minYuvFps = 1.f / maxFastYuvRate; 663 664 boolean haveFastAeTargetFps = false; 665 for (Range<Integer> r : fpsRanges) { 666 if (r.getLower() >= minYuvFps) { 667 haveFastAeTargetFps = true; 668 break; 669 } 670 } 671 672 // Ensure that maximum sync latency is small enough for fast setting changes, even if 673 // it's not quite per-frame 674 675 Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY); 676 assertNotNull(String.format("No sync latency declared for ID %s", mIds[counter]), 677 maxSyncLatencyValue); 678 679 int maxSyncLatency = maxSyncLatencyValue; 680 final long MAX_LATENCY_BOUND = 4; 681 boolean haveFastSyncLatency = 682 (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0); 683 684 if (haveBurstCapability) { 685 assertTrue("Must have slow YUV size array when BURST_CAPTURE capability is defined!", 686 slowYuvSizes != null); 687 assertTrue( 688 String.format("BURST-capable camera device %s does not have maximum YUV " + 689 "size that is at least max JPEG size", 690 mIds[counter]), 691 haveMaxYuv); 692 assertTrue( 693 String.format("BURST-capable camera device %s max-resolution " + 694 "YUV frame rate is too slow" + 695 "(%d ns min frame duration reported, less than %d ns expected)", 696 mIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS), 697 haveMaxYuvRate); 698 assertTrue( 699 String.format("BURST-capable camera device %s >= 8MP YUV output " + 700 "frame rate is too slow" + 701 "(%d ns min frame duration reported, less than %d ns expected)", 702 mIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS), 703 haveFastYuvRate); 704 assertTrue( 705 String.format("BURST-capable camera device %s does not list an AE target " + 706 " FPS range with min FPS >= %f, for full-AUTO bursts", 707 mIds[counter], minYuvFps), 708 haveFastAeTargetFps); 709 assertTrue( 710 String.format("BURST-capable camera device %s YUV sync latency is too long" + 711 "(%d frames reported, [0, %d] frames expected)", 712 mIds[counter], maxSyncLatency, MAX_LATENCY_BOUND), 713 haveFastSyncLatency); 714 assertTrue( 715 String.format("BURST-capable camera device %s max YUV size %s should be" + 716 "close to active array size %s or cropped active array size %s", 717 mIds[counter], maxYuvSize.toString(), sensorSize.toString(), 718 croppedSensorSize.toString()), 719 maxYuvMatchSensor); 720 assertTrue( 721 String.format("BURST-capable camera device %s does not support AE lock", 722 mIds[counter]), 723 haveAeLock); 724 assertTrue( 725 String.format("BURST-capable camera device %s does not support AWB lock", 726 mIds[counter]), 727 haveAwbLock); 728 } else { 729 assertTrue("Must have null slow YUV size array when no BURST_CAPTURE capability!", 730 slowYuvSizes == null); 731 assertTrue( 732 String.format("Camera device %s has all the requirements for BURST" + 733 " capability but does not report it!", mIds[counter]), 734 !(haveMaxYuv && haveMaxYuvRate && haveFastYuvRate && haveFastAeTargetFps && 735 haveFastSyncLatency && maxYuvMatchSensor && 736 haveAeLock && haveAwbLock)); 737 } 738 739 counter++; 740 } 741 } 742 743 /** 744 * Check reprocessing capabilities. 745 */ testReprocessingCharacteristics()746 public void testReprocessingCharacteristics() { 747 int counter = 0; 748 749 for (CameraCharacteristics c : mCharacteristics) { 750 Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mIds[counter]); 751 752 int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 753 assertNotNull("android.request.availableCapabilities must never be null", 754 capabilities); 755 boolean supportYUV = arrayContains(capabilities, 756 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING); 757 boolean supportOpaque = arrayContains(capabilities, 758 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING); 759 StreamConfigurationMap configs = 760 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 761 Integer maxNumInputStreams = 762 c.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS); 763 int[] availableEdgeModes = c.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES); 764 int[] availableNoiseReductionModes = c.get( 765 CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES); 766 767 int[] inputFormats = configs.getInputFormats(); 768 769 boolean supportZslEdgeMode = false; 770 boolean supportZslNoiseReductionMode = false; 771 boolean supportHiQNoiseReductionMode = false; 772 boolean supportHiQEdgeMode = false; 773 774 if (availableEdgeModes != null) { 775 supportZslEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)). 776 contains(CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG); 777 supportHiQEdgeMode = Arrays.asList(CameraTestUtils.toObject(availableEdgeModes)). 778 contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY); 779 } 780 781 if (availableNoiseReductionModes != null) { 782 supportZslNoiseReductionMode = Arrays.asList( 783 CameraTestUtils.toObject(availableNoiseReductionModes)).contains( 784 CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG); 785 supportHiQNoiseReductionMode = Arrays.asList( 786 CameraTestUtils.toObject(availableNoiseReductionModes)).contains( 787 CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); 788 } 789 790 if (supportYUV || supportOpaque) { 791 mCollector.expectTrue("Support reprocessing but max number of input stream is " + 792 maxNumInputStreams, maxNumInputStreams != null && maxNumInputStreams > 0); 793 mCollector.expectTrue("Support reprocessing but EDGE_MODE_ZERO_SHUTTER_LAG is " + 794 "not supported", supportZslEdgeMode); 795 mCollector.expectTrue("Support reprocessing but " + 796 "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is not supported", 797 supportZslNoiseReductionMode); 798 799 // For reprocessing, if we only require OFF and ZSL mode, it will be just like jpeg 800 // encoding. We implicitly require FAST to make reprocessing meaningful, which means 801 // that we also require HIGH_QUALITY. 802 mCollector.expectTrue("Support reprocessing but EDGE_MODE_HIGH_QUALITY is " + 803 "not supported", supportHiQEdgeMode); 804 mCollector.expectTrue("Support reprocessing but " + 805 "NOISE_REDUCTION_MODE_HIGH_QUALITY is not supported", 806 supportHiQNoiseReductionMode); 807 808 // Verify mandatory input formats are supported 809 mCollector.expectTrue("YUV_420_888 input must be supported for YUV reprocessing", 810 !supportYUV || arrayContains(inputFormats, ImageFormat.YUV_420_888)); 811 mCollector.expectTrue("PRIVATE input must be supported for OPAQUE reprocessing", 812 !supportOpaque || arrayContains(inputFormats, ImageFormat.PRIVATE)); 813 814 // max capture stall must be reported if one of the reprocessing is supported. 815 final int MAX_ALLOWED_STALL_FRAMES = 4; 816 Integer maxCaptureStall = c.get(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL); 817 mCollector.expectTrue("max capture stall must be non-null and no larger than " 818 + MAX_ALLOWED_STALL_FRAMES, 819 maxCaptureStall != null && maxCaptureStall <= MAX_ALLOWED_STALL_FRAMES); 820 821 for (int input : inputFormats) { 822 // Verify mandatory output formats are supported 823 int[] outputFormats = configs.getValidOutputFormatsForInput(input); 824 mCollector.expectTrue("YUV_420_888 output must be supported for reprocessing", 825 arrayContains(outputFormats, ImageFormat.YUV_420_888)); 826 mCollector.expectTrue("JPEG output must be supported for reprocessing", 827 arrayContains(outputFormats, ImageFormat.JPEG)); 828 829 // Verify camera can output the reprocess input formats and sizes. 830 Size[] inputSizes = configs.getInputSizes(input); 831 Size[] outputSizes = configs.getOutputSizes(input); 832 Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input); 833 mCollector.expectTrue("no input size supported for format " + input, 834 inputSizes.length > 0); 835 mCollector.expectTrue("no output size supported for format " + input, 836 outputSizes.length > 0); 837 838 for (Size inputSize : inputSizes) { 839 mCollector.expectTrue("Camera must be able to output the supported " + 840 "reprocessing input size", 841 arrayContains(outputSizes, inputSize) || 842 arrayContains(highResOutputSizes, inputSize)); 843 } 844 } 845 } else { 846 mCollector.expectTrue("Doesn't support reprocessing but report input format: " + 847 Arrays.toString(inputFormats), inputFormats.length == 0); 848 mCollector.expectTrue("Doesn't support reprocessing but max number of input " + 849 "stream is " + maxNumInputStreams, 850 maxNumInputStreams == null || maxNumInputStreams == 0); 851 mCollector.expectTrue("Doesn't support reprocessing but " + 852 "EDGE_MODE_ZERO_SHUTTER_LAG is supported", !supportZslEdgeMode); 853 mCollector.expectTrue("Doesn't support reprocessing but " + 854 "NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG is supported", 855 !supportZslNoiseReductionMode); 856 } 857 counter++; 858 } 859 } 860 861 /** 862 * Check depth output capability 863 */ testDepthOutputCharacteristics()864 public void testDepthOutputCharacteristics() { 865 int counter = 0; 866 867 for (CameraCharacteristics c : mCharacteristics) { 868 Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mIds[counter]); 869 870 int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 871 assertNotNull("android.request.availableCapabilities must never be null", 872 capabilities); 873 boolean supportDepth = arrayContains(capabilities, 874 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT); 875 StreamConfigurationMap configs = 876 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 877 878 int[] outputFormats = configs.getOutputFormats(); 879 boolean hasDepth16 = arrayContains(outputFormats, ImageFormat.DEPTH16); 880 881 Boolean depthIsExclusive = c.get(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE); 882 883 float[] poseRotation = c.get(CameraCharacteristics.LENS_POSE_ROTATION); 884 float[] poseTranslation = c.get(CameraCharacteristics.LENS_POSE_TRANSLATION); 885 Integer poseReference = c.get(CameraCharacteristics.LENS_POSE_REFERENCE); 886 float[] cameraIntrinsics = c.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION); 887 float[] distortion = getLensDistortion(c); 888 Rect precorrectionArray = c.get( 889 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 890 891 if (supportDepth) { 892 mCollector.expectTrue("Supports DEPTH_OUTPUT but does not support DEPTH16", 893 hasDepth16); 894 if (hasDepth16) { 895 Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16); 896 mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!", 897 depthSizes != null && depthSizes.length > 0); 898 if (depthSizes != null) { 899 for (Size depthSize : depthSizes) { 900 mCollector.expectTrue("All depth16 sizes must be positive", 901 depthSize.getWidth() > 0 && depthSize.getHeight() > 0); 902 long minFrameDuration = configs.getOutputMinFrameDuration( 903 ImageFormat.DEPTH16, depthSize); 904 mCollector.expectTrue("Non-negative min frame duration for depth size " 905 + depthSize + " expected, got " + minFrameDuration, 906 minFrameDuration >= 0); 907 long stallDuration = configs.getOutputStallDuration( 908 ImageFormat.DEPTH16, depthSize); 909 mCollector.expectTrue("Non-negative stall duration for depth size " 910 + depthSize + " expected, got " + stallDuration, 911 stallDuration >= 0); 912 } 913 } 914 } 915 if (arrayContains(outputFormats, ImageFormat.DEPTH_POINT_CLOUD)) { 916 Size[] depthCloudSizes = configs.getOutputSizes(ImageFormat.DEPTH_POINT_CLOUD); 917 mCollector.expectTrue("Supports DEPTH_POINT_CLOUD " + 918 "but no sizes for DEPTH_POINT_CLOUD supported!", 919 depthCloudSizes != null && depthCloudSizes.length > 0); 920 if (depthCloudSizes != null) { 921 for (Size depthCloudSize : depthCloudSizes) { 922 mCollector.expectTrue("All depth point cloud sizes must be nonzero", 923 depthCloudSize.getWidth() > 0); 924 mCollector.expectTrue("All depth point cloud sizes must be N x 1", 925 depthCloudSize.getHeight() == 1); 926 long minFrameDuration = configs.getOutputMinFrameDuration( 927 ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize); 928 mCollector.expectTrue("Non-negative min frame duration for depth size " 929 + depthCloudSize + " expected, got " + minFrameDuration, 930 minFrameDuration >= 0); 931 long stallDuration = configs.getOutputStallDuration( 932 ImageFormat.DEPTH_POINT_CLOUD, depthCloudSize); 933 mCollector.expectTrue("Non-negative stall duration for depth size " 934 + depthCloudSize + " expected, got " + stallDuration, 935 stallDuration >= 0); 936 } 937 } 938 } 939 940 mCollector.expectTrue("Supports DEPTH_OUTPUT but DEPTH_IS_EXCLUSIVE is not defined", 941 depthIsExclusive != null); 942 943 verifyLensCalibration(poseRotation, poseTranslation, poseReference, 944 cameraIntrinsics, distortion, precorrectionArray); 945 946 } else { 947 boolean hasFields = 948 hasDepth16 && (poseTranslation != null) && 949 (poseRotation != null) && (cameraIntrinsics != null) && 950 (distortion != null) && (depthIsExclusive != null); 951 952 mCollector.expectTrue( 953 "All necessary depth fields defined, but DEPTH_OUTPUT capability is not listed", 954 !hasFields); 955 } 956 counter++; 957 } 958 } 959 verifyLensCalibration(float[] poseRotation, float[] poseTranslation, Integer poseReference, float[] cameraIntrinsics, float[] distortion, Rect precorrectionArray)960 private void verifyLensCalibration(float[] poseRotation, float[] poseTranslation, 961 Integer poseReference, float[] cameraIntrinsics, float[] distortion, 962 Rect precorrectionArray) { 963 964 mCollector.expectTrue( 965 "LENS_POSE_ROTATION not right size", 966 poseRotation != null && poseRotation.length == 4); 967 mCollector.expectTrue( 968 "LENS_POSE_TRANSLATION not right size", 969 poseTranslation != null && poseTranslation.length == 3); 970 mCollector.expectTrue( 971 "LENS_POSE_REFERENCE is not defined", 972 poseReference != null); 973 mCollector.expectTrue( 974 "LENS_INTRINSIC_CALIBRATION not right size", 975 cameraIntrinsics != null && cameraIntrinsics.length == 5); 976 mCollector.expectTrue( 977 "LENS_DISTORTION not right size", 978 distortion != null && distortion.length == 6); 979 980 if (poseRotation != null && poseRotation.length == 4) { 981 float normSq = 982 poseRotation[0] * poseRotation[0] + 983 poseRotation[1] * poseRotation[1] + 984 poseRotation[2] * poseRotation[2] + 985 poseRotation[3] * poseRotation[3]; 986 mCollector.expectTrue( 987 "LENS_POSE_ROTATION quarternion must be unit-length", 988 0.9999f < normSq && normSq < 1.0001f); 989 990 // TODO: Cross-validate orientation/facing and poseRotation 991 } 992 993 if (poseTranslation != null && poseTranslation.length == 3) { 994 float normSq = 995 poseTranslation[0] * poseTranslation[0] + 996 poseTranslation[1] * poseTranslation[1] + 997 poseTranslation[2] * poseTranslation[2]; 998 mCollector.expectTrue("Pose translation is larger than 1 m", 999 normSq < 1.f); 1000 } 1001 1002 if (poseReference != null) { 1003 int ref = poseReference; 1004 boolean validReference = false; 1005 switch (ref) { 1006 case CameraCharacteristics.LENS_POSE_REFERENCE_PRIMARY_CAMERA: 1007 case CameraCharacteristics.LENS_POSE_REFERENCE_GYROSCOPE: 1008 // Allowed values 1009 validReference = true; 1010 break; 1011 default: 1012 } 1013 mCollector.expectTrue("POSE_REFERENCE has unknown value", validReference); 1014 } 1015 1016 mCollector.expectTrue("Does not have precorrection active array defined", 1017 precorrectionArray != null); 1018 1019 if (cameraIntrinsics != null && precorrectionArray != null) { 1020 float fx = cameraIntrinsics[0]; 1021 float fy = cameraIntrinsics[1]; 1022 float cx = cameraIntrinsics[2]; 1023 float cy = cameraIntrinsics[3]; 1024 float s = cameraIntrinsics[4]; 1025 mCollector.expectTrue("Optical center expected to be within precorrection array", 1026 0 <= cx && cx < precorrectionArray.width() && 1027 0 <= cy && cy < precorrectionArray.height()); 1028 1029 // TODO: Verify focal lengths and skew are reasonable 1030 } 1031 1032 if (distortion != null) { 1033 // TODO: Verify radial distortion 1034 } 1035 1036 } 1037 1038 /** 1039 * Cross-check StreamConfigurationMap output 1040 */ testStreamConfigurationMap()1041 public void testStreamConfigurationMap() throws Exception { 1042 int counter = 0; 1043 for (CameraCharacteristics c : mCharacteristics) { 1044 Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]); 1045 StreamConfigurationMap config = 1046 c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1047 assertNotNull(String.format("No stream configuration map found for: ID %s", 1048 mIds[counter]), config); 1049 1050 int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 1051 assertNotNull("android.request.availableCapabilities must never be null", 1052 actualCapabilities); 1053 1054 if (arrayContains(actualCapabilities, BC)) { 1055 assertTrue("ImageReader must be supported", 1056 config.isOutputSupportedFor(android.media.ImageReader.class)); 1057 assertTrue("MediaRecorder must be supported", 1058 config.isOutputSupportedFor(android.media.MediaRecorder.class)); 1059 assertTrue("MediaCodec must be supported", 1060 config.isOutputSupportedFor(android.media.MediaCodec.class)); 1061 assertTrue("Allocation must be supported", 1062 config.isOutputSupportedFor(android.renderscript.Allocation.class)); 1063 assertTrue("SurfaceHolder must be supported", 1064 config.isOutputSupportedFor(android.view.SurfaceHolder.class)); 1065 assertTrue("SurfaceTexture must be supported", 1066 config.isOutputSupportedFor(android.graphics.SurfaceTexture.class)); 1067 1068 assertTrue("YUV_420_888 must be supported", 1069 config.isOutputSupportedFor(ImageFormat.YUV_420_888)); 1070 assertTrue("JPEG must be supported", 1071 config.isOutputSupportedFor(ImageFormat.JPEG)); 1072 } else { 1073 assertTrue("YUV_420_88 may not be supported if BACKWARD_COMPATIBLE capability is not listed", 1074 !config.isOutputSupportedFor(ImageFormat.YUV_420_888)); 1075 assertTrue("JPEG may not be supported if BACKWARD_COMPATIBLE capability is not listed", 1076 !config.isOutputSupportedFor(ImageFormat.JPEG)); 1077 } 1078 1079 // Check RAW 1080 1081 if (arrayContains(actualCapabilities, 1082 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 1083 assertTrue("RAW_SENSOR must be supported if RAW capability is advertised", 1084 config.isOutputSupportedFor(ImageFormat.RAW_SENSOR)); 1085 } 1086 1087 // Cross check public formats and sizes 1088 1089 int[] supportedFormats = config.getOutputFormats(); 1090 for (int format : supportedFormats) { 1091 assertTrue("Format " + format + " fails cross check", 1092 config.isOutputSupportedFor(format)); 1093 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes( 1094 Arrays.asList(config.getOutputSizes(format)), /*ascending*/true); 1095 if (arrayContains(actualCapabilities, 1096 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) { 1097 supportedSizes.addAll( 1098 Arrays.asList(config.getHighResolutionOutputSizes(format))); 1099 supportedSizes = CameraTestUtils.getAscendingOrderSizes( 1100 supportedSizes, /*ascending*/true); 1101 } 1102 assertTrue("Supported format " + format + " has no sizes listed", 1103 supportedSizes.size() > 0); 1104 for (int i = 0; i < supportedSizes.size(); i++) { 1105 Size size = supportedSizes.get(i); 1106 if (VERBOSE) { 1107 Log.v(TAG, 1108 String.format("Testing camera %s, format %d, size %s", 1109 mIds[counter], format, size.toString())); 1110 } 1111 1112 long stallDuration = config.getOutputStallDuration(format, size); 1113 switch(format) { 1114 case ImageFormat.YUV_420_888: 1115 assertTrue("YUV_420_888 may not have a non-zero stall duration", 1116 stallDuration == 0); 1117 break; 1118 case ImageFormat.JPEG: 1119 case ImageFormat.RAW_SENSOR: 1120 final float TOLERANCE_FACTOR = 2.0f; 1121 long prevDuration = 0; 1122 if (i > 0) { 1123 prevDuration = config.getOutputStallDuration( 1124 format, supportedSizes.get(i - 1)); 1125 } 1126 long nextDuration = Long.MAX_VALUE; 1127 if (i < (supportedSizes.size() - 1)) { 1128 nextDuration = config.getOutputStallDuration( 1129 format, supportedSizes.get(i + 1)); 1130 } 1131 long curStallDuration = config.getOutputStallDuration(format, size); 1132 // Stall duration should be in a reasonable range: larger size should 1133 // normally have larger stall duration. 1134 mCollector.expectInRange("Stall duration (format " + format + 1135 " and size " + size + ") is not in the right range", 1136 curStallDuration, 1137 (long) (prevDuration / TOLERANCE_FACTOR), 1138 (long) (nextDuration * TOLERANCE_FACTOR)); 1139 break; 1140 default: 1141 assertTrue("Negative stall duration for format " + format, 1142 stallDuration >= 0); 1143 break; 1144 } 1145 long minDuration = config.getOutputMinFrameDuration(format, size); 1146 if (arrayContains(actualCapabilities, 1147 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 1148 assertTrue("MANUAL_SENSOR capability, need positive min frame duration for" 1149 + "format " + format + " for size " + size + " minDuration " + 1150 minDuration, 1151 minDuration > 0); 1152 } else { 1153 assertTrue("Need non-negative min frame duration for format " + format, 1154 minDuration >= 0); 1155 } 1156 1157 // todo: test opaque image reader when it's supported. 1158 if (format != ImageFormat.PRIVATE) { 1159 ImageReader testReader = ImageReader.newInstance( 1160 size.getWidth(), 1161 size.getHeight(), 1162 format, 1163 1); 1164 Surface testSurface = testReader.getSurface(); 1165 1166 assertTrue( 1167 String.format("isOutputSupportedFor fails for config %s, format %d", 1168 size.toString(), format), 1169 config.isOutputSupportedFor(testSurface)); 1170 1171 testReader.close(); 1172 } 1173 } // sizes 1174 1175 // Try an invalid size in this format, should round 1176 Size invalidSize = findInvalidSize(supportedSizes); 1177 int MAX_ROUNDING_WIDTH = 1920; 1178 // todo: test opaque image reader when it's supported. 1179 if (format != ImageFormat.PRIVATE && 1180 invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) { 1181 ImageReader testReader = ImageReader.newInstance( 1182 invalidSize.getWidth(), 1183 invalidSize.getHeight(), 1184 format, 1185 1); 1186 Surface testSurface = testReader.getSurface(); 1187 1188 assertTrue( 1189 String.format("isOutputSupportedFor fails for config %s, %d", 1190 invalidSize.toString(), format), 1191 config.isOutputSupportedFor(testSurface)); 1192 1193 testReader.close(); 1194 } 1195 } // formats 1196 1197 // Cross-check opaque format and sizes 1198 if (arrayContains(actualCapabilities, BC)) { 1199 SurfaceTexture st = new SurfaceTexture(1); 1200 Surface surf = new Surface(st); 1201 1202 Size[] opaqueSizes = CameraTestUtils.getSupportedSizeForClass(SurfaceTexture.class, 1203 mIds[counter], mCameraManager); 1204 assertTrue("Opaque format has no sizes listed", 1205 opaqueSizes.length > 0); 1206 for (Size size : opaqueSizes) { 1207 long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size); 1208 assertTrue("Opaque output may not have a non-zero stall duration", 1209 stallDuration == 0); 1210 1211 long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size); 1212 if (arrayContains(actualCapabilities, 1213 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { 1214 assertTrue("MANUAL_SENSOR capability, need positive min frame duration for" 1215 + "opaque format", 1216 minDuration > 0); 1217 } else { 1218 assertTrue("Need non-negative min frame duration for opaque format ", 1219 minDuration >= 0); 1220 } 1221 st.setDefaultBufferSize(size.getWidth(), size.getHeight()); 1222 1223 assertTrue( 1224 String.format("isOutputSupportedFor fails for SurfaceTexture config %s", 1225 size.toString()), 1226 config.isOutputSupportedFor(surf)); 1227 1228 } // opaque sizes 1229 1230 // Try invalid opaque size, should get rounded 1231 Size invalidSize = findInvalidSize(opaqueSizes); 1232 st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight()); 1233 assertTrue( 1234 String.format("isOutputSupportedFor fails for SurfaceTexture config %s", 1235 invalidSize.toString()), 1236 config.isOutputSupportedFor(surf)); 1237 1238 } 1239 counter++; 1240 } // mCharacteristics 1241 } 1242 1243 /** 1244 * Test high speed capability and cross-check the high speed sizes and fps ranges from 1245 * the StreamConfigurationMap. 1246 */ testConstrainedHighSpeedCapability()1247 public void testConstrainedHighSpeedCapability() throws Exception { 1248 int counter = 0; 1249 for (CameraCharacteristics c : mCharacteristics) { 1250 int[] capabilities = CameraTestUtils.getValueNotNull( 1251 c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 1252 boolean supportHighSpeed = arrayContains(capabilities, CONSTRAINED_HIGH_SPEED); 1253 if (supportHighSpeed) { 1254 StreamConfigurationMap config = 1255 CameraTestUtils.getValueNotNull( 1256 c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1257 List<Size> highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes()); 1258 assertTrue("High speed sizes shouldn't be empty", highSpeedSizes.size() > 0); 1259 Size[] allSizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.PRIVATE, 1260 mIds[counter], mCameraManager); 1261 assertTrue("Normal size for PRIVATE format shouldn't be null or empty", 1262 allSizes != null && allSizes.length > 0); 1263 for (Size size: highSpeedSizes) { 1264 // The sizes must be a subset of the normal sizes 1265 assertTrue("High speed size " + size + 1266 " must be part of normal sizes " + Arrays.toString(allSizes), 1267 Arrays.asList(allSizes).contains(size)); 1268 1269 // Sanitize the high speed FPS ranges for each size 1270 List<Range<Integer>> ranges = 1271 Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size)); 1272 for (Range<Integer> range : ranges) { 1273 assertTrue("The range " + range + " doesn't satisfy the" 1274 + " min/max boundary requirements.", 1275 range.getLower() >= HIGH_SPEED_FPS_LOWER_MIN && 1276 range.getUpper() >= HIGH_SPEED_FPS_UPPER_MIN); 1277 assertTrue("The range " + range + " should be multiple of 30fps", 1278 range.getLower() % 30 == 0 && range.getUpper() % 30 == 0); 1279 // If the range is fixed high speed range, it should contain the 1280 // [30, fps_max] in the high speed range list; if it's variable FPS range, 1281 // the corresponding fixed FPS Range must be included in the range list. 1282 if (range.getLower() == range.getUpper()) { 1283 Range<Integer> variableRange = new Range<Integer>(30, range.getUpper()); 1284 assertTrue("The variable FPS range " + variableRange + 1285 " shoould be included in the high speed ranges for size " + 1286 size, ranges.contains(variableRange)); 1287 } else { 1288 Range<Integer> fixedRange = 1289 new Range<Integer>(range.getUpper(), range.getUpper()); 1290 assertTrue("The fixed FPS range " + fixedRange + 1291 " shoould be included in the high speed ranges for size " + 1292 size, ranges.contains(fixedRange)); 1293 } 1294 } 1295 } 1296 // If the device advertise some high speed profiles, the sizes and FPS ranges 1297 // should be advertise by the camera. 1298 for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P; 1299 quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) { 1300 int cameraId = Integer.valueOf(mIds[counter]); 1301 if (CamcorderProfile.hasProfile(cameraId, quality)) { 1302 CamcorderProfile profile = CamcorderProfile.get(cameraId, quality); 1303 Size camcorderProfileSize = 1304 new Size(profile.videoFrameWidth, profile.videoFrameHeight); 1305 assertTrue("CamcorderPrfile size " + camcorderProfileSize + 1306 " must be included in the high speed sizes " + 1307 Arrays.toString(highSpeedSizes.toArray()), 1308 highSpeedSizes.contains(camcorderProfileSize)); 1309 Range<Integer> camcorderFpsRange = 1310 new Range<Integer>(profile.videoFrameRate, profile.videoFrameRate); 1311 List<Range<Integer>> allRanges = 1312 Arrays.asList(config.getHighSpeedVideoFpsRangesFor( 1313 camcorderProfileSize)); 1314 assertTrue("Camcorder fps range " + camcorderFpsRange + 1315 " should be included by high speed fps ranges " + 1316 Arrays.toString(allRanges.toArray()), 1317 allRanges.contains(camcorderFpsRange)); 1318 } 1319 } 1320 } 1321 counter++; 1322 } 1323 } 1324 1325 /** 1326 * Sanity check of optical black regions. 1327 */ testOpticalBlackRegions()1328 public void testOpticalBlackRegions() { 1329 int counter = 0; 1330 for (CameraCharacteristics c : mCharacteristics) { 1331 List<CaptureResult.Key<?>> resultKeys = c.getAvailableCaptureResultKeys(); 1332 boolean hasDynamicBlackLevel = 1333 resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL); 1334 boolean hasDynamicWhiteLevel = 1335 resultKeys.contains(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL); 1336 boolean hasFixedBlackLevel = 1337 c.getKeys().contains(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN); 1338 boolean hasFixedWhiteLevel = 1339 c.getKeys().contains(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL); 1340 // The black and white levels should be either all supported or none of them is 1341 // supported. 1342 mCollector.expectTrue("Dynamic black and white level should be all or none of them" 1343 + " be supported", hasDynamicWhiteLevel == hasDynamicBlackLevel); 1344 mCollector.expectTrue("Fixed black and white level should be all or none of them" 1345 + " be supported", hasFixedBlackLevel == hasFixedWhiteLevel); 1346 mCollector.expectTrue("Fixed black level should be supported if dynamic black" 1347 + " level is supported", !hasDynamicBlackLevel || hasFixedBlackLevel); 1348 1349 if (c.getKeys().contains(CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS)) { 1350 // Regions shouldn't be null or empty. 1351 Rect[] regions = CameraTestUtils.getValueNotNull(c, 1352 CameraCharacteristics.SENSOR_OPTICAL_BLACK_REGIONS); 1353 CameraTestUtils.assertArrayNotEmpty(regions, "Optical back region arrays must not" 1354 + " be empty"); 1355 1356 // Dynamic black level should be supported if the optical black region is 1357 // advertised. 1358 mCollector.expectTrue("Dynamic black and white level keys should be advertised in " 1359 + "available capture result key list", hasDynamicWhiteLevel); 1360 1361 // Range check. 1362 for (Rect region : regions) { 1363 mCollector.expectTrue("Camera " + mIds[counter] + ": optical black region" + 1364 " shouldn't be empty!", !region.isEmpty()); 1365 mCollector.expectGreaterOrEqual("Optical black region left", 0/*expected*/, 1366 region.left/*actual*/); 1367 mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/, 1368 region.top/*actual*/); 1369 mCollector.expectTrue("Optical black region left/right/width/height must be" 1370 + " even number, otherwise, the bayer CFA pattern in this region will" 1371 + " be messed up", 1372 region.left % 2 == 0 && region.top % 2 == 0 && 1373 region.width() % 2 == 0 && region.height() % 2 == 0); 1374 mCollector.expectGreaterOrEqual("Optical black region top", 0/*expected*/, 1375 region.top/*actual*/); 1376 Size size = CameraTestUtils.getValueNotNull(c, 1377 CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE); 1378 mCollector.expectLessOrEqual("Optical black region width", 1379 size.getWidth()/*expected*/, region.width()); 1380 mCollector.expectLessOrEqual("Optical black region height", 1381 size.getHeight()/*expected*/, region.height()); 1382 Rect activeArray = CameraTestUtils.getValueNotNull(c, 1383 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 1384 mCollector.expectTrue("Optical black region" + region + " should be outside of" 1385 + " active array " + activeArray, 1386 !region.intersect(activeArray)); 1387 // Region need to be disjoint: 1388 for (Rect region2 : regions) { 1389 mCollector.expectTrue("Optical black region" + region + " should have no " 1390 + "overlap with " + region2, 1391 region == region2 || !region.intersect(region2)); 1392 } 1393 } 1394 } else { 1395 Log.i(TAG, "Camera " + mIds[counter] + " doesn't support optical black regions," 1396 + " skip the region test"); 1397 } 1398 counter++; 1399 } 1400 } 1401 1402 /** 1403 * Check Logical camera capability 1404 */ testLogicalCameraCharacteristics()1405 public void testLogicalCameraCharacteristics() throws Exception { 1406 int counter = 0; 1407 List<String> cameraIdList = Arrays.asList(mIds); 1408 1409 for (CameraCharacteristics c : mCharacteristics) { 1410 int[] capabilities = CameraTestUtils.getValueNotNull( 1411 c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 1412 boolean supportLogicalCamera = arrayContains(capabilities, 1413 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA); 1414 if (supportLogicalCamera) { 1415 Set<String> physicalCameraIds = c.getPhysicalCameraIds(); 1416 assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null", 1417 physicalCameraIds); 1418 assertTrue("Logical camera must contain at least 2 physical camera ids", 1419 physicalCameraIds.size() >= 2); 1420 1421 mCollector.expectKeyValueInRange(c, 1422 CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE, 1423 CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE, 1424 CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED); 1425 1426 Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE); 1427 for (String physicalCameraId : physicalCameraIds) { 1428 assertNotNull("Physical camera id shouldn't be null", physicalCameraId); 1429 assertTrue( 1430 String.format("Physical camera id %s shouldn't be the same as logical" 1431 + " camera id %s", physicalCameraId, mIds[counter]), 1432 physicalCameraId != mIds[counter]); 1433 assertTrue( 1434 String.format("Physical camera id %s should be in available camera ids", 1435 physicalCameraId), 1436 cameraIdList.contains(physicalCameraId)); 1437 1438 //validation for depth static metadata of physical cameras 1439 CameraCharacteristics pc = 1440 mCameraManager.getCameraCharacteristics(physicalCameraId); 1441 1442 float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION); 1443 float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION); 1444 Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE); 1445 float[] cameraIntrinsics = pc.get( 1446 CameraCharacteristics.LENS_INTRINSIC_CALIBRATION); 1447 float[] distortion = getLensDistortion(pc); 1448 Rect precorrectionArray = pc.get( 1449 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 1450 1451 verifyLensCalibration(poseRotation, poseTranslation, poseReference, 1452 cameraIntrinsics, distortion, precorrectionArray); 1453 1454 Integer timestampSourcePhysical = 1455 pc.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE); 1456 mCollector.expectEquals("Logical camera and physical cameras must have same " + 1457 "timestamp source", timestampSource, timestampSourcePhysical); 1458 } 1459 } 1460 counter++; 1461 } 1462 } 1463 1464 /** 1465 * Check monochrome camera capability 1466 */ testMonochromeCharacteristics()1467 public void testMonochromeCharacteristics() { 1468 int counter = 0; 1469 1470 for (CameraCharacteristics c : mCharacteristics) { 1471 Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + mIds[counter]); 1472 1473 int[] capabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 1474 assertNotNull("android.request.availableCapabilities must never be null", 1475 capabilities); 1476 boolean supportMonochrome = arrayContains(capabilities, MONOCHROME); 1477 1478 if (!supportMonochrome) { 1479 continue; 1480 } 1481 1482 assertTrue("Monochrome camera must have BACKWARD_COMPATIBLE capability", 1483 arrayContains(capabilities, BC)); 1484 assertTrue("Monochrome camera must not have RAW capability", 1485 !arrayContains(capabilities, RAW)); 1486 assertTrue("Monochrome camera must not have MANUAL_POST_PROCESSING capability", 1487 !arrayContains(capabilities, MANUAL_POSTPROC)); 1488 1489 // Check that awbSupportedModes only contains AUTO 1490 int[] awbAvailableModes = c.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); 1491 assertTrue("availableAwbModes must not be null", awbAvailableModes != null); 1492 assertTrue("availableAwbModes must contain only AUTO", awbAvailableModes.length == 1 && 1493 awbAvailableModes[0] == CaptureRequest.CONTROL_AWB_MODE_AUTO); 1494 } 1495 } 1496 1497 /** 1498 * Get lens distortion coefficients, as a list of 6 floats; returns null if no valid 1499 * distortion field is available 1500 */ getLensDistortion(CameraCharacteristics c)1501 private float[] getLensDistortion(CameraCharacteristics c) { 1502 float[] distortion = null; 1503 float[] newDistortion = c.get(CameraCharacteristics.LENS_DISTORTION); 1504 if (Build.VERSION.FIRST_SDK_INT > Build.VERSION_CODES.O_MR1 || newDistortion != null) { 1505 // New devices need to use fixed radial distortion definition; old devices can 1506 // opt-in to it 1507 if (newDistortion != null && newDistortion.length == 5) { 1508 distortion = new float[6]; 1509 distortion[0] = 1.0f; 1510 for (int i = 1; i < 6; i++) { 1511 distortion[i] = newDistortion[i-1]; 1512 } 1513 } 1514 } else { 1515 // Select old field only if on older first SDK and new definition not available 1516 distortion = c.get(CameraCharacteristics.LENS_RADIAL_DISTORTION); 1517 } 1518 return distortion; 1519 } 1520 1521 /** 1522 * Create an invalid size that's close to one of the good sizes in the list, but not one of them 1523 */ findInvalidSize(Size[] goodSizes)1524 private Size findInvalidSize(Size[] goodSizes) { 1525 return findInvalidSize(Arrays.asList(goodSizes)); 1526 } 1527 1528 /** 1529 * Create an invalid size that's close to one of the good sizes in the list, but not one of them 1530 */ findInvalidSize(List<Size> goodSizes)1531 private Size findInvalidSize(List<Size> goodSizes) { 1532 Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight()); 1533 while(goodSizes.contains(invalidSize)) { 1534 invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight()); 1535 } 1536 return invalidSize; 1537 } 1538 1539 /** 1540 * Check key is present in characteristics if the hardware level is at least {@code hwLevel}; 1541 * check that the key is present if the actual capabilities are one of {@code capabilities}. 1542 * 1543 * @return value of the {@code key} from {@code c} 1544 */ expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key, int hwLevel, int... capabilities)1545 private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key, 1546 int hwLevel, int... capabilities) { 1547 1548 Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); 1549 assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel); 1550 1551 int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 1552 assertNotNull("android.request.availableCapabilities must never be null", 1553 actualCapabilities); 1554 1555 List<Key<?>> allKeys = c.getKeys(); 1556 1557 T value = c.get(key); 1558 1559 // For LIMITED-level targeted keys, rely on capability check, not level 1560 if ((compareHardwareLevel(actualHwLevel, hwLevel) >= 0) && (hwLevel != LIMITED)) { 1561 mCollector.expectTrue( 1562 String.format("Key (%s) must be in characteristics for this hardware level " + 1563 "(required minimal HW level %s, actual HW level %s)", 1564 key.getName(), toStringHardwareLevel(hwLevel), 1565 toStringHardwareLevel(actualHwLevel)), 1566 value != null); 1567 mCollector.expectTrue( 1568 String.format("Key (%s) must be in characteristics list of keys for this " + 1569 "hardware level (required minimal HW level %s, actual HW level %s)", 1570 key.getName(), toStringHardwareLevel(hwLevel), 1571 toStringHardwareLevel(actualHwLevel)), 1572 allKeys.contains(key)); 1573 } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) { 1574 if (!(hwLevel == LIMITED && compareHardwareLevel(actualHwLevel, hwLevel) < 0)) { 1575 // Don't enforce LIMITED-starting keys on LEGACY level, even if cap is defined 1576 mCollector.expectTrue( 1577 String.format("Key (%s) must be in characteristics for these capabilities " + 1578 "(required capabilities %s, actual capabilities %s)", 1579 key.getName(), Arrays.toString(capabilities), 1580 Arrays.toString(actualCapabilities)), 1581 value != null); 1582 mCollector.expectTrue( 1583 String.format("Key (%s) must be in characteristics list of keys for " + 1584 "these capabilities (required capabilities %s, actual capabilities %s)", 1585 key.getName(), Arrays.toString(capabilities), 1586 Arrays.toString(actualCapabilities)), 1587 allKeys.contains(key)); 1588 } 1589 } else { 1590 if (actualHwLevel == LEGACY && hwLevel != OPT) { 1591 if (value != null || allKeys.contains(key)) { 1592 Log.w(TAG, String.format( 1593 "Key (%s) is not required for LEGACY devices but still appears", 1594 key.getName())); 1595 } 1596 } 1597 // OK: Key may or may not be present. 1598 } 1599 return value; 1600 } 1601 arrayContains(int[] arr, int needle)1602 private static boolean arrayContains(int[] arr, int needle) { 1603 if (arr == null) { 1604 return false; 1605 } 1606 1607 for (int elem : arr) { 1608 if (elem == needle) { 1609 return true; 1610 } 1611 } 1612 1613 return false; 1614 } 1615 arrayContains(T[] arr, T needle)1616 private static <T> boolean arrayContains(T[] arr, T needle) { 1617 if (arr == null) { 1618 return false; 1619 } 1620 1621 for (T elem : arr) { 1622 if (elem.equals(needle)) { 1623 return true; 1624 } 1625 } 1626 1627 return false; 1628 } 1629 arrayContainsAnyOf(int[] arr, int[] needles)1630 private static boolean arrayContainsAnyOf(int[] arr, int[] needles) { 1631 for (int needle : needles) { 1632 if (arrayContains(arr, needle)) { 1633 return true; 1634 } 1635 } 1636 return false; 1637 } 1638 1639 /** 1640 * The key name has a prefix of either "android." or a valid TLD; other prefixes are not valid. 1641 */ assertKeyPrefixValid(String keyName)1642 private static void assertKeyPrefixValid(String keyName) { 1643 assertStartsWithAndroidOrTLD( 1644 "All metadata keys must start with 'android.' (built-in keys) " + 1645 "or valid TLD (vendor-extended keys)", keyName); 1646 } 1647 assertTrueForKey(String msg, CameraCharacteristics.Key<?> key, boolean actual)1648 private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key, 1649 boolean actual) { 1650 assertTrue(msg + " (key = '" + key.getName() + "')", actual); 1651 } 1652 assertOneOf(String msg, T[] expected, T actual)1653 private static <T> void assertOneOf(String msg, T[] expected, T actual) { 1654 for (int i = 0; i < expected.length; ++i) { 1655 if (Objects.equals(expected[i], actual)) { 1656 return; 1657 } 1658 } 1659 1660 fail(String.format("%s: (expected one of %s, actual %s)", 1661 msg, Arrays.toString(expected), actual)); 1662 } 1663 assertStartsWithAndroidOrTLD(String msg, String keyName)1664 private static <T> void assertStartsWithAndroidOrTLD(String msg, String keyName) { 1665 String delimiter = "."; 1666 if (keyName.startsWith(PREFIX_ANDROID + delimiter)) { 1667 return; 1668 } 1669 Pattern tldPattern = Pattern.compile(Patterns.TOP_LEVEL_DOMAIN_STR); 1670 Matcher match = tldPattern.matcher(keyName); 1671 if (match.find(0) && (0 == match.start()) && (!match.hitEnd())) { 1672 if (keyName.regionMatches(match.end(), delimiter, 0, delimiter.length())) { 1673 return; 1674 } 1675 } 1676 1677 fail(String.format("%s: (expected to start with %s or valid TLD, but value was %s)", 1678 msg, PREFIX_ANDROID + delimiter, keyName)); 1679 } 1680 1681 /** Return a positive int if left > right, 0 if left==right, negative int if left < right */ compareHardwareLevel(int left, int right)1682 private static int compareHardwareLevel(int left, int right) { 1683 return remapHardwareLevel(left) - remapHardwareLevel(right); 1684 } 1685 1686 /** Remap HW levels worst<->best, 0 = LEGACY, 1 = LIMITED, 2 = FULL, ..., N = LEVEL_N */ remapHardwareLevel(int level)1687 private static int remapHardwareLevel(int level) { 1688 switch (level) { 1689 case OPT: 1690 return Integer.MAX_VALUE; 1691 case LEGACY: 1692 return 0; // lowest 1693 case EXTERNAL: 1694 return 1; // second lowest 1695 case LIMITED: 1696 return 2; 1697 case FULL: 1698 return 3; // good 1699 case LEVEL_3: 1700 return 4; 1701 default: 1702 fail("Unknown HW level: " + level); 1703 } 1704 return -1; 1705 } 1706 toStringHardwareLevel(int level)1707 private static String toStringHardwareLevel(int level) { 1708 switch (level) { 1709 case LEGACY: 1710 return "LEGACY"; 1711 case LIMITED: 1712 return "LIMITED"; 1713 case FULL: 1714 return "FULL"; 1715 case EXTERNAL: 1716 return "EXTERNAL"; 1717 default: 1718 if (level >= LEVEL_3) { 1719 return String.format("LEVEL_%d", level); 1720 } 1721 } 1722 1723 // unknown 1724 Log.w(TAG, "Unknown hardware level " + level); 1725 return Integer.toString(level); 1726 } 1727 } 1728