Camera2: Fix anti-flickering check

The orignal check was too eager, we should make the test more robust. Also
fixed some request/result synchronization issues.

Change-Id: Ie6c66f8763ea7ba263758055cae57cb6c3b0e382
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 99143fd..4bd159b 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -59,7 +59,7 @@
     private static final int MIN_SHADING_MAP_SIZE = 1 * 1 * RGGB_COLOR_CHANNEL_COUNT;
     private static final long IGORE_REQUESTED_EXPOSURE_TIME_CHECK = -1L;
     private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
-    private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8300000L; // 8.3ms, Approximation.
+    private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8333333L; // 8.3ms, Approximation.
     private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
     private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
     private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
@@ -68,6 +68,8 @@
     private static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
     private static final int NUM_TEST_FOCUS_DISTANCES = 10;
     private static final float FOCUS_DISTANCE_ERROR_PERCENT = 0.05f; // 5 percent error margin
+    private static final int ANTI_FLICKERING_50HZ = 1;
+    private static final int ANTI_FLICKERING_60HZ = 2;
 
     // Linear tone mapping curve example.
     private static final float[] TONEMAP_CURVE_LINEAR = {0, 0, 1.0f, 1.0f};
@@ -223,15 +225,13 @@
                     continue;
                 }
 
-                SimpleCaptureListener listener = new SimpleCaptureListener();
-
                 byte[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
 
                 Size previewSz =
                         getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
 
                 for (byte mode : modes) {
-                    antiBandingTestByMode(listener, previewSz, mode);
+                    antiBandingTestByMode(previewSz, mode);
                 }
             } finally {
                 closeDevice();
@@ -698,6 +698,12 @@
 
     private void verifyAntiBandingMode(SimpleCaptureListener listener, int numFramesVerified,
             int mode, boolean isAeManual, long requestExpTime) throws Exception {
+        // Skip the first a couple of frames as antibanding may not be fully up yet.
+        final int NUM_FRAMES_SKIPPED = 5;
+        for (int i = 0; i < NUM_FRAMES_SKIPPED; i++) {
+            listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        }
+
         for (int i = 0; i < numFramesVerified; i++) {
             CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
             Long resultExpTime = result.get(CaptureRequest.SENSOR_EXPOSURE_TIME);
@@ -718,11 +724,11 @@
             if (mode == CONTROL_AE_ANTIBANDING_MODE_50HZ) {
                 // result exposure time must be adjusted by 50Hz illuminant source.
                 expectedExpTime =
-                        resultExpTime - (resultExpTime % EXPOSURE_TIME_BOUNDARY_50HZ_NS);
+                        getAntiFlickeringExposureTime(ANTI_FLICKERING_50HZ, resultExpTime);
             } else if (mode == CONTROL_AE_ANTIBANDING_MODE_60HZ) {
                 // result exposure time must be adjusted by 60Hz illuminant source.
                 expectedExpTime =
-                        resultExpTime - (resultExpTime % EXPOSURE_TIME_BOUNDARY_60HZ_NS);
+                        getAntiFlickeringExposureTime(ANTI_FLICKERING_60HZ, resultExpTime);
             } else if (mode == CONTROL_AE_ANTIBANDING_MODE_AUTO){
                 /**
                  * Use STATISTICS_SCENE_FLICKER to tell the illuminant source
@@ -730,11 +736,11 @@
                  */
                 expectedExpTime = resultExpTime;
                 if (flicker == STATISTICS_SCENE_FLICKER_60HZ) {
-                    expectedExpTime = resultExpTime
-                            - (resultExpTime % EXPOSURE_TIME_BOUNDARY_60HZ_NS);
+                    expectedExpTime =
+                            getAntiFlickeringExposureTime(ANTI_FLICKERING_60HZ, resultExpTime);
                 } else if (flicker == STATISTICS_SCENE_FLICKER_50HZ) {
-                    expectedExpTime = resultExpTime
-                            - (resultExpTime % EXPOSURE_TIME_BOUNDARY_50HZ_NS);
+                    expectedExpTime =
+                            getAntiFlickeringExposureTime(ANTI_FLICKERING_50HZ, resultExpTime);
                 }
             }
 
@@ -746,7 +752,7 @@
         }
     }
 
-    private void antiBandingTestByMode(SimpleCaptureListener listener, Size size, int mode)
+    private void antiBandingTestByMode(Size size, int mode)
             throws Exception {
         if(VERBOSE) {
             Log.v(TAG, "Anti-banding test for mode " + mode + " for camera " + mCamera.getId());
@@ -757,17 +763,20 @@
         requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, mode);
 
         // Test auto AE mode anti-banding behavior
-        startPreview(requestBuilder, size, listener);
-        verifyAntiBandingMode(listener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/false,
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, size, resultListener);
+        verifyAntiBandingMode(resultListener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/false,
                 IGORE_REQUESTED_EXPOSURE_TIME_CHECK);
 
         // Test manual AE mode anti-banding behavior
         // 65ms, must be supported by full capability devices.
         final long TEST_MANUAL_EXP_TIME_NS = 65000000L;
-        changeExposure(requestBuilder, TEST_MANUAL_EXP_TIME_NS);
-        startPreview(requestBuilder, size, listener);
-        verifyAntiBandingMode(listener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/true,
-                TEST_MANUAL_EXP_TIME_NS);
+        long manualExpTime = mStaticInfo.getExposureClampToRange(TEST_MANUAL_EXP_TIME_NS);
+        changeExposure(requestBuilder, manualExpTime);
+        resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, size, resultListener);
+        verifyAntiBandingMode(resultListener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/true,
+                manualExpTime);
 
         stopPreview();
     }
@@ -1448,4 +1457,39 @@
 
         mCamera.stopRepeating();
     }
+
+    /**
+     * Calculate the anti-flickering corrected exposure time.
+     * <p>
+     * If the input exposure time is very short (shorter than flickering
+     * boundary), which indicate the scene is bright and very likely at outdoor
+     * environment, skip the correction, as it doesn't make much sense by doing so.
+     * </p>
+     * <p>
+     * For long exposure time (larger than the flickering boundary), find the
+     * exposure time that is closest to the flickering boundary.
+     * </p>
+     *
+     * @param flickeringMode The flickering mode
+     * @param exposureTime The input exposureTime to be corrected
+     * @return anti-flickering corrected exposure time
+     */
+    private long getAntiFlickeringExposureTime(int flickeringMode, long exposureTime) {
+        if (flickeringMode != ANTI_FLICKERING_50HZ && flickeringMode != ANTI_FLICKERING_60HZ) {
+            throw new IllegalArgumentException("Input anti-flickering mode must be 50 or 60Hz");
+        }
+        long flickeringBoundary = EXPOSURE_TIME_BOUNDARY_50HZ_NS;
+        if (flickeringMode == ANTI_FLICKERING_60HZ) {
+            flickeringBoundary = EXPOSURE_TIME_BOUNDARY_60HZ_NS;
+        }
+
+        if (exposureTime <= flickeringBoundary) {
+            return exposureTime;
+        }
+
+        // Find the closest anti-flickering corrected exposure time
+        long correctedExpTime = exposureTime + (flickeringBoundary / 2);
+        correctedExpTime = correctedExpTime - (correctedExpTime % flickeringBoundary);
+        return correctedExpTime;
+    }
 }