Merge "Update FLP HAL definition for thread semantics."
diff --git a/include/hardware/hwcomposer.h b/include/hardware/hwcomposer.h
index d75a047..9492d3a 100644
--- a/include/hardware/hwcomposer.h
+++ b/include/hardware/hwcomposer.h
@@ -54,6 +54,13 @@
     int bottom;
 } hwc_rect_t;
 
+typedef struct hwc_frect {
+    float left;
+    float top;
+    float right;
+    float bottom;
+} hwc_frect_t;
+
 typedef struct hwc_region {
     size_t numRects;
     hwc_rect_t const* rects;
@@ -149,8 +156,17 @@
             int32_t blending;
 
             /* area of the source to consider, the origin is the top-left corner of
-             * the buffer */
-            hwc_rect_t sourceCrop;
+             * the buffer. As of HWC_DEVICE_API_VERSION_1_3, sourceRect uses floats.
+             * If the h/w can't support a non-integer source crop rectangle, it should
+             * punt to OpenGL ES composition.
+             */
+            union {
+                // crop rectangle in integer (pre HWC_DEVICE_API_VERSION_1_3)
+                hwc_rect_t sourceCropi;
+                hwc_rect_t sourceCrop; // just for source compatibility
+                // crop rectangle in floats (as of HWC_DEVICE_API_VERSION_1_3)
+                hwc_frect_t sourceCropf;
+            };
 
             /* where to composite the sourceCrop onto the display. The sourceCrop
              * is scaled using linear filtering to the displayFrame. The origin is the
diff --git a/include/hardware/hwcomposer_defs.h b/include/hardware/hwcomposer_defs.h
index 1edfd3d..ce4723c 100644
--- a/include/hardware/hwcomposer_defs.h
+++ b/include/hardware/hwcomposer_defs.h
@@ -35,6 +35,7 @@
 #define HWC_DEVICE_API_VERSION_1_0  HARDWARE_DEVICE_API_VERSION_2(1, 0, HWC_HEADER_VERSION)
 #define HWC_DEVICE_API_VERSION_1_1  HARDWARE_DEVICE_API_VERSION_2(1, 1, HWC_HEADER_VERSION)
 #define HWC_DEVICE_API_VERSION_1_2  HARDWARE_DEVICE_API_VERSION_2(1, 2, HWC_HEADER_VERSION)
+#define HWC_DEVICE_API_VERSION_1_3  HARDWARE_DEVICE_API_VERSION_2(1, 3, HWC_HEADER_VERSION)
 
 enum {
     /* hwc_composer_device_t::set failed in EGL */
diff --git a/include/hardware/sensors.h b/include/hardware/sensors.h
index f938d1b..b377051 100644
--- a/include/hardware/sensors.h
+++ b/include/hardware/sensors.h
@@ -451,6 +451,9 @@
  *  SENSOR_TYPE_MAGNETIC_FIELD must be present and both must return the
  *  same sensor_t::name and sensor_t::vendor.
  *
+ *  Minimum filtering should be applied to this sensor. In particular, low pass
+ *  filters should be avoided.
+ *
  * See SENSOR_TYPE_MAGNETIC_FIELD for more information
  */
 #define SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED     (14)
@@ -608,7 +611,7 @@
  *
  * A sensor of this type returns the number of steps taken by the user since
  * the last reboot while activated. The value is returned as a uint64_t and is
- * reset to zero only on a system reboot.
+ * reset to zero only on a system / android reboot.
  *
  * The timestamp of the event is set to the time when the first step
  * for that event was taken.
@@ -662,7 +665,9 @@
  *  of using a gyroscope.
  *
  *  This sensor must be based on a magnetometer. It cannot be implemented using
- *  a gyroscope, and gyroscope input cannot be used by this sensor.
+ *  a gyroscope, and gyroscope input cannot be used by this sensor, as the
+ *  goal of this sensor is to be low power.
+ *  The accelerometer can be (and usually is) used.
  *
  *  Just like SENSOR_TYPE_ROTATION_VECTOR, this sensor reports an estimated
  *  heading accuracy:
@@ -909,6 +914,10 @@
              * handle is the handle of the sensor to change.
              * enabled set to 1 to enable, or 0 to disable the sensor.
              *
+             * if enabled is set to 1, the sensor is activated even if
+             * setDelay() wasn't called before. In this case, a default rate
+             * should be used.
+             *
              * unless otherwise noted in the sensor types definitions, an
              * activated sensor never prevents the SoC to go into suspend
              * mode; that is, the HAL shall not hold a partial wake-lock on
@@ -918,10 +927,10 @@
              * receiving an event and they must still accept to be deactivated
              * through a call to activate(..., ..., 0).
              *
-             * if "enabled" is true and the sensor is already activated, this
+             * if "enabled" is 1 and the sensor is already activated, this
              * function is a no-op and succeeds.
              *
-             * if "enabled" is false and the sensor is already de-activated,
+             * if "enabled" is 0 and the sensor is already de-activated,
              * this function is a no-op and succeeds.
              *
              * return 0 on success, negative errno code otherwise
@@ -945,6 +954,9 @@
              * sensor_t::minDelay unless sensor_t::minDelay is 0, in which
              * case it is clamped to >= 1ms.
              *
+             * setDelay will not be called when the sensor is in batching mode.
+             * In this case, batch() will be called with the new period.
+             *
              * @return 0 if successful, < 0 on error
              */
             int (*setDelay)(struct sensors_poll_device_t *dev,
@@ -1074,19 +1086,30 @@
      * if a batch call with SENSORS_BATCH_DRY_RUN is successful,
      * the same call without SENSORS_BATCH_DRY_RUN must succeed as well).
      *
-     * If successful, 0 is returned.
-     * If the specified sensor doesn't support batch mode, -EINVAL is returned.
-     * If the specified sensor's trigger-mode is one-shot, -EINVAL is returned.
-     * If WAKE_UPON_FIFO_FULL is specified and the specified sensor's internal
-     * FIFO is too small to store at least 10 seconds worth of data at the
-     * given rate, -EINVAL is returned. Note that as stated above, this has to
-     * be determined at compile time, and not based on the state of the system.
-     * If some other constraints above cannot be satisfied, -EINVAL is returned.
+     * When timeout is not 0:
+     *   If successful, 0 is returned.
+     *   If the specified sensor doesn't support batch mode, return -EINVAL.
+     *   If the specified sensor's trigger-mode is one-shot, return -EINVAL.
+     *   If WAKE_UPON_FIFO_FULL is specified and the specified sensor's internal
+     *   FIFO is too small to store at least 10 seconds worth of data at the
+     *   given rate, -EINVAL is returned. Note that as stated above, this has to
+     *   be determined at compile time, and not based on the state of the
+     *   system.
+     *   If some other constraints above cannot be satisfied, return -EINVAL.
      *
      * Note: the timeout parameter, when > 0, has no impact on whether this
      *       function succeeds or fails.
      *
-     * If timeout is set to 0, this function must succeed.
+     * When timeout is 0:
+     *   The caller will never set the wake_upon_fifo_full flag.
+     *   The function must succeed, and batch mode must be deactivated.
+     *
+     * Independently of whether DRY_RUN is specified, When the call to batch()
+     * fails, no state should be changed. In particular, a failed call to
+     * batch() should not change the rate of the sensor. Example:
+     *   setDelay(..., 10ms)
+     *   batch(..., 20ms, ...) fails
+     *   rate should stay 10ms.
      *
      *
      * IMPLEMENTATION NOTES:
diff --git a/modules/camera/Metadata.cpp b/modules/camera/Metadata.cpp
index b26986d..d5854f9 100644
--- a/modules/camera/Metadata.cpp
+++ b/modules/camera/Metadata.cpp
@@ -167,8 +167,6 @@
 
 camera_metadata_t* Metadata::generate()
 {
-    Entry *current;
-
     pthread_mutex_lock(&mMutex);
     // Reuse if old generated metadata still valid
     if (!mDirty && mGenerated != NULL) {
@@ -191,7 +189,7 @@
         goto out;
     }
     // Walk list of entries adding each one to newly allocated metadata
-    for (current = mHead; current != NULL; current = current->mNext) {
+    for (Entry *current = mHead; current != NULL; current = current->mNext) {
         int res = add_camera_metadata_entry(mGenerated, current->mTag,
                 current->mData, current->mCount);
         if (res != 0) {
diff --git a/tests/camera2/CameraBurstTests.cpp b/tests/camera2/CameraBurstTests.cpp
index 5c4b6e7..b71cfd1 100644
--- a/tests/camera2/CameraBurstTests.cpp
+++ b/tests/camera2/CameraBurstTests.cpp
@@ -19,13 +19,14 @@
 #define LOG_TAG "CameraBurstTest"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
+#include <utils/Timers.h>
 
 #include <cmath>
 
 #include "CameraStreamFixture.h"
 #include "TestExtensions.h"
 
-#define CAMERA_FRAME_TIMEOUT    1000000000 //nsecs (1 secs)
+#define CAMERA_FRAME_TIMEOUT    1000000000LL //nsecs (1 secs)
 #define CAMERA_HEAP_COUNT       2 //HALBUG: 1 means registerBuffers fails
 #define CAMERA_BURST_DEBUGGING  0
 #define CAMERA_FRAME_BURST_COUNT 10
@@ -37,6 +38,10 @@
 #define CAMERA_EXPOSURE_FORMAT CAMERA_STREAM_AUTO_CPU_FORMAT
 #define CAMERA_EXPOSURE_STARTING 100000 // 1/10ms, up to 51.2ms with 10 steps
 
+#define USEC 1000LL        // in ns
+#define MSEC 1000000LL     // in ns
+#define SEC  1000000000LL  // in ns
+
 #if CAMERA_BURST_DEBUGGING
 #define dout std::cout
 #else
@@ -122,6 +127,23 @@
 
         return acc;
     }
+
+    // Parses a comma-separated string list into a Vector
+    template<typename T>
+    void ParseList(const char *src, Vector<T> &list) {
+        std::istringstream s(src);
+        while (!s.eof()) {
+            char c = s.peek();
+            if (c == ',' || c == ' ') {
+                s.ignore(1, EOF);
+                continue;
+            }
+            T val;
+            s >> val;
+            list.push_back(val);
+        }
+    }
+
 };
 
 TEST_F(CameraBurstTest, ManualExposureControl) {
@@ -257,6 +279,412 @@
       << " times over each consecutive frame as the exposure is doubled";
 }
 
+/**
+ * This test varies exposure time, frame duration, and sensitivity for a
+ * burst of captures. It picks values by default, but the selection can be
+ * overridden with the environment variables
+ *   CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES
+ *   CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS
+ *   CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES
+ * which must all be a list of comma-separated values, and each list must be
+ * the same length.  In addition, if the environment variable
+ *   CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES
+ * is set to 1, then the YUV buffers are dumped into files named
+ *   "camera2_test_variable_burst_frame_NNN.yuv"
+ *
+ * For example:
+ *   $ setenv CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES 10000000,20000000
+ *   $ setenv CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS 40000000,40000000
+ *   $ setenv CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES 200,100
+ *   $ setenv CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES 1
+ *   $ /data/nativetest/camera2_test/camera2_test --gtest_filter="*VariableBurst"
+ */
+TEST_F(CameraBurstTest, VariableBurst) {
+
+    TEST_EXTENSION_FORKING_INIT;
+
+    // Bounds for checking frame duration is within range
+    const nsecs_t DURATION_UPPER_BOUND = 10 * MSEC;
+    const nsecs_t DURATION_LOWER_BOUND = 20 * MSEC;
+
+    // Threshold for considering two captures to have equivalent exposure value,
+    // as a ratio of the smaller EV to the larger EV.
+    const float   EV_MATCH_BOUND = 0.95;
+    // Bound for two captures with equivalent exp values to have the same
+    // measured brightness, in 0-255 luminance.
+    const float   BRIGHTNESS_MATCH_BOUND = 5;
+
+    // Environment variables to look for to override test settings
+    const char *expEnv         = "CAMERA2_TEST_VARIABLE_BURST_EXPOSURE_TIMES";
+    const char *durationEnv    = "CAMERA2_TEST_VARIABLE_BURST_FRAME_DURATIONS";
+    const char *sensitivityEnv = "CAMERA2_TEST_VARIABLE_BURST_SENSITIVITIES";
+    const char *dumpFrameEnv   = "CAMERA2_TEST_VARIABLE_BURST_DUMP_FRAMES";
+
+    // Range of valid exposure times, in nanoseconds
+    int64_t minExp = 0, maxExp = 0;
+    // List of valid sensor sensitivities
+    Vector<int32_t> sensitivities;
+    // Range of valid frame durations, in nanoseconds
+    int64_t minDuration = 0, maxDuration = 0;
+
+    {
+        camera_metadata_ro_entry exposureTimeRange =
+            GetStaticEntry(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE);
+
+        EXPECT_EQ(2u, exposureTimeRange.count) << "Bad exposure time range tag."
+                "Using default values";
+        if (exposureTimeRange.count == 2) {
+            minExp = exposureTimeRange.data.i64[0];
+            maxExp = exposureTimeRange.data.i64[1];
+        }
+
+        EXPECT_LT(0, minExp) << "Minimum exposure time is 0";
+        EXPECT_LT(0, maxExp) << "Maximum exposure time is 0";
+        EXPECT_LE(minExp, maxExp) << "Minimum exposure is greater than maximum";
+
+        if (minExp == 0) {
+            minExp = 1 * MSEC; // Fallback minimum exposure time
+        }
+
+        if (maxExp == 0) {
+            maxExp = 10 * SEC; // Fallback maximum exposure time
+        }
+    }
+
+    dout << "Stream size is " << mWidth << " x " << mHeight << std::endl;
+    dout << "Valid exposure range is: " <<
+            minExp << " - " << maxExp << " ns " << std::endl;
+
+    {
+        camera_metadata_ro_entry availableSensitivities =
+            GetStaticEntry(ANDROID_SENSOR_INFO_AVAILABLE_SENSITIVITIES);
+
+        EXPECT_LT(0u, availableSensitivities.count) << "No sensitivities listed."
+                "Falling back to default set.";
+        sensitivities.appendArray(availableSensitivities.data.i32,
+                availableSensitivities.count);
+        if (availableSensitivities.count == 0) {
+            sensitivities.push_back(100);
+            sensitivities.push_back(200);
+            sensitivities.push_back(400);
+            sensitivities.push_back(800);
+        }
+    }
+
+    dout << "Available sensitivities: ";
+    for (size_t i = 0; i < sensitivities.size(); i++) {
+        dout << sensitivities[i] << " ";
+    }
+    dout << std::endl;
+
+    {
+        camera_metadata_ro_entry availableProcessedSizes =
+                GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
+
+        camera_metadata_ro_entry availableProcessedMinFrameDurations =
+                GetStaticEntry(ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS);
+
+        EXPECT_EQ(availableProcessedSizes.count,
+                availableProcessedMinFrameDurations.count * 2) <<
+                "The number of minimum frame durations doesn't match the number of "
+                "available sizes. Using fallback values";
+
+        if (availableProcessedSizes.count ==
+                availableProcessedMinFrameDurations.count * 2) {
+            bool gotSize = false;
+            for (size_t i = 0; i < availableProcessedSizes.count; i += 2) {
+                if (availableProcessedSizes.data.i32[i] == mWidth &&
+                        availableProcessedSizes.data.i32[i+1] == mHeight) {
+                    gotSize = true;
+                    minDuration = availableProcessedMinFrameDurations.data.i64[i/2];
+                }
+            }
+            EXPECT_TRUE(gotSize) << "Can't find stream size in list of "
+                    "available sizes: " << mWidth << ", " << mHeight;
+        }
+        if (minDuration == 0) {
+            minDuration = 1 * SEC / 30; // Fall back to 30 fps as minimum duration
+        }
+
+        ASSERT_LT(0, minDuration);
+
+        camera_metadata_ro_entry maxFrameDuration =
+                GetStaticEntry(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION);
+
+        EXPECT_EQ(1u, maxFrameDuration.count) << "No valid maximum frame duration";
+
+        if (maxFrameDuration.count == 1) {
+            maxDuration = maxFrameDuration.data.i64[0];
+        }
+
+        EXPECT_GT(0, maxDuration) << "Max duration is 0 or not given, using fallback";
+
+        if (maxDuration == 0) {
+            maxDuration = 10 * SEC; // Fall back to 10 seconds as max duration
+        }
+
+    }
+    dout << "Available frame duration range for configured stream size: "
+         << minDuration << " - " << maxDuration << " ns" << std::endl;
+
+    // Get environment variables if set
+    const char *expVal = getenv(expEnv);
+    const char *durationVal = getenv(durationEnv);
+    const char *sensitivityVal = getenv(sensitivityEnv);
+
+    bool gotExp = (expVal != NULL);
+    bool gotDuration = (durationVal != NULL);
+    bool gotSensitivity = (sensitivityVal != NULL);
+
+    // All or none must be provided if using override envs
+    ASSERT_TRUE( (gotDuration && gotExp && gotSensitivity) ||
+            (!gotDuration && !gotExp && !gotSensitivity) ) <<
+            "Incomplete set of environment variable overrides provided";
+
+    Vector<int64_t> expList, durationList;
+    Vector<int32_t> sensitivityList;
+    if (gotExp) {
+        ParseList(expVal, expList);
+        ParseList(durationVal, durationList);
+        ParseList(sensitivityVal, sensitivityList);
+
+        ASSERT_TRUE(
+            (expList.size() == durationList.size()) &&
+            (durationList.size() == sensitivityList.size())) <<
+                "Mismatched sizes in env lists, or parse error";
+
+        dout << "Using burst list from environment with " << expList.size() <<
+                " captures" << std::endl;
+    } else {
+        // Create a default set of controls based on the available ranges
+
+        int64_t e;
+        int64_t d;
+        int32_t s;
+
+        // Exposure ramp
+
+        e = minExp;
+        d = minDuration;
+        s = sensitivities[0];
+        while (e < maxExp) {
+            expList.push_back(e);
+            durationList.push_back(d);
+            sensitivityList.push_back(s);
+            e = e * 2;
+        }
+        e = maxExp;
+        expList.push_back(e);
+        durationList.push_back(d);
+        sensitivityList.push_back(s);
+
+        // Duration ramp
+
+        e = 30 * MSEC;
+        d = minDuration;
+        s = sensitivities[0];
+        while (d < maxDuration) {
+            // make sure exposure <= frame duration
+            expList.push_back(e > d ? d : e);
+            durationList.push_back(d);
+            sensitivityList.push_back(s);
+            d = d * 2;
+        }
+
+        // Sensitivity ramp
+
+        e = 30 * MSEC;
+        d = 30 * MSEC;
+        d = d > minDuration ? d : minDuration;
+        for (size_t i = 0; i < sensitivities.size(); i++) {
+            expList.push_back(e);
+            durationList.push_back(d);
+            sensitivityList.push_back(sensitivities[i]);
+        }
+
+        // Constant-EV ramp, duration == exposure
+
+        e = 30 * MSEC; // at ISO 100
+        for (size_t i = 0; i < sensitivities.size(); i++) {
+            int64_t e_adj = e * 100 / sensitivities[i];
+            expList.push_back(e_adj);
+            durationList.push_back(e_adj > minDuration ? e_adj : minDuration);
+            sensitivityList.push_back(sensitivities[i]);
+        }
+
+        dout << "Default burst sequence created with " << expList.size() <<
+                " entries" << std::endl;
+    }
+
+    // Validate the list, but warn only
+    for (size_t i = 0; i < expList.size(); i++) {
+        EXPECT_GE(maxExp, expList[i])
+                << "Capture " << i << " exposure too long: " << expList[i];
+        EXPECT_LE(minExp, expList[i])
+                << "Capture " << i << " exposure too short: " << expList[i];
+        EXPECT_GE(maxDuration, durationList[i])
+                << "Capture " << i << " duration too long: " << durationList[i];
+        EXPECT_LE(minDuration, durationList[i])
+                 << "Capture " << i << " duration too short: "  << durationList[i];
+        bool validSensitivity = false;
+        for (size_t j = 0; j < sensitivities.size(); j++) {
+            if (sensitivityList[i] == sensitivities[j]) {
+                validSensitivity = true;
+                break;
+            }
+        }
+        EXPECT_TRUE(validSensitivity)
+                << "Capture " << i << " sensitivity not in list: " << sensitivityList[i];
+    }
+
+    // Check if debug yuv dumps are requested
+
+    bool dumpFrames = false;
+    {
+        const char *frameDumpVal = getenv(dumpFrameEnv);
+        if (frameDumpVal != NULL) {
+            if (frameDumpVal[0] == '1') dumpFrames = true;
+        }
+    }
+
+    dout << "Dumping YUV frames " <<
+            (dumpFrames ? "enabled, not checking timing" : "disabled") << std::endl;
+
+    // Create a base preview request, turning off all 3A
+    CameraMetadata previewRequest;
+    ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                                                &previewRequest));
+    {
+        Vector<uint8_t> outputStreamIds;
+        outputStreamIds.push(mStreamId);
+        ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS,
+                                            outputStreamIds));
+
+        // Disable all 3A routines
+        uint8_t cmOff = static_cast<uint8_t>(ANDROID_CONTROL_MODE_OFF);
+        ASSERT_EQ(OK, previewRequest.update(ANDROID_CONTROL_MODE,
+                                            &cmOff, 1));
+
+        int requestId = 1;
+        ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_ID,
+                                            &requestId, 1));
+    }
+
+    // Submit capture requests
+
+    for (size_t i = 0; i < expList.size(); ++i) {
+        CameraMetadata tmpRequest = previewRequest;
+        ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_EXPOSURE_TIME,
+                                        &expList[i], 1));
+        ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_FRAME_DURATION,
+                                        &durationList[i], 1));
+        ASSERT_EQ(OK, tmpRequest.update(ANDROID_SENSOR_SENSITIVITY,
+                                        &sensitivityList[i], 1));
+        ALOGV("Submitting capture %d with exposure %lld, frame duration %lld, sensitivity %d",
+                i, expList[i], durationList[i], sensitivityList[i]);
+        dout << "Capture request " << i <<
+                ": exposure is " << (expList[i]/1e6f) << " ms" <<
+                ", frame duration is " << (durationList[i]/1e6f) << " ms" <<
+                ", sensitivity is " << sensitivityList[i] <<
+                std::endl;
+        ASSERT_EQ(OK, mDevice->capture(tmpRequest));
+    }
+
+    Vector<float> brightnesses;
+    Vector<nsecs_t> captureTimes;
+    brightnesses.setCapacity(expList.size());
+    captureTimes.setCapacity(expList.size());
+
+    // Get each frame (metadata) and then the buffer. Calculate brightness.
+    for (size_t i = 0; i < expList.size(); ++i) {
+
+        ALOGV("Reading request %d", i);
+        dout << "Waiting for capture " << i << ": " <<
+                " exposure " << (expList[i]/1e6f) << " ms," <<
+                " frame duration " << (durationList[i]/1e6f) << " ms," <<
+                " sensitivity " << sensitivityList[i] <<
+                std::endl;
+
+        // Set wait limit based on expected frame duration, or minimum timeout
+        int64_t waitLimit = CAMERA_FRAME_TIMEOUT;
+        if (expList[i] * 2 > waitLimit) waitLimit = expList[i] * 2;
+        if (durationList[i] * 2 > waitLimit) waitLimit = durationList[i] * 2;
+
+        ASSERT_EQ(OK, mDevice->waitForNextFrame(waitLimit));
+        ALOGV("Reading capture request-1 %d", i);
+        CameraMetadata frameMetadata;
+        ASSERT_EQ(OK, mDevice->getNextFrame(&frameMetadata));
+        ALOGV("Reading capture request-2 %d", i);
+
+        ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT));
+        ALOGV("We got the frame now");
+
+        captureTimes.push_back(systemTime());
+
+        CpuConsumer::LockedBuffer imgBuffer;
+        ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer));
+
+        int underexposed, overexposed;
+        float avgBrightness = 0;
+        long long brightness = TotalBrightness(imgBuffer, &underexposed,
+                                               &overexposed);
+        int numValidPixels = mWidth * mHeight - (underexposed + overexposed);
+        if (numValidPixels != 0) {
+            avgBrightness = brightness * 1.0f / numValidPixels;
+        } else if (underexposed < overexposed) {
+            avgBrightness = 255;
+        }
+
+        ALOGV("Total brightness for frame %d was %lld (underexposed %d, "
+              "overexposed %d), avg %f", i, brightness, underexposed,
+              overexposed, avgBrightness);
+        dout << "Average brightness (frame " << i << ") was " << avgBrightness
+             << " (underexposed " << underexposed << ", overexposed "
+             << overexposed << ")" << std::endl;
+        brightnesses.push_back(avgBrightness);
+
+        if (i != 0) {
+            float prevEv = static_cast<float>(expList[i - 1]) * sensitivityList[i - 1];
+            float currentEv = static_cast<float>(expList[i]) * sensitivityList[i];
+            float evRatio = (prevEv > currentEv) ? (currentEv / prevEv) :
+                    (prevEv / currentEv);
+            if ( evRatio > EV_MATCH_BOUND ) {
+                EXPECT_LT( fabs(brightnesses[i] - brightnesses[i - 1]),
+                        BRIGHTNESS_MATCH_BOUND) <<
+                        "Capture brightness different from previous, even though "
+                        "they have the same EV value. Ev now: " << currentEv <<
+                        ", previous: " << prevEv << ". Brightness now: " <<
+                        brightnesses[i] << ", previous: " << brightnesses[i-1];
+            }
+            // Only check timing if not saving to disk, since that slows things
+            // down substantially
+            if (!dumpFrames) {
+                nsecs_t timeDelta = captureTimes[i] - captureTimes[i-1];
+                nsecs_t expectedDelta = expList[i] > durationList[i] ?
+                        expList[i] : durationList[i];
+                EXPECT_LT(timeDelta, expectedDelta + DURATION_UPPER_BOUND) <<
+                        "Capture took " << timeDelta << " ns to receive, but expected"
+                        " frame duration was " << expectedDelta << " ns.";
+                EXPECT_GT(timeDelta, expectedDelta - DURATION_LOWER_BOUND) <<
+                        "Capture took " << timeDelta << " ns to receive, but expected"
+                    " frame duration was " << expectedDelta << " ns.";
+                dout << "Time delta from previous frame: " << timeDelta / 1e6 <<
+                        " ms.  Expected " << expectedDelta / 1e6 << " ms" << std::endl;
+            }
+        }
+
+        if (dumpFrames) {
+            String8 dumpName =
+                    String8::format("/data/local/tmp/camera2_test_variable_burst_frame_%03d.yuv", i);
+            dout << "  Writing YUV dump to " << dumpName << std::endl;
+            DumpYuvToFile(dumpName, imgBuffer);
+        }
+
+        ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer));
+    }
+
+}
+
 }
 }
 }
diff --git a/tests/camera2/CameraStreamFixture.h b/tests/camera2/CameraStreamFixture.h
index a4dc4a8..3d614db 100644
--- a/tests/camera2/CameraStreamFixture.h
+++ b/tests/camera2/CameraStreamFixture.h
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest.h>
 #include <iostream>
+#include <fstream>
 
 #include <gui/CpuConsumer.h>
 #include <gui/Surface.h>
@@ -29,6 +30,8 @@
 #include "CameraModuleFixture.h"
 #include "TestExtensions.h"
 
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
 namespace android {
 namespace camera2 {
 namespace tests {
@@ -194,6 +197,80 @@
         return format;
     }
 
+    void DumpYuvToFile(const String8 &fileName, const CpuConsumer::LockedBuffer &img) {
+        uint8_t *dataCb, *dataCr;
+        uint32_t stride;
+        uint32_t chromaStride;
+        uint32_t chromaStep;
+
+        switch (img.format) {
+            case HAL_PIXEL_FORMAT_YCbCr_420_888:
+                stride = img.stride;
+                chromaStride = img.chromaStride;
+                chromaStep = img.chromaStep;
+                dataCb = img.dataCb;
+                dataCr = img.dataCr;
+                break;
+            case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+                stride = img.width;
+                chromaStride = img.width;
+                chromaStep = 2;
+                dataCr = img.data + img.width * img.height;
+                dataCb = dataCr + 1;
+                break;
+            case HAL_PIXEL_FORMAT_YV12:
+                stride = img.stride;
+                chromaStride = ALIGN(img.width / 2, 16);
+                chromaStep = 1;
+                dataCr = img.data + img.stride * img.height;
+                dataCb = dataCr + chromaStride * img.height/2;
+                break;
+            default:
+                ALOGE("Unknown format %d, not dumping", img.format);
+                return;
+        }
+
+        // Write Y
+        FILE *yuvFile = fopen(fileName.string(), "w");
+
+        size_t bytes;
+
+        for (size_t y = 0; y < img.height; ++y) {
+            bytes = fwrite(
+                reinterpret_cast<const char*>(img.data + stride * y),
+                1, img.width, yuvFile);
+            if (bytes != img.width) {
+                ALOGE("Unable to write to file %s", fileName.string());
+                fclose(yuvFile);
+                return;
+            }
+        }
+
+        // Write Cb/Cr
+        uint8_t *src = dataCb;
+        for (int c = 0; c < 2; ++c) {
+            for (size_t y = 0; y < img.height / 2; ++y) {
+                uint8_t *px = src + y * chromaStride;
+                if (chromaStep != 1) {
+                    for (size_t x = 0; x < img.width / 2; ++x) {
+                        fputc(*px, yuvFile);
+                        px += chromaStep;
+                    }
+                } else {
+                    bytes = fwrite(reinterpret_cast<const char*>(px),
+                            1, img.width / 2, yuvFile);
+                    if (bytes != img.width / 2) {
+                        ALOGE("Unable to write to file %s", fileName.string());
+                        fclose(yuvFile);
+                        return;
+                    }
+                }
+            }
+            src = dataCr;
+        }
+        fclose(yuvFile);
+    }
+
     int mWidth;
     int mHeight;