camera cts: testFlashTurnOff(v2): Check on->off->on transitions.
Check on -> off -> on transitions for torch and also allow a certain
number of FLASH_STATE_PARTIAL states an transition edges.
Bug: 137224452
Test: runtest -x CaptureRequestTest.java -m testFlashTurnOff on various
Pixel devices.
Change-Id: I41f676efa7ca8215881348c37b982b0f80f16ee7
Signed-off-by: Jayant Chowdhary <jchowdhary@google.com>
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 2dbe878..2a46cf7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -86,6 +86,9 @@
private static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
private static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
private static final int NUM_FRAMES_WAITED_FOR_TORCH = 100;
+ private static final int NUM_PARTIAL_FRAMES_PFC = 2;
+ private static final int NUM_PARTIAL_FRAMES_NPFC = 6;
+
private static final int NUM_TEST_FOCUS_DISTANCES = 10;
private static final int NUM_FOCUS_DISTANCES_REPEAT = 3;
// 5 percent error margin for calibrated device
@@ -525,7 +528,6 @@
flashTurnOffTest(listener,
/* initiaAeControl */CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH,
/* offAeControl */CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
-
flashTurnOffTest(listener,
/* initiaAeControl */CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH,
/* offAeControl */CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
@@ -1479,17 +1481,123 @@
// Test that the flash actually turned on continuously.
mCollector.expectEquals("Flash state result must be FIRED", CaptureResult.FLASH_STATE_FIRED,
result.get(CaptureResult.FLASH_STATE));
-
+ mSession.stopRepeating();
// Turn off the torch
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, flashOffAeControl);
// TODO: jchowdhary@, b/130323585, this line can be removed.
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
+ int numAllowedTransitionStates = NUM_PARTIAL_FRAMES_NPFC;
+ if (mStaticInfo.isPerFrameControlSupported()) {
+ numAllowedTransitionStates = NUM_PARTIAL_FRAMES_PFC;
+
+ }
+ // We submit 2 * numAllowedTransitionStates + 1 requests since we have two torch mode
+ // transitions. The additional request is to check for at least 1 expected (FIRED / READY)
+ // state.
+ int numTorchTestSamples = 2 * numAllowedTransitionStates + 1;
CaptureRequest flashOffRequest = requestBuilder.build();
- mSession.setRepeatingRequest(flashOffRequest, listener, mHandler);
- waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_TORCH);
- result = listener.getCaptureResultForRequest(flashOffRequest, NUM_RESULTS_WAIT_TIMEOUT);
- mCollector.expectEquals("Flash state result must be READY", CaptureResult.FLASH_STATE_READY,
- result.get(CaptureResult.FLASH_STATE));
+ int flashModeOffRequests = captureRequestsSynchronizedBurst(flashOffRequest,
+ numTorchTestSamples, listener, mHandler);
+ // Turn it on again.
+ requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
+ // We need to have CONTROL_AE_MODE be either CONTROL_AE_MODE_ON or CONTROL_AE_MODE_OFF to
+ // turn the torch on again.
+ requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
+ CaptureRequest flashModeTorchRequest = requestBuilder.build();
+ int flashModeTorchRequests = captureRequestsSynchronizedBurst(flashModeTorchRequest,
+ numTorchTestSamples, listener, mHandler);
+
+ CaptureResult[] torchStateResults =
+ new CaptureResult[flashModeTorchRequests + flashModeOffRequests];
+ Arrays.fill(torchStateResults, null);
+ int i = 0;
+ for (; i < flashModeOffRequests; i++) {
+ torchStateResults[i] =
+ listener.getCaptureResultForRequest(flashOffRequest, NUM_RESULTS_WAIT_TIMEOUT);
+ mCollector.expectNotEquals("Result for flashModeOff request null",
+ torchStateResults[i], null);
+ }
+ for (int j = i; j < torchStateResults.length; j++) {
+ torchStateResults[j] =
+ listener.getCaptureResultForRequest(flashModeTorchRequest,
+ NUM_RESULTS_WAIT_TIMEOUT);
+ mCollector.expectNotEquals("Result for flashModeTorch request null",
+ torchStateResults[j], null);
+ }
+ checkTorchStates(torchStateResults, numAllowedTransitionStates, flashModeOffRequests,
+ flashModeTorchRequests);
+ }
+
+ // We check that torch states appear in the order expected. We don't necessarily know how many
+ // times each state might appear, however we make sure that the states do not appear out of
+ // order.
+ private void checkTorchTransitionStates(CaptureResult []torchStateResults, int beg, int end,
+ List<Integer> stateOrder, boolean isTurningOff) {
+ Integer flashState;
+ Integer curIndex = 0;
+ for (int i = beg; i <= end; i++) {
+ flashState = torchStateResults[i].get(CaptureResult.FLASH_STATE);
+ int index = stateOrder.indexOf(flashState);
+ mCollector.expectNotEquals("Invalid state " + flashState + " not in expected list" +
+ stateOrder, index, -1);
+ mCollector.expectGreaterOrEqual("state " + flashState + " index " + index +
+ " is expected to be >= " + curIndex,
+ curIndex, index);
+ curIndex = index;
+ }
+ }
+
+ private void checkTorchStates(CaptureResult []torchResults, int numAllowedTransitionStates,
+ int numTorchOffSamples, int numTorchOnSamples) {
+ // We test for flash states from request:
+ // Request: O(0) O(1) O(2) O(n)....O(nOFF) T(0) T(1) T(2) ....T(n) .... T(nON)
+ // Valid Result : P/R P/R P/R R R R...P/R P/R P/F P/F P/F F F
+ // For the FLASH_STATE_OFF requests, once FLASH_STATE READY has been seen, for the
+ // transition states while switching the torch off, it must not transition to
+ // FLASH_STATE_PARTIAL again till the next transition period which turns the torch on.
+ // P - FLASH_STATE_PARTIAL
+ // R - FLASH_STATE_READY
+ // F - FLASH_STATE_FIRED
+ // O(k) - kth FLASH_MODE_OFF request
+ // T(k) - kth FLASH_MODE_TORCH request
+ // nOFF - number of torch off samples
+ // nON - number of torch on samples
+ Integer flashState;
+ // Check on -> off transition states
+ List<Integer> onToOffStateOrderList = new ArrayList<Integer>();
+ onToOffStateOrderList.add(CaptureRequest.FLASH_STATE_PARTIAL);
+ onToOffStateOrderList.add(CaptureRequest.FLASH_STATE_READY);
+ checkTorchTransitionStates(torchResults, 0, numAllowedTransitionStates,
+ onToOffStateOrderList, true);
+ // The next frames (before transition) must have its flash state as FLASH_STATE_READY
+ for (int i = numAllowedTransitionStates + 1;
+ i < numTorchOffSamples - numAllowedTransitionStates; i++) {
+ flashState = torchResults[numAllowedTransitionStates].get(CaptureResult.FLASH_STATE);
+ mCollector.expectEquals("flash state result must be READY",
+ CaptureResult.FLASH_STATE_READY, flashState);
+ }
+ // check off -> on transition states, before the FLASH_MODE_TORCH request was sent
+ List<Integer> offToOnPreStateOrderList = new ArrayList<Integer>();
+ offToOnPreStateOrderList.add(CaptureRequest.FLASH_STATE_READY);
+ offToOnPreStateOrderList.add(CaptureRequest.FLASH_STATE_PARTIAL);
+ checkTorchTransitionStates(torchResults,
+ numTorchOffSamples - numAllowedTransitionStates, numTorchOffSamples - 1,
+ offToOnPreStateOrderList, false);
+ // check off -> on transition states
+ List<Integer> offToOnPostStateOrderList = new ArrayList<Integer>();
+ offToOnPostStateOrderList.add(CaptureRequest.FLASH_STATE_PARTIAL);
+ offToOnPostStateOrderList.add(CaptureRequest.FLASH_STATE_FIRED);
+ checkTorchTransitionStates(torchResults,
+ numTorchOffSamples, numTorchOffSamples + numAllowedTransitionStates,
+ offToOnPostStateOrderList, false);
+ // check on states after off -> on transition
+ // The next frames must have its flash state as FLASH_STATE_FIRED
+ for (int i = numTorchOffSamples + numAllowedTransitionStates + 1;
+ i < torchResults.length - 1; i++) {
+ flashState = torchResults[i].get(CaptureResult.FLASH_STATE);
+ mCollector.expectEquals("flash state result must be FIRED for frame " + i,
+ CaptureRequest.FLASH_STATE_FIRED, flashState);
+ }
}
/**
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 1f8b792..5a95d62 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -385,6 +385,31 @@
}
/**
+ * Submit a burst of the same capture request, then submit additional captures in order to
+ * ensure that the camera will be synchronized.
+ *
+ * <p>
+ * The additional capture count is determined by android.sync.maxLatency (or
+ * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
+ * </p>
+ *
+ * <p>Returns the number of captures that were submitted (at least 1), which is useful
+ * with {@link #waitForNumResults}.</p>
+ *
+ * @param request capture request to forward to {@link CameraDevice#capture}
+ * @param listener request listener to forward to {@link CameraDevice#capture}
+ * @param handler handler to forward to {@link CameraDevice#capture}
+ *
+ * @return the number of captures that were submitted
+ *
+ * @throws CameraAccessException if capturing failed
+ */
+ protected int captureRequestsSynchronizedBurst(
+ CaptureRequest request, int count, CaptureCallback listener, Handler handler)
+ throws CameraAccessException {
+ return captureRequestsSynchronizedImpl(request, count, listener, handler, true);
+ }
+ /**
* Submit a capture once, then submit additional captures in order to ensure that
* the camera will be synchronized.
*
@@ -407,7 +432,7 @@
protected int captureRequestsSynchronized(
CaptureRequest request, CaptureCallback listener, Handler handler)
throws CameraAccessException {
- return captureRequestsSynchronized(request, /*count*/1, listener, handler);
+ return captureRequestsSynchronizedImpl(request, /*count*/1, listener, handler, false);
}
/**
@@ -435,24 +460,7 @@
protected int captureRequestsSynchronized(
CaptureRequest request, int count, CaptureCallback listener, Handler handler)
throws CameraAccessException {
- if (count < 1) {
- throw new IllegalArgumentException("count must be positive");
- }
-
- int maxLatency = mStaticInfo.getSyncMaxLatency();
- if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
- maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
- }
-
- assertTrue("maxLatency is non-negative", maxLatency >= 0);
-
- int numCaptures = maxLatency + count;
-
- for (int i = 0; i < numCaptures; ++i) {
- mSession.capture(request, listener, handler);
- }
-
- return numCaptures;
+ return captureRequestsSynchronizedImpl(request, count, listener, handler, false);
}
/**
@@ -878,4 +886,34 @@
protected Range<Integer> getSuitableFpsRangeForDuration(String cameraId, long frameDuration) {
return CameraTestUtils.getSuitableFpsRangeForDuration(cameraId, frameDuration, mStaticInfo);
}
+
+ private int captureRequestsSynchronizedImpl(
+ CaptureRequest request, int count, CaptureCallback listener, Handler handler,
+ boolean isBurst) throws CameraAccessException {
+ if (count < 1) {
+ throw new IllegalArgumentException("count must be positive");
+ }
+
+ int maxLatency = mStaticInfo.getSyncMaxLatency();
+ if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
+ maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
+ }
+
+ assertTrue("maxLatency is non-negative", maxLatency >= 0);
+
+ int numCaptures = maxLatency + count;
+ ArrayList<CaptureRequest> burstCaptureRequests = new ArrayList<>();
+ for (int i = 0; i < numCaptures; ++i) {
+ if (isBurst) {
+ burstCaptureRequests.add(request);
+ } else {
+ mSession.capture(request, listener, handler);
+ }
+ }
+ if (isBurst) {
+ mSession.captureBurst(burstCaptureRequests, listener, handler);
+ }
+
+ return numCaptures;
+ }
}