1 /* 2 * Copyright 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.*; 20 import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.*; 21 22 import android.content.Context; 23 import android.graphics.ImageFormat; 24 import android.graphics.SurfaceTexture; 25 import android.hardware.camera2.CameraCaptureSession; 26 import android.hardware.camera2.CameraCharacteristics; 27 import android.hardware.camera2.CameraDevice; 28 import android.hardware.camera2.CameraManager; 29 import android.hardware.camera2.CaptureRequest; 30 import android.hardware.camera2.CaptureResult; 31 import android.hardware.camera2.TotalCaptureResult; 32 import android.hardware.camera2.CaptureFailure; 33 import android.hardware.camera2.params.InputConfiguration; 34 import android.hardware.camera2.params.StreamConfigurationMap; 35 import android.hardware.camera2.cts.helpers.StaticMetadata; 36 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase; 37 import android.media.CamcorderProfile; 38 import android.media.Image; 39 import android.media.ImageReader; 40 import android.media.ImageWriter; 41 import android.util.Log; 42 import android.util.Size; 43 import android.view.Display; 44 import android.view.Surface; 45 import android.view.WindowManager; 46 47 import com.android.ex.camera2.blocking.BlockingSessionCallback; 48 49 import java.util.Arrays; 50 import java.util.ArrayList; 51 import java.util.List; 52 53 import static junit.framework.Assert.assertTrue; 54 import static org.mockito.Mockito.*; 55 56 /** 57 * Tests exercising edge cases in camera setup, configuration, and usage. 58 */ 59 public class RobustnessTest extends Camera2AndroidTestCase { 60 private static final String TAG = "RobustnessTest"; 61 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 62 63 private static final int CONFIGURE_TIMEOUT = 5000; //ms 64 private static final int CAPTURE_TIMEOUT = 1000; //ms 65 66 // For testTriggerInteractions 67 private static final int PREVIEW_WARMUP_FRAMES = 60; 68 private static final int MAX_RESULT_STATE_CHANGE_WAIT_FRAMES = 100; 69 private static final int MAX_TRIGGER_SEQUENCE_FRAMES = 180; // 6 sec at 30 fps 70 private static final int MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES = 10; 71 72 /** 73 * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing 74 * a dimension other than one of the supported output dimensions. The buffers produced into 75 * this surface are expected have the dimensions of the closest possible buffer size in the 76 * available stream configurations for a surface with this format. 77 */ testBadSurfaceDimensions()78 public void testBadSurfaceDimensions() throws Exception { 79 for (String id : mCameraIds) { 80 try { 81 Log.i(TAG, "Testing Camera " + id); 82 openDevice(id); 83 84 List<Size> testSizes = null; 85 int format = mStaticInfo.isColorOutputSupported() ? 86 ImageFormat.YUV_420_888 : ImageFormat.DEPTH16; 87 88 testSizes = CameraTestUtils.getSortedSizesForFormat(id, mCameraManager, 89 format, null); 90 91 // Find some size not supported by the camera 92 Size weirdSize = new Size(643, 577); 93 int count = 0; 94 while(testSizes.contains(weirdSize)) { 95 // Really, they can't all be supported... 96 weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1); 97 count++; 98 assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100); 99 } 100 101 // Setup imageReader with invalid dimension 102 ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(), 103 weirdSize.getHeight(), format, 3); 104 105 // Setup ImageReaderListener 106 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(); 107 imageReader.setOnImageAvailableListener(imageListener, mHandler); 108 109 Surface surface = imageReader.getSurface(); 110 List<Surface> surfaces = new ArrayList<>(); 111 surfaces.add(surface); 112 113 // Setup a capture request and listener 114 CaptureRequest.Builder request = 115 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 116 request.addTarget(surface); 117 118 // Check that correct session callback is hit. 119 CameraCaptureSession.StateCallback sessionListener = 120 mock(CameraCaptureSession.StateCallback.class); 121 CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera, 122 surfaces, sessionListener, mHandler); 123 124 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()). 125 onConfigured(any(CameraCaptureSession.class)); 126 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()). 127 onReady(any(CameraCaptureSession.class)); 128 verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class)); 129 verify(sessionListener, never()).onActive(any(CameraCaptureSession.class)); 130 verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class)); 131 132 CameraCaptureSession.CaptureCallback captureListener = 133 mock(CameraCaptureSession.CaptureCallback.class); 134 session.capture(request.build(), captureListener, mHandler); 135 136 verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()). 137 onCaptureCompleted(any(CameraCaptureSession.class), 138 any(CaptureRequest.class), any(TotalCaptureResult.class)); 139 verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class), 140 any(CaptureRequest.class), any(CaptureFailure.class)); 141 142 Image image = imageListener.getImage(CAPTURE_TIMEOUT); 143 int imageWidth = image.getWidth(); 144 int imageHeight = image.getHeight(); 145 Size actualSize = new Size(imageWidth, imageHeight); 146 147 assertTrue("Camera does not contain outputted image resolution " + actualSize, 148 testSizes.contains(actualSize)); 149 } finally { 150 closeDevice(id); 151 } 152 } 153 } 154 155 /** 156 * Test for making sure the required output combinations for each hardware level and capability 157 * work as expected. 158 */ testMandatoryOutputCombinations()159 public void testMandatoryOutputCombinations() throws Exception { 160 /** 161 * Tables for maximum sizes to try for each hardware level and capability. 162 * 163 * Keep in sync with the tables in 164 * frameworks/base/core/java/android/hardware/camera2/CameraDevice.java#createCaptureSession 165 * 166 * Each row of the table is a set of (format, max resolution) pairs, using the below consts 167 */ 168 169 // Enum values are defined in MaxStreamSizes 170 final int[][] LEGACY_COMBINATIONS = { 171 {PRIV, MAXIMUM}, // Simple preview, GPU video processing, or no-preview video recording 172 {JPEG, MAXIMUM}, // No-viewfinder still image capture 173 {YUV, MAXIMUM}, // In-application video/image processing 174 {PRIV, PREVIEW, JPEG, MAXIMUM}, // Standard still imaging. 175 {YUV, PREVIEW, JPEG, MAXIMUM}, // In-app processing plus still capture. 176 {PRIV, PREVIEW, PRIV, PREVIEW}, // Standard recording. 177 {PRIV, PREVIEW, YUV, PREVIEW}, // Preview plus in-app processing. 178 {PRIV, PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM} // Still capture plus in-app processing. 179 }; 180 181 final int[][] LIMITED_COMBINATIONS = { 182 {PRIV, PREVIEW, PRIV, RECORD }, // High-resolution video recording with preview. 183 {PRIV, PREVIEW, YUV , RECORD }, // High-resolution in-app video processing with preview. 184 {YUV , PREVIEW, YUV , RECORD }, // Two-input in-app video processing. 185 {PRIV, PREVIEW, PRIV, RECORD, JPEG, RECORD }, // High-resolution recording with video snapshot. 186 {PRIV, PREVIEW, YUV, RECORD, JPEG, RECORD }, // High-resolution in-app processing with video snapshot. 187 {YUV , PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM } // Two-input in-app processing with still capture. 188 }; 189 190 final int[][] BURST_COMBINATIONS = { 191 {PRIV, PREVIEW, PRIV, MAXIMUM }, // Maximum-resolution GPU processing with preview. 192 {PRIV, PREVIEW, YUV, MAXIMUM }, // Maximum-resolution in-app processing with preview. 193 {YUV, PREVIEW, YUV, MAXIMUM }, // Maximum-resolution two-input in-app processsing. 194 }; 195 196 final int[][] FULL_COMBINATIONS = { 197 {PRIV, PREVIEW, PRIV, PREVIEW, JPEG, MAXIMUM }, //Video recording with maximum-size video snapshot. 198 {YUV, VGA, PRIV, PREVIEW, YUV, MAXIMUM }, // Standard video recording plus maximum-resolution in-app processing. 199 {YUV, VGA, YUV, PREVIEW, YUV, MAXIMUM } // Preview plus two-input maximum-resolution in-app processing. 200 }; 201 202 final int[][] RAW_COMBINATIONS = { 203 {RAW, MAXIMUM }, // No-preview DNG capture. 204 {PRIV, PREVIEW, RAW, MAXIMUM }, // Standard DNG capture. 205 {YUV, PREVIEW, RAW, MAXIMUM }, // In-app processing plus DNG capture. 206 {PRIV, PREVIEW, PRIV, PREVIEW, RAW, MAXIMUM}, // Video recording with DNG capture. 207 {PRIV, PREVIEW, YUV, PREVIEW, RAW, MAXIMUM}, // Preview with in-app processing and DNG capture. 208 {YUV, PREVIEW, YUV, PREVIEW, RAW, MAXIMUM}, // Two-input in-app processing plus DNG capture. 209 {PRIV, PREVIEW, JPEG, MAXIMUM, RAW, MAXIMUM}, // Still capture with simultaneous JPEG and DNG. 210 {YUV, PREVIEW, JPEG, MAXIMUM, RAW, MAXIMUM} // In-app processing with simultaneous JPEG and DNG. 211 }; 212 213 final int[][][] TABLES = 214 { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS }; 215 216 sanityCheckConfigurationTables(TABLES); 217 218 for (String id : mCameraIds) { 219 openDevice(id); 220 221 // Find the concrete max sizes for each format/resolution combination 222 MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, getContext()); 223 224 String streamConfigurationMapString = 225 mStaticInfo.getCharacteristics().get( 226 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString(); 227 if (VERBOSE) { 228 Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString); 229 } 230 231 // Always run legacy-level tests for color-supporting devices 232 233 if (mStaticInfo.isColorOutputSupported()) { 234 for (int[] config : LEGACY_COMBINATIONS) { 235 testOutputCombination(id, config, maxSizes); 236 } 237 } 238 239 // Then run higher-level tests if applicable 240 241 if (!mStaticInfo.isHardwareLevelLegacy()) { 242 243 // If not legacy, at least limited, so run limited-level tests 244 245 if (mStaticInfo.isColorOutputSupported()) { 246 for (int[] config : LIMITED_COMBINATIONS) { 247 testOutputCombination(id, config, maxSizes); 248 } 249 } 250 251 // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate 252 253 if (mStaticInfo.isCapabilitySupported( 254 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) { 255 for (int[] config : BURST_COMBINATIONS) { 256 testOutputCombination(id, config, maxSizes); 257 } 258 } 259 260 if (mStaticInfo.isHardwareLevelFull()) { 261 for (int[] config : FULL_COMBINATIONS) { 262 testOutputCombination(id, config, maxSizes); 263 } 264 } 265 266 if (mStaticInfo.isCapabilitySupported( 267 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 268 for (int[] config : RAW_COMBINATIONS) { 269 testOutputCombination(id, config, maxSizes); 270 } 271 } 272 } 273 274 closeDevice(id); 275 } 276 } 277 278 /** 279 * Test for making sure the required reprocess input/output combinations for each hardware 280 * level and capability work as expected. 281 */ testMandatoryReprocessConfigurations()282 public void testMandatoryReprocessConfigurations() throws Exception { 283 284 /** 285 * For each stream combination, verify that 286 * 1. A reprocessable session can be created using the stream combination. 287 * 2. Reprocess capture requests targeting YUV and JPEG outputs are successful. 288 */ 289 final int[][] LIMITED_COMBINATIONS = { 290 // Input Outputs 291 {PRIV, MAXIMUM, JPEG, MAXIMUM}, 292 {YUV , MAXIMUM, JPEG, MAXIMUM}, 293 {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM}, 294 {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM}, 295 {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM}, 296 {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM}, 297 {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 298 {YUV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 299 }; 300 301 final int[][] FULL_COMBINATIONS = { 302 // Input Outputs 303 {YUV , MAXIMUM, PRIV, PREVIEW}, 304 {YUV , MAXIMUM, YUV , PREVIEW}, 305 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD}, 306 {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD}, 307 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , MAXIMUM}, 308 {PRIV, MAXIMUM, YUV , PREVIEW, YUV , MAXIMUM}, 309 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 310 {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM}, 311 }; 312 313 final int[][] RAW_COMBINATIONS = { 314 // Input Outputs 315 {PRIV, MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM}, 316 {YUV , MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM}, 317 {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 318 {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 319 {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 320 {YUV , MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM}, 321 {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 322 {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 323 {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 324 {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM}, 325 }; 326 327 final int[][][] TABLES = 328 { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS }; 329 330 sanityCheckConfigurationTables(TABLES); 331 332 for (String id : mCameraIds) { 333 CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id); 334 StaticMetadata staticInfo = new StaticMetadata(cc); 335 MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id, getContext()); 336 337 // Skip the test for legacy devices. 338 if (staticInfo.isHardwareLevelLegacy()) { 339 continue; 340 } 341 342 openDevice(id); 343 344 try { 345 for (int[] config : LIMITED_COMBINATIONS) { 346 testReprocessStreamCombination(id, config, maxSizes, staticInfo); 347 } 348 349 // Check FULL devices 350 if (staticInfo.isHardwareLevelFull()) { 351 for (int[] config : FULL_COMBINATIONS) { 352 testReprocessStreamCombination(id, config, maxSizes, staticInfo); 353 } 354 } 355 356 // Check devices with RAW capability. 357 if (staticInfo.isCapabilitySupported( 358 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 359 for (int[] config : RAW_COMBINATIONS) { 360 testReprocessStreamCombination(id, config, maxSizes, staticInfo); 361 } 362 } 363 } finally { 364 closeDevice(id); 365 } 366 } 367 } 368 testBasicTriggerSequence()369 public void testBasicTriggerSequence() throws Exception { 370 371 for (String id : mCameraIds) { 372 Log.i(TAG, String.format("Testing Camera %s", id)); 373 374 openDevice(id); 375 try { 376 // Legacy devices do not support precapture trigger; don't test devices that 377 // can't focus 378 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) { 379 continue; 380 } 381 // Depth-only devices won't support AE 382 if (!mStaticInfo.isColorOutputSupported()) { 383 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 384 continue; 385 } 386 387 int[] availableAfModes = mStaticInfo.getCharacteristics().get( 388 CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); 389 int[] availableAeModes = mStaticInfo.getCharacteristics().get( 390 CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); 391 392 for (int afMode : availableAfModes) { 393 394 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 395 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 396 // Only test AF modes that have meaningful trigger behavior 397 continue; 398 } 399 400 for (int aeMode : availableAeModes) { 401 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 402 // Only test AE modes that have meaningful trigger behavior 403 continue; 404 } 405 406 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 407 408 CaptureRequest.Builder previewRequest = 409 prepareTriggerTestSession(preview, aeMode, afMode); 410 411 SimpleCaptureCallback captureListener = 412 new CameraTestUtils.SimpleCaptureCallback(); 413 414 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 415 mHandler); 416 417 // Cancel triggers 418 419 cancelTriggersAndWait(previewRequest, captureListener, afMode); 420 421 // 422 // Standard sequence - AF trigger then AE trigger 423 424 if (VERBOSE) { 425 Log.v(TAG, String.format("Triggering AF")); 426 } 427 428 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 429 CaptureRequest.CONTROL_AF_TRIGGER_START); 430 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 431 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 432 433 CaptureRequest triggerRequest = previewRequest.build(); 434 mCameraSession.capture(triggerRequest, captureListener, mHandler); 435 436 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 437 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 438 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 439 boolean focusComplete = false; 440 441 for (int i = 0; 442 i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete; 443 i++) { 444 445 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 446 447 CaptureResult focusResult = captureListener.getCaptureResult( 448 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 449 afState = focusResult.get(CaptureResult.CONTROL_AF_STATE); 450 } 451 452 assertTrue("Focusing never completed!", focusComplete); 453 454 // Standard sequence - Part 2 AE trigger 455 456 if (VERBOSE) { 457 Log.v(TAG, String.format("Triggering AE")); 458 } 459 460 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 461 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 462 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 463 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 464 465 triggerRequest = previewRequest.build(); 466 mCameraSession.capture(triggerRequest, captureListener, mHandler); 467 468 triggerResult = captureListener.getCaptureResultForRequest( 469 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 470 471 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 472 473 boolean precaptureComplete = false; 474 475 for (int i = 0; 476 i < MAX_TRIGGER_SEQUENCE_FRAMES && !precaptureComplete; 477 i++) { 478 479 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 480 481 CaptureResult precaptureResult = captureListener.getCaptureResult( 482 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 483 aeState = precaptureResult.get(CaptureResult.CONTROL_AE_STATE); 484 } 485 486 assertTrue("Precapture sequence never completed!", precaptureComplete); 487 488 for (int i = 0; i < MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES; i++) { 489 CaptureResult postPrecaptureResult = captureListener.getCaptureResult( 490 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 491 aeState = postPrecaptureResult.get(CaptureResult.CONTROL_AE_STATE); 492 assertTrue("Late transition to PRECAPTURE state seen", 493 aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE); 494 } 495 496 // Done 497 498 stopCapture(/*fast*/ false); 499 preview.release(); 500 } 501 502 } 503 504 } finally { 505 closeDevice(id); 506 } 507 } 508 509 } 510 testSimultaneousTriggers()511 public void testSimultaneousTriggers() throws Exception { 512 for (String id : mCameraIds) { 513 Log.i(TAG, String.format("Testing Camera %s", id)); 514 515 openDevice(id); 516 try { 517 // Legacy devices do not support precapture trigger; don't test devices that 518 // can't focus 519 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) { 520 continue; 521 } 522 // Depth-only devices won't support AE 523 if (!mStaticInfo.isColorOutputSupported()) { 524 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 525 continue; 526 } 527 528 int[] availableAfModes = mStaticInfo.getCharacteristics().get( 529 CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); 530 int[] availableAeModes = mStaticInfo.getCharacteristics().get( 531 CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); 532 533 for (int afMode : availableAfModes) { 534 535 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 536 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 537 // Only test AF modes that have meaningful trigger behavior 538 continue; 539 } 540 541 for (int aeMode : availableAeModes) { 542 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 543 // Only test AE modes that have meaningful trigger behavior 544 continue; 545 } 546 547 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 548 549 CaptureRequest.Builder previewRequest = 550 prepareTriggerTestSession(preview, aeMode, afMode); 551 552 SimpleCaptureCallback captureListener = 553 new CameraTestUtils.SimpleCaptureCallback(); 554 555 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 556 mHandler); 557 558 // Cancel triggers 559 560 cancelTriggersAndWait(previewRequest, captureListener, afMode); 561 562 // 563 // Trigger AF and AE together 564 565 if (VERBOSE) { 566 Log.v(TAG, String.format("Triggering AF and AE together")); 567 } 568 569 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 570 CaptureRequest.CONTROL_AF_TRIGGER_START); 571 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 572 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 573 574 CaptureRequest triggerRequest = previewRequest.build(); 575 mCameraSession.capture(triggerRequest, captureListener, mHandler); 576 577 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 578 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 579 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 580 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 581 582 boolean precaptureComplete = false; 583 boolean focusComplete = false; 584 585 for (int i = 0; 586 i < MAX_TRIGGER_SEQUENCE_FRAMES && 587 !(focusComplete && precaptureComplete); 588 i++) { 589 590 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 591 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 592 593 CaptureResult sequenceResult = captureListener.getCaptureResult( 594 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 595 afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE); 596 aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE); 597 } 598 599 assertTrue("Precapture sequence never completed!", precaptureComplete); 600 assertTrue("Focus sequence never completed!", focusComplete); 601 602 // Done 603 604 stopCapture(/*fast*/ false); 605 preview.release(); 606 607 } 608 } 609 } finally { 610 closeDevice(id); 611 } 612 } 613 } 614 testAfThenAeTrigger()615 public void testAfThenAeTrigger() throws Exception { 616 for (String id : mCameraIds) { 617 Log.i(TAG, String.format("Testing Camera %s", id)); 618 619 openDevice(id); 620 try { 621 // Legacy devices do not support precapture trigger; don't test devices that 622 // can't focus 623 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) { 624 continue; 625 } 626 // Depth-only devices won't support AE 627 if (!mStaticInfo.isColorOutputSupported()) { 628 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 629 continue; 630 } 631 632 int[] availableAfModes = mStaticInfo.getCharacteristics().get( 633 CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); 634 int[] availableAeModes = mStaticInfo.getCharacteristics().get( 635 CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); 636 637 for (int afMode : availableAfModes) { 638 639 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 640 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 641 // Only test AF modes that have meaningful trigger behavior 642 continue; 643 } 644 645 for (int aeMode : availableAeModes) { 646 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 647 // Only test AE modes that have meaningful trigger behavior 648 continue; 649 } 650 651 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 652 653 CaptureRequest.Builder previewRequest = 654 prepareTriggerTestSession(preview, aeMode, afMode); 655 656 SimpleCaptureCallback captureListener = 657 new CameraTestUtils.SimpleCaptureCallback(); 658 659 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 660 mHandler); 661 662 // Cancel triggers 663 664 cancelTriggersAndWait(previewRequest, captureListener, afMode); 665 666 // 667 // AF with AE a request later 668 669 if (VERBOSE) { 670 Log.v(TAG, "Trigger AF, then AE trigger on next request"); 671 } 672 673 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 674 CaptureRequest.CONTROL_AF_TRIGGER_START); 675 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 676 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 677 678 CaptureRequest triggerRequest = previewRequest.build(); 679 mCameraSession.capture(triggerRequest, captureListener, mHandler); 680 681 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 682 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 683 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 684 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 685 686 CaptureRequest triggerRequest2 = previewRequest.build(); 687 mCameraSession.capture(triggerRequest2, captureListener, mHandler); 688 689 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 690 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 691 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 692 693 boolean precaptureComplete = false; 694 boolean focusComplete = false; 695 696 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 697 698 triggerResult = captureListener.getCaptureResultForRequest( 699 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 700 afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 701 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 702 703 for (int i = 0; 704 i < MAX_TRIGGER_SEQUENCE_FRAMES && 705 !(focusComplete && precaptureComplete); 706 i++) { 707 708 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 709 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 710 711 CaptureResult sequenceResult = captureListener.getCaptureResult( 712 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 713 afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE); 714 aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE); 715 } 716 717 assertTrue("Precapture sequence never completed!", precaptureComplete); 718 assertTrue("Focus sequence never completed!", focusComplete); 719 720 // Done 721 722 stopCapture(/*fast*/ false); 723 preview.release(); 724 725 } 726 } 727 } finally { 728 closeDevice(id); 729 } 730 } 731 } 732 testAeThenAfTrigger()733 public void testAeThenAfTrigger() throws Exception { 734 for (String id : mCameraIds) { 735 Log.i(TAG, String.format("Testing Camera %s", id)); 736 737 openDevice(id); 738 try { 739 // Legacy devices do not support precapture trigger; don't test devices that 740 // can't focus 741 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) { 742 continue; 743 } 744 // Depth-only devices won't support AE 745 if (!mStaticInfo.isColorOutputSupported()) { 746 Log.i(TAG, "Camera " + id + " does not support color outputs, skipping"); 747 continue; 748 } 749 750 int[] availableAfModes = mStaticInfo.getCharacteristics().get( 751 CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); 752 int[] availableAeModes = mStaticInfo.getCharacteristics().get( 753 CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); 754 755 for (int afMode : availableAfModes) { 756 757 if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF || 758 afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) { 759 // Only test AF modes that have meaningful trigger behavior 760 continue; 761 } 762 763 for (int aeMode : availableAeModes) { 764 if (aeMode == CameraCharacteristics.CONTROL_AE_MODE_OFF) { 765 // Only test AE modes that have meaningful trigger behavior 766 continue; 767 } 768 769 SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); 770 771 CaptureRequest.Builder previewRequest = 772 prepareTriggerTestSession(preview, aeMode, afMode); 773 774 SimpleCaptureCallback captureListener = 775 new CameraTestUtils.SimpleCaptureCallback(); 776 777 mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener, 778 mHandler); 779 780 // Cancel triggers 781 782 cancelTriggersAndWait(previewRequest, captureListener, afMode); 783 784 // 785 // AE with AF a request later 786 787 if (VERBOSE) { 788 Log.v(TAG, "Trigger AE, then AF trigger on next request"); 789 } 790 791 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 792 CaptureRequest.CONTROL_AF_TRIGGER_IDLE); 793 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 794 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 795 796 CaptureRequest triggerRequest = previewRequest.build(); 797 mCameraSession.capture(triggerRequest, captureListener, mHandler); 798 799 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 800 CaptureRequest.CONTROL_AF_TRIGGER_START); 801 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 802 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); 803 804 CaptureRequest triggerRequest2 = previewRequest.build(); 805 mCameraSession.capture(triggerRequest2, captureListener, mHandler); 806 807 CaptureResult triggerResult = captureListener.getCaptureResultForRequest( 808 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 809 int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 810 811 boolean precaptureComplete = false; 812 boolean focusComplete = false; 813 814 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 815 816 triggerResult = captureListener.getCaptureResultForRequest( 817 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES); 818 int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE); 819 aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE); 820 821 for (int i = 0; 822 i < MAX_TRIGGER_SEQUENCE_FRAMES && 823 !(focusComplete && precaptureComplete); 824 i++) { 825 826 focusComplete = verifyAfSequence(afMode, afState, focusComplete); 827 precaptureComplete = verifyAeSequence(aeState, precaptureComplete); 828 829 CaptureResult sequenceResult = captureListener.getCaptureResult( 830 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 831 afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE); 832 aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE); 833 } 834 835 assertTrue("Precapture sequence never completed!", precaptureComplete); 836 assertTrue("Focus sequence never completed!", focusComplete); 837 838 // Done 839 840 stopCapture(/*fast*/ false); 841 preview.release(); 842 843 } 844 } 845 } finally { 846 closeDevice(id); 847 } 848 } 849 } 850 prepareTriggerTestSession( SurfaceTexture preview, int aeMode, int afMode)851 private CaptureRequest.Builder prepareTriggerTestSession( 852 SurfaceTexture preview, int aeMode, int afMode) throws Exception { 853 Log.i(TAG, String.format("Testing AE mode %s, AF mode %s", 854 StaticMetadata.AE_MODE_NAMES[aeMode], 855 StaticMetadata.AF_MODE_NAMES[afMode])); 856 857 858 Surface previewSurface = new Surface(preview); 859 860 preview.setDefaultBufferSize(640, 480); 861 862 ArrayList<Surface> sessionOutputs = new ArrayList<>(); 863 sessionOutputs.add(previewSurface); 864 865 createSession(sessionOutputs); 866 867 CaptureRequest.Builder previewRequest = 868 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 869 870 previewRequest.addTarget(previewSurface); 871 872 previewRequest.set(CaptureRequest.CONTROL_AE_MODE, aeMode); 873 previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode); 874 875 return previewRequest; 876 } 877 cancelTriggersAndWait(CaptureRequest.Builder previewRequest, SimpleCaptureCallback captureListener, int afMode)878 private void cancelTriggersAndWait(CaptureRequest.Builder previewRequest, 879 SimpleCaptureCallback captureListener, int afMode) throws Exception { 880 previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER, 881 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); 882 previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 883 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL); 884 885 CaptureRequest triggerRequest = previewRequest.build(); 886 mCameraSession.capture(triggerRequest, captureListener, mHandler); 887 888 // Wait for a few frames to initialize 3A 889 890 CaptureResult previewResult = null; 891 int afState; 892 int aeState; 893 894 for (int i = 0; i < PREVIEW_WARMUP_FRAMES; i++) { 895 previewResult = captureListener.getCaptureResult( 896 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS); 897 if (VERBOSE) { 898 afState = previewResult.get(CaptureResult.CONTROL_AF_STATE); 899 aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE); 900 Log.v(TAG, String.format("AF state: %s, AE state: %s", 901 StaticMetadata.AF_STATE_NAMES[afState], 902 StaticMetadata.AE_STATE_NAMES[aeState])); 903 } 904 } 905 906 // Verify starting states 907 908 afState = previewResult.get(CaptureResult.CONTROL_AF_STATE); 909 aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE); 910 911 switch (afMode) { 912 case CaptureResult.CONTROL_AF_MODE_AUTO: 913 case CaptureResult.CONTROL_AF_MODE_MACRO: 914 assertTrue(String.format("AF state not INACTIVE, is %s", 915 StaticMetadata.AF_STATE_NAMES[afState]), 916 afState == CaptureResult.CONTROL_AF_STATE_INACTIVE); 917 break; 918 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE: 919 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO: 920 // After several frames, AF must no longer be in INACTIVE state 921 assertTrue(String.format("In AF mode %s, AF state not PASSIVE_SCAN" + 922 ", PASSIVE_FOCUSED, or PASSIVE_UNFOCUSED, is %s", 923 StaticMetadata.AF_MODE_NAMES[afMode], 924 StaticMetadata.AF_STATE_NAMES[afState]), 925 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN || 926 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED || 927 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED); 928 break; 929 default: 930 fail("unexpected af mode"); 931 } 932 933 // After several frames, AE must no longer be in INACTIVE state 934 assertTrue(String.format("AE state must be SEARCHING, CONVERGED, " + 935 "or FLASH_REQUIRED, is %s", StaticMetadata.AE_STATE_NAMES[aeState]), 936 aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING || 937 aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED || 938 aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED); 939 } 940 verifyAfSequence(int afMode, int afState, boolean focusComplete)941 private boolean verifyAfSequence(int afMode, int afState, boolean focusComplete) { 942 if (focusComplete) { 943 assertTrue(String.format("AF Mode %s: Focus lock lost after convergence: AF state: %s", 944 StaticMetadata.AF_MODE_NAMES[afMode], 945 StaticMetadata.AF_STATE_NAMES[afState]), 946 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 947 afState ==CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 948 return focusComplete; 949 } 950 if (VERBOSE) { 951 Log.v(TAG, String.format("AF mode: %s, AF state: %s", 952 StaticMetadata.AF_MODE_NAMES[afMode], 953 StaticMetadata.AF_STATE_NAMES[afState])); 954 } 955 switch (afMode) { 956 case CaptureResult.CONTROL_AF_MODE_AUTO: 957 case CaptureResult.CONTROL_AF_MODE_MACRO: 958 assertTrue(String.format("AF mode %s: Unexpected AF state %s", 959 StaticMetadata.AF_MODE_NAMES[afMode], 960 StaticMetadata.AF_STATE_NAMES[afState]), 961 afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN || 962 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 963 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 964 focusComplete = 965 (afState != CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN); 966 break; 967 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE: 968 assertTrue(String.format("AF mode %s: Unexpected AF state %s", 969 StaticMetadata.AF_MODE_NAMES[afMode], 970 StaticMetadata.AF_STATE_NAMES[afState]), 971 afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN || 972 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 973 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 974 focusComplete = 975 (afState != CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN); 976 break; 977 case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO: 978 assertTrue(String.format("AF mode %s: Unexpected AF state %s", 979 StaticMetadata.AF_MODE_NAMES[afMode], 980 StaticMetadata.AF_STATE_NAMES[afState]), 981 afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 982 afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 983 focusComplete = true; 984 break; 985 default: 986 fail("Unexpected AF mode: " + StaticMetadata.AF_MODE_NAMES[afMode]); 987 } 988 return focusComplete; 989 } 990 verifyAeSequence(int aeState, boolean precaptureComplete)991 private boolean verifyAeSequence(int aeState, boolean precaptureComplete) { 992 if (precaptureComplete) { 993 assertTrue("Precapture state seen after convergence", 994 aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE); 995 return precaptureComplete; 996 } 997 if (VERBOSE) { 998 Log.v(TAG, String.format("AE state: %s", StaticMetadata.AE_STATE_NAMES[aeState])); 999 } 1000 switch (aeState) { 1001 case CaptureResult.CONTROL_AE_STATE_PRECAPTURE: 1002 // scan still continuing 1003 break; 1004 case CaptureResult.CONTROL_AE_STATE_CONVERGED: 1005 case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED: 1006 // completed 1007 precaptureComplete = true; 1008 break; 1009 default: 1010 fail(String.format("Precapture sequence transitioned to " 1011 + "state %s incorrectly!", StaticMetadata.AE_STATE_NAMES[aeState])); 1012 break; 1013 } 1014 return precaptureComplete; 1015 } 1016 1017 /** 1018 * Sanity check the configuration tables. 1019 */ sanityCheckConfigurationTables(final int[][][] tables)1020 private void sanityCheckConfigurationTables(final int[][][] tables) throws Exception { 1021 int tableIdx = 0; 1022 for (int[][] table : tables) { 1023 int rowIdx = 0; 1024 for (int[] row : table) { 1025 assertTrue(String.format("Odd number of entries for table %d row %d: %s ", 1026 tableIdx, rowIdx, Arrays.toString(row)), 1027 (row.length % 2) == 0); 1028 for (int i = 0; i < row.length; i += 2) { 1029 int format = row[i]; 1030 int maxSize = row[i + 1]; 1031 assertTrue(String.format("table %d row %d index %d format not valid: %d", 1032 tableIdx, rowIdx, i, format), 1033 format == PRIV || format == JPEG || format == YUV || format == RAW); 1034 assertTrue(String.format("table %d row %d index %d max size not valid: %d", 1035 tableIdx, rowIdx, i + 1, maxSize), 1036 maxSize == PREVIEW || maxSize == RECORD || 1037 maxSize == MAXIMUM || maxSize == VGA); 1038 } 1039 rowIdx++; 1040 } 1041 tableIdx++; 1042 } 1043 } 1044 1045 /** 1046 * Simple holder for resolutions to use for different camera outputs and size limits. 1047 */ 1048 static class MaxStreamSizes { 1049 // Format shorthands 1050 static final int PRIV = ImageFormat.PRIVATE; 1051 static final int JPEG = ImageFormat.JPEG; 1052 static final int YUV = ImageFormat.YUV_420_888; 1053 static final int RAW = ImageFormat.RAW_SENSOR; 1054 1055 // Max resolution indices 1056 static final int PREVIEW = 0; 1057 static final int RECORD = 1; 1058 static final int MAXIMUM = 2; 1059 static final int VGA = 3; 1060 static final int RESOLUTION_COUNT = 4; 1061 MaxStreamSizes(StaticMetadata sm, String cameraId, Context context)1062 public MaxStreamSizes(StaticMetadata sm, String cameraId, Context context) { 1063 Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE, 1064 StaticMetadata.StreamDirection.Output); 1065 Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888, 1066 StaticMetadata.StreamDirection.Output); 1067 Size[] jpegSizes = sm.getJpegOutputSizesChecked(); 1068 Size[] rawSizes = sm.getRawOutputSizesChecked(); 1069 1070 Size maxPreviewSize = getMaxPreviewSize(context, cameraId); 1071 1072 maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null; 1073 1074 if (sm.isColorOutputSupported()) { 1075 maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize); 1076 maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, maxPreviewSize); 1077 maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize); 1078 1079 maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId); 1080 maxYuvSizes[RECORD] = getMaxRecordingSize(cameraId); 1081 maxJpegSizes[RECORD] = getMaxRecordingSize(cameraId); 1082 1083 maxPrivSizes[MAXIMUM] = CameraTestUtils.getMaxSize(privSizes); 1084 maxYuvSizes[MAXIMUM] = CameraTestUtils.getMaxSize(yuvSizes); 1085 maxJpegSizes[MAXIMUM] = CameraTestUtils.getMaxSize(jpegSizes); 1086 1087 // Must always be supported, add unconditionally 1088 final Size vgaSize = new Size(640, 480); 1089 maxPrivSizes[VGA] = vgaSize; 1090 maxYuvSizes[VGA] = vgaSize; 1091 maxJpegSizes[VGA] = vgaSize; 1092 } 1093 1094 StreamConfigurationMap configs = sm.getCharacteristics().get( 1095 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1096 Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE); 1097 maxInputPrivSize = privInputSizes != null ? 1098 CameraTestUtils.getMaxSize(privInputSizes) : null; 1099 Size[] yuvInputSizes = configs.getInputSizes(ImageFormat.YUV_420_888); 1100 maxInputYuvSize = yuvInputSizes != null ? 1101 CameraTestUtils.getMaxSize(yuvInputSizes) : null; 1102 1103 } 1104 1105 public final Size[] maxPrivSizes = new Size[RESOLUTION_COUNT]; 1106 public final Size[] maxJpegSizes = new Size[RESOLUTION_COUNT]; 1107 public final Size[] maxYuvSizes = new Size[RESOLUTION_COUNT]; 1108 public final Size maxRawSize; 1109 // TODO: support non maximum reprocess input. 1110 public final Size maxInputPrivSize; 1111 public final Size maxInputYuvSize; 1112 configToString(int[] config)1113 static public String configToString(int[] config) { 1114 StringBuilder b = new StringBuilder("{ "); 1115 for (int i = 0; i < config.length; i += 2) { 1116 int format = config[i]; 1117 int sizeLimit = config[i + 1]; 1118 1119 appendFormatSize(b, format, sizeLimit); 1120 b.append(" "); 1121 } 1122 b.append("}"); 1123 return b.toString(); 1124 } 1125 reprocessConfigToString(int[] reprocessConfig)1126 static public String reprocessConfigToString(int[] reprocessConfig) { 1127 // reprocessConfig[0..1] is the input configuration 1128 StringBuilder b = new StringBuilder("Input: "); 1129 appendFormatSize(b, reprocessConfig[0], reprocessConfig[1]); 1130 1131 // reprocessConfig[0..1] is also output configuration to be captured as reprocess input. 1132 b.append(", Outputs: { "); 1133 for (int i = 0; i < reprocessConfig.length; i += 2) { 1134 int format = reprocessConfig[i]; 1135 int sizeLimit = reprocessConfig[i + 1]; 1136 1137 appendFormatSize(b, format, sizeLimit); 1138 b.append(" "); 1139 } 1140 b.append("}"); 1141 return b.toString(); 1142 } 1143 appendFormatSize(StringBuilder b, int format, int Size)1144 static private void appendFormatSize(StringBuilder b, int format, int Size) { 1145 switch (format) { 1146 case PRIV: 1147 b.append("[PRIV, "); 1148 break; 1149 case JPEG: 1150 b.append("[JPEG, "); 1151 break; 1152 case YUV: 1153 b.append("[YUV, "); 1154 break; 1155 case RAW: 1156 b.append("[RAW, "); 1157 break; 1158 default: 1159 b.append("[UNK, "); 1160 break; 1161 } 1162 1163 switch (Size) { 1164 case PREVIEW: 1165 b.append("PREVIEW]"); 1166 break; 1167 case RECORD: 1168 b.append("RECORD]"); 1169 break; 1170 case MAXIMUM: 1171 b.append("MAXIMUM]"); 1172 break; 1173 case VGA: 1174 b.append("VGA]"); 1175 break; 1176 default: 1177 b.append("UNK]"); 1178 break; 1179 } 1180 } 1181 } 1182 1183 /** 1184 * Return an InputConfiguration for a given reprocess configuration. 1185 */ getInputConfig(int[] reprocessConfig, MaxStreamSizes maxSizes)1186 private InputConfiguration getInputConfig(int[] reprocessConfig, MaxStreamSizes maxSizes) { 1187 int format; 1188 Size size; 1189 1190 if (reprocessConfig[1] != MAXIMUM) { 1191 throw new IllegalArgumentException("Test only supports MAXIMUM input"); 1192 } 1193 1194 switch (reprocessConfig[0]) { 1195 case PRIV: 1196 format = ImageFormat.PRIVATE; 1197 size = maxSizes.maxInputPrivSize; 1198 break; 1199 case YUV: 1200 format = ImageFormat.YUV_420_888; 1201 size = maxSizes.maxInputYuvSize; 1202 break; 1203 default: 1204 throw new IllegalArgumentException("Input format not supported: " + 1205 reprocessConfig[0]); 1206 } 1207 1208 return new InputConfiguration(size.getWidth(), size.getHeight(), format); 1209 } 1210 testReprocessStreamCombination(String cameraId, int[] reprocessConfig, MaxStreamSizes maxSizes, StaticMetadata staticInfo)1211 private void testReprocessStreamCombination(String cameraId, int[] reprocessConfig, 1212 MaxStreamSizes maxSizes, StaticMetadata staticInfo) throws Exception { 1213 1214 Log.i(TAG, String.format("Testing Camera %s, reprocess config: %s", cameraId, 1215 MaxStreamSizes.reprocessConfigToString(reprocessConfig))); 1216 1217 final int TIMEOUT_FOR_RESULT_MS = 3000; 1218 final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3; 1219 1220 List<SurfaceTexture> privTargets = new ArrayList<>(); 1221 List<ImageReader> jpegTargets = new ArrayList<>(); 1222 List<ImageReader> yuvTargets = new ArrayList<>(); 1223 List<ImageReader> rawTargets = new ArrayList<>(); 1224 List<Surface> outputSurfaces = new ArrayList<>(); 1225 ImageReader inputReader = null; 1226 ImageWriter inputWriter = null; 1227 SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener(); 1228 SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback(); 1229 SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback(); 1230 1231 boolean supportYuvReprocess = staticInfo.isCapabilitySupported( 1232 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING); 1233 boolean supportOpaqueReprocess = staticInfo.isCapabilitySupported( 1234 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING); 1235 1236 // Skip the configuration if the format is not supported for reprocessing. 1237 if ((reprocessConfig[0] == YUV && !supportYuvReprocess) || 1238 (reprocessConfig[0] == PRIV && !supportOpaqueReprocess)) { 1239 return; 1240 } 1241 1242 try { 1243 // reprocessConfig[2..] are additional outputs 1244 setupConfigurationTargets( 1245 Arrays.copyOfRange(reprocessConfig, 2, reprocessConfig.length), 1246 maxSizes, privTargets, jpegTargets, yuvTargets, rawTargets, outputSurfaces, 1247 NUM_REPROCESS_CAPTURES_PER_CONFIG); 1248 1249 // reprocessConfig[0:1] is input 1250 InputConfiguration inputConfig = getInputConfig( 1251 Arrays.copyOfRange(reprocessConfig, 0, 2), maxSizes); 1252 1253 // For each config, YUV and JPEG outputs will be tested. (For YUV reprocessing, 1254 // the YUV ImageReader for input is also used for output.) 1255 final int totalNumReprocessCaptures = NUM_REPROCESS_CAPTURES_PER_CONFIG * ( 1256 (inputConfig.getFormat() == ImageFormat.YUV_420_888 ? 1 : 0) + 1257 jpegTargets.size() + yuvTargets.size()); 1258 1259 // It needs 1 input buffer for each reprocess capture + the number of buffers 1260 // that will be used as outputs. 1261 inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(), 1262 inputConfig.getFormat(), 1263 totalNumReprocessCaptures + NUM_REPROCESS_CAPTURES_PER_CONFIG); 1264 inputReader.setOnImageAvailableListener(inputReaderListener, mHandler); 1265 outputSurfaces.add(inputReader.getSurface()); 1266 1267 // Verify we can create a reprocessable session with the input and all outputs. 1268 BlockingSessionCallback sessionListener = new BlockingSessionCallback(); 1269 CameraCaptureSession session = configureReprocessableCameraSession(mCamera, 1270 inputConfig, outputSurfaces, sessionListener, mHandler); 1271 inputWriter = ImageWriter.newInstance(session.getInputSurface(), 1272 totalNumReprocessCaptures); 1273 1274 // Prepare a request for reprocess input 1275 CaptureRequest.Builder builder = mCamera.createCaptureRequest( 1276 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG); 1277 builder.addTarget(inputReader.getSurface()); 1278 1279 for (int i = 0; i < totalNumReprocessCaptures; i++) { 1280 session.capture(builder.build(), inputCaptureListener, mHandler); 1281 } 1282 1283 List<CaptureRequest> reprocessRequests = new ArrayList<>(); 1284 List<Surface> reprocessOutputs = new ArrayList<>(); 1285 if (inputConfig.getFormat() == ImageFormat.YUV_420_888) { 1286 reprocessOutputs.add(inputReader.getSurface()); 1287 } 1288 1289 for (ImageReader reader : jpegTargets) { 1290 reprocessOutputs.add(reader.getSurface()); 1291 } 1292 1293 for (ImageReader reader : yuvTargets) { 1294 reprocessOutputs.add(reader.getSurface()); 1295 } 1296 1297 for (int i = 0; i < NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) { 1298 for (Surface output : reprocessOutputs) { 1299 TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult( 1300 TIMEOUT_FOR_RESULT_MS); 1301 builder = mCamera.createReprocessCaptureRequest(result); 1302 inputWriter.queueInputImage( 1303 inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS)); 1304 builder.addTarget(output); 1305 reprocessRequests.add(builder.build()); 1306 } 1307 } 1308 1309 session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler); 1310 1311 for (int i = 0; i < reprocessOutputs.size() * NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) { 1312 TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult( 1313 TIMEOUT_FOR_RESULT_MS); 1314 } 1315 } catch (Throwable e) { 1316 mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s", 1317 MaxStreamSizes.reprocessConfigToString(reprocessConfig), e.getMessage())); 1318 } finally { 1319 inputReaderListener.drain(); 1320 reprocessOutputCaptureListener.drain(); 1321 1322 for (SurfaceTexture target : privTargets) { 1323 target.release(); 1324 } 1325 1326 for (ImageReader target : jpegTargets) { 1327 target.close(); 1328 } 1329 1330 for (ImageReader target : yuvTargets) { 1331 target.close(); 1332 } 1333 1334 for (ImageReader target : rawTargets) { 1335 target.close(); 1336 } 1337 1338 if (inputReader != null) { 1339 inputReader.close(); 1340 } 1341 1342 if (inputWriter != null) { 1343 inputWriter.close(); 1344 } 1345 } 1346 } 1347 testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)1348 private void testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes) 1349 throws Exception { 1350 1351 Log.i(TAG, String.format("Testing Camera %s, config %s", 1352 cameraId, MaxStreamSizes.configToString(config))); 1353 1354 // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS 1355 final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000; 1356 final int MIN_RESULT_COUNT = 3; 1357 1358 // Set up outputs 1359 List<Surface> outputSurfaces = new ArrayList<Surface>(); 1360 List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>(); 1361 List<ImageReader> jpegTargets = new ArrayList<ImageReader>(); 1362 List<ImageReader> yuvTargets = new ArrayList<ImageReader>(); 1363 List<ImageReader> rawTargets = new ArrayList<ImageReader>(); 1364 1365 setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets, 1366 rawTargets, outputSurfaces, MIN_RESULT_COUNT); 1367 1368 boolean haveSession = false; 1369 try { 1370 CaptureRequest.Builder requestBuilder = 1371 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 1372 1373 for (Surface s : outputSurfaces) { 1374 requestBuilder.addTarget(s); 1375 } 1376 1377 CameraCaptureSession.CaptureCallback mockCaptureCallback = 1378 mock(CameraCaptureSession.CaptureCallback.class); 1379 1380 createSession(outputSurfaces); 1381 haveSession = true; 1382 CaptureRequest request = requestBuilder.build(); 1383 mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler); 1384 1385 verify(mockCaptureCallback, 1386 timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT)) 1387 .onCaptureCompleted( 1388 eq(mCameraSession), 1389 eq(request), 1390 isA(TotalCaptureResult.class)); 1391 verify(mockCaptureCallback, never()). 1392 onCaptureFailed( 1393 eq(mCameraSession), 1394 eq(request), 1395 isA(CaptureFailure.class)); 1396 1397 } catch (Throwable e) { 1398 mCollector.addMessage(String.format("Output combination %s failed due to: %s", 1399 MaxStreamSizes.configToString(config), e.getMessage())); 1400 } 1401 if (haveSession) { 1402 try { 1403 Log.i(TAG, String.format("Done with camera %s, config %s, closing session", 1404 cameraId, MaxStreamSizes.configToString(config))); 1405 stopCapture(/*fast*/false); 1406 } catch (Throwable e) { 1407 mCollector.addMessage( 1408 String.format("Closing down for output combination %s failed due to: %s", 1409 MaxStreamSizes.configToString(config), e.getMessage())); 1410 } 1411 } 1412 1413 for (SurfaceTexture target : privTargets) { 1414 target.release(); 1415 } 1416 for (ImageReader target : jpegTargets) { 1417 target.close(); 1418 } 1419 for (ImageReader target : yuvTargets) { 1420 target.close(); 1421 } 1422 for (ImageReader target : rawTargets) { 1423 target.close(); 1424 } 1425 } 1426 setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes, List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets, List<ImageReader> yuvTargets, List<ImageReader> rawTargets, List<Surface> outputSurfaces, int numBuffers)1427 private void setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes, 1428 List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets, 1429 List<ImageReader> yuvTargets, List<ImageReader> rawTargets, 1430 List<Surface> outputSurfaces, int numBuffers) { 1431 1432 ImageDropperListener imageDropperListener = new ImageDropperListener(); 1433 1434 for (int i = 0; i < outputConfigs.length; i += 2) { 1435 int format = outputConfigs[i]; 1436 int sizeLimit = outputConfigs[i + 1]; 1437 1438 switch (format) { 1439 case PRIV: { 1440 Size targetSize = maxSizes.maxPrivSizes[sizeLimit]; 1441 SurfaceTexture target = new SurfaceTexture(/*random int*/1); 1442 target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight()); 1443 outputSurfaces.add(new Surface(target)); 1444 privTargets.add(target); 1445 break; 1446 } 1447 case JPEG: { 1448 Size targetSize = maxSizes.maxJpegSizes[sizeLimit]; 1449 ImageReader target = ImageReader.newInstance( 1450 targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers); 1451 target.setOnImageAvailableListener(imageDropperListener, mHandler); 1452 outputSurfaces.add(target.getSurface()); 1453 jpegTargets.add(target); 1454 break; 1455 } 1456 case YUV: { 1457 Size targetSize = maxSizes.maxYuvSizes[sizeLimit]; 1458 ImageReader target = ImageReader.newInstance( 1459 targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers); 1460 target.setOnImageAvailableListener(imageDropperListener, mHandler); 1461 outputSurfaces.add(target.getSurface()); 1462 yuvTargets.add(target); 1463 break; 1464 } 1465 case RAW: { 1466 Size targetSize = maxSizes.maxRawSize; 1467 ImageReader target = ImageReader.newInstance( 1468 targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers); 1469 target.setOnImageAvailableListener(imageDropperListener, mHandler); 1470 outputSurfaces.add(target.getSurface()); 1471 rawTargets.add(target); 1472 break; 1473 } 1474 default: 1475 fail("Unknown output format " + format); 1476 } 1477 } 1478 } 1479 getMaxRecordingSize(String cameraId)1480 private static Size getMaxRecordingSize(String cameraId) { 1481 int id = Integer.valueOf(cameraId); 1482 1483 int quality = 1484 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_2160P) ? 1485 CamcorderProfile.QUALITY_2160P : 1486 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_1080P) ? 1487 CamcorderProfile.QUALITY_1080P : 1488 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_720P) ? 1489 CamcorderProfile.QUALITY_720P : 1490 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_480P) ? 1491 CamcorderProfile.QUALITY_480P : 1492 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QVGA) ? 1493 CamcorderProfile.QUALITY_QVGA : 1494 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_CIF) ? 1495 CamcorderProfile.QUALITY_CIF : 1496 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QCIF) ? 1497 CamcorderProfile.QUALITY_QCIF : 1498 -1; 1499 1500 assertTrue("No recording supported for camera id " + cameraId, quality != -1); 1501 1502 CamcorderProfile maxProfile = CamcorderProfile.get(id, quality); 1503 return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight); 1504 } 1505 1506 /** 1507 * Get maximum size in list that's equal or smaller to than the bound. 1508 * Returns null if no size is smaller than or equal to the bound. 1509 */ getMaxSize(Size[] sizes, Size bound)1510 private static Size getMaxSize(Size[] sizes, Size bound) { 1511 if (sizes == null || sizes.length == 0) { 1512 throw new IllegalArgumentException("sizes was empty"); 1513 } 1514 1515 Size sz = null; 1516 for (Size size : sizes) { 1517 if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) { 1518 1519 if (sz == null) { 1520 sz = size; 1521 } else { 1522 long curArea = sz.getWidth() * (long) sz.getHeight(); 1523 long newArea = size.getWidth() * (long) size.getHeight(); 1524 if ( newArea > curArea ) { 1525 sz = size; 1526 } 1527 } 1528 } 1529 } 1530 1531 assertTrue("No size under bound found: " + Arrays.toString(sizes) + " bound " + bound, 1532 sz != null); 1533 1534 return sz; 1535 } 1536 getMaxPreviewSize(Context context, String cameraId)1537 private static Size getMaxPreviewSize(Context context, String cameraId) { 1538 try { 1539 WindowManager windowManager = 1540 (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 1541 Display display = windowManager.getDefaultDisplay(); 1542 1543 int width = display.getWidth(); 1544 int height = display.getHeight(); 1545 1546 if (height > width) { 1547 height = width; 1548 width = display.getHeight(); 1549 } 1550 1551 CameraManager camMgr = 1552 (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 1553 List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes( 1554 cameraId, camMgr, PREVIEW_SIZE_BOUND); 1555 1556 if (orderedPreviewSizes != null) { 1557 for (Size size : orderedPreviewSizes) { 1558 if (width >= size.getWidth() && 1559 height >= size.getHeight()) 1560 return size; 1561 } 1562 } 1563 } catch (Exception e) { 1564 Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString()); 1565 } 1566 return PREVIEW_SIZE_BOUND; 1567 } 1568 } 1569