merge in jb-mr1-release history after reset to jb-mr1-dev
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 948b59f..3239483 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -334,6 +334,10 @@
         result.appendFormat("    useZslFormat\n");
         haveQuirk = true;
     }
+    if (p.quirks.meteringCropRegion) {
+        result.appendFormat("    meteringCropRegion\n");
+        haveQuirk = true;
+    }
     if (!haveQuirk) {
         result.appendFormat("    none\n");
     }
@@ -374,7 +378,16 @@
 
     ALOGV("Camera %d: Shutting down", mCameraId);
 
-    stopPreviewL();
+    res = mStreamingProcessor->stopStream();
+    if (res != OK) {
+        ALOGE("%s: Problem stopping streaming: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+    }
+    res = mDevice->waitUntilDrained();
+    if (res != OK) {
+        ALOGE("%s: Problem waiting for HAL: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+    }
 
     {
         SharedParameters::Lock l(mParameters);
@@ -536,7 +549,7 @@
             // Already running preview - need to stop and create a new stream
             // TODO: Optimize this so that we don't wait for old stream to drain
             // before spinning up new stream
-            mDevice->clearStreamingRequest();
+            mStreamingProcessor->stopStream();
             l.mParameters.state = Parameters::WAITING_FOR_PREVIEW_WINDOW;
             break;
     }
@@ -719,6 +732,7 @@
 
 void Camera2Client::stopPreviewL() {
     ATRACE_CALL();
+    status_t res;
     Parameters::State state;
     {
         SharedParameters::Lock l(mParameters);
@@ -740,6 +754,11 @@
             // no break - identical to preview
         case Parameters::PREVIEW:
             mStreamingProcessor->stopStream();
+            res = mDevice->waitUntilDrained();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
+                        __FUNCTION__, mCameraId, strerror(-res), res);
+            }
             // no break
         case Parameters::WAITING_FOR_PREVIEW_WINDOW: {
             SharedParameters::Lock l(mParameters);
@@ -946,9 +965,24 @@
     int triggerId;
     {
         SharedParameters::Lock l(mParameters);
+        if (l.mParameters.state < Parameters::PREVIEW) {
+            return INVALID_OPERATION;
+        }
+
+        if (l.mParameters.quirks.triggerAfWithAuto &&
+                l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED &&
+                l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO) {
+            ALOGV("%s: Quirk: Switching from focusMode %d to AUTO",
+                    __FUNCTION__, l.mParameters.focusMode);
+            l.mParameters.shadowFocusMode = l.mParameters.focusMode;
+            l.mParameters.focusMode = Parameters::FOCUS_MODE_AUTO;
+            updateRequests(l.mParameters);
+        }
+
         l.mParameters.currentAfTriggerId = ++l.mParameters.afTriggerCounter;
         triggerId = l.mParameters.currentAfTriggerId;
     }
+    syncWithDevice();
 
     mDevice->triggerAutofocus(triggerId);
 
@@ -966,7 +1000,18 @@
     {
         SharedParameters::Lock l(mParameters);
         triggerId = ++l.mParameters.afTriggerCounter;
+
+        // When using triggerAfWithAuto quirk, may need to reset focus mode to
+        // the real state at this point.
+        if (l.mParameters.shadowFocusMode != Parameters::FOCUS_MODE_INVALID) {
+            ALOGV("%s: Quirk: Restoring focus mode to %d", __FUNCTION__,
+                    l.mParameters.shadowFocusMode);
+            l.mParameters.focusMode = l.mParameters.shadowFocusMode;
+            l.mParameters.shadowFocusMode = Parameters::FOCUS_MODE_INVALID;
+            updateRequests(l.mParameters);
+        }
     }
+    syncWithDevice();
 
     mDevice->triggerCancelAutofocus(triggerId);
 
@@ -1017,6 +1062,9 @@
         return res;
     }
 
+    // Need HAL to have correct settings before (possibly) triggering precapture
+    syncWithDevice();
+
     res = mCaptureSequencer->startCapture();
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to start capture: %s (%d)",
@@ -1397,13 +1445,18 @@
     return mZslProcessor->getStreamId();
 }
 
-status_t Camera2Client::registerFrameListener(int32_t id,
+status_t Camera2Client::registerFrameListener(int32_t minId, int32_t maxId,
         wp<camera2::FrameProcessor::FilteredListener> listener) {
-    return mFrameProcessor->registerListener(id, listener);
+    return mFrameProcessor->registerListener(minId, maxId, listener);
 }
 
-status_t Camera2Client::removeFrameListener(int32_t id) {
-    return mFrameProcessor->removeListener(id);
+status_t Camera2Client::removeFrameListener(int32_t minId, int32_t maxId,
+        wp<camera2::FrameProcessor::FilteredListener> listener) {
+    return mFrameProcessor->removeListener(minId, maxId, listener);
+}
+
+status_t Camera2Client::stopStream() {
+    return mStreamingProcessor->stopStream();
 }
 
 Camera2Client::SharedCameraClient::Lock::Lock(SharedCameraClient &client):
@@ -1432,9 +1485,12 @@
     mCameraClient.clear();
 }
 
-const int32_t Camera2Client::kPreviewRequestId;
-const int32_t Camera2Client::kRecordRequestId;
-const int32_t Camera2Client::kFirstCaptureRequestId;
+const int32_t Camera2Client::kPreviewRequestIdStart;
+const int32_t Camera2Client::kPreviewRequestIdEnd;
+const int32_t Camera2Client::kRecordingRequestIdStart;
+const int32_t Camera2Client::kRecordingRequestIdEnd;
+const int32_t Camera2Client::kCaptureRequestIdStart;
+const int32_t Camera2Client::kCaptureRequestIdEnd;
 
 /** Utility methods */
 
@@ -1443,6 +1499,13 @@
 
     ALOGV("%s: Camera %d: state = %d", __FUNCTION__, getCameraId(), params.state);
 
+    res = mStreamingProcessor->incrementStreamingIds();
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to increment request IDs: %s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+
     res = mStreamingProcessor->updatePreviewRequest(params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)",
@@ -1504,4 +1567,23 @@
     }
 }
 
+status_t Camera2Client::syncWithDevice() {
+    ATRACE_CALL();
+    const nsecs_t kMaxSyncTimeout = 100000000; // 100 ms
+    status_t res;
+
+    int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
+    if (activeRequestId == 0) return OK;
+
+    res = mDevice->waitUntilRequestReceived(activeRequestId, kMaxSyncTimeout);
+    if (res == TIMED_OUT) {
+        ALOGE("%s: Camera %d: Timed out waiting sync with HAL",
+                __FUNCTION__, mCameraId);
+    } else if (res != OK) {
+        ALOGE("%s: Camera %d: Error while waiting to sync with HAL",
+                __FUNCTION__, mCameraId);
+    }
+    return res;
+}
+
 } // namespace android
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index fb1dcde..55ead02 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -107,9 +107,12 @@
     int getRecordingStreamId() const;
     int getZslStreamId() const;
 
-    status_t registerFrameListener(int32_t id,
+    status_t registerFrameListener(int32_t minId, int32_t maxId,
             wp<camera2::FrameProcessor::FilteredListener> listener);
-    status_t removeFrameListener(int32_t id);
+    status_t removeFrameListener(int32_t minId, int32_t maxId,
+            wp<camera2::FrameProcessor::FilteredListener> listener);
+
+    status_t stopStream();
 
     // Simple class to ensure that access to ICameraClient is serialized by
     // requiring mCameraClientLock to be locked before access to mCameraClient
@@ -135,9 +138,14 @@
     static size_t calculateBufferSize(int width, int height,
             int format, int stride);
 
-    static const int32_t kPreviewRequestId = 1000;
-    static const int32_t kRecordRequestId  = 2000;
-    static const int32_t kFirstCaptureRequestId = 3000;
+    static const int32_t kPreviewRequestIdStart = 10000000;
+    static const int32_t kPreviewRequestIdEnd   = 20000000;
+
+    static const int32_t kRecordingRequestIdStart  = 20000000;
+    static const int32_t kRecordingRequestIdEnd    = 30000000;
+
+    static const int32_t kCaptureRequestIdStart = 30000000;
+    static const int32_t kCaptureRequestIdEnd   = 40000000;
 
 private:
     /** ICamera interface-related private members */
@@ -208,6 +216,9 @@
 
     /** Utility members */
 
+    // Wait until the camera device has received the latest control settings
+    status_t syncWithDevice();
+
     // Verify that caller is the owner of the camera
     status_t checkPid(const char *checkLocation) const;
 };
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 25b7a58..d6445c1 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -27,6 +27,7 @@
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
+#include <utils/Timers.h>
 #include "Camera2Device.h"
 
 namespace android {
@@ -228,6 +229,11 @@
     return mRequestQueue.setStreamSlot(NULL);
 }
 
+status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
+    ATRACE_CALL();
+    return mRequestQueue.waitForDequeue(requestId, timeout);
+}
+
 status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
         uint32_t width, uint32_t height, int format, size_t size, int *id) {
     ATRACE_CALL();
@@ -567,6 +573,7 @@
 Camera2Device::MetadataQueue::MetadataQueue():
             mDevice(NULL),
             mFrameCount(0),
+            mLatestRequestId(0),
             mCount(0),
             mStreamSlotCount(0),
             mSignalConsumer(true)
@@ -678,6 +685,16 @@
         mFrameCount++;
     }
 
+    // Check for request ID, and if present, signal waiters.
+    camera_metadata_entry_t requestId;
+    res = find_camera_metadata_entry(b,
+            ANDROID_REQUEST_ID,
+            &requestId);
+    if (res == OK) {
+        mLatestRequestId = requestId.data.i32[0];
+        mNewRequestId.signal();
+    }
+
     *buf = b;
     mCount--;
 
@@ -695,6 +712,22 @@
     return OK;
 }
 
+status_t Camera2Device::MetadataQueue::waitForDequeue(int32_t id,
+        nsecs_t timeout) {
+    Mutex::Autolock l(mMutex);
+    status_t res;
+    while (mLatestRequestId != id) {
+        nsecs_t startTime = systemTime();
+
+        res = mNewRequestId.waitRelative(mMutex, timeout);
+        if (res != OK) return res;
+
+        timeout -= (systemTime() - startTime);
+    }
+
+    return OK;
+}
+
 status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf)
 {
     ATRACE_CALL();
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 38662e3..29830bd 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -67,6 +67,13 @@
     status_t clearStreamingRequest();
 
     /**
+     * Wait until a request with the given ID has been dequeued by the
+     * HAL. Returns TIMED_OUT if the timeout duration is reached. Returns
+     * immediately if the latest request received by the HAL has this id.
+     */
+    status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout);
+
+    /**
      * Create an output stream of the requested size and format.
      *
      * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects
@@ -226,6 +233,9 @@
         status_t dequeue(camera_metadata_t **buf, bool incrementCount = true);
         int      getBufferCount();
         status_t waitForBuffer(nsecs_t timeout);
+        // Wait until a buffer with the given ID is dequeued. Will return
+        // immediately if the latest buffer dequeued has that ID.
+        status_t waitForDequeue(int32_t id, nsecs_t timeout);
 
         // Set repeating buffer(s); if the queue is empty on a dequeue call, the
         // queue copies the contents of the stream slot into the queue, and then
@@ -247,6 +257,8 @@
         Condition notEmpty;
 
         int mFrameCount;
+        int32_t mLatestRequestId;
+        Condition mNewRequestId;
 
         int mCount;
         List<camera_metadata_t*> mEntries;
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
index d56af64..a849246 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
@@ -44,7 +44,7 @@
         mCaptureState(IDLE),
         mTriggerId(0),
         mTimeoutCount(0),
-        mCaptureId(Camera2Client::kFirstCaptureRequestId) {
+        mCaptureId(Camera2Client::kCaptureRequestIdStart) {
     ALOGV("%s", __FUNCTION__);
 }
 
@@ -84,12 +84,12 @@
 }
 
 void CaptureSequencer::onFrameAvailable(int32_t frameId,
-        CameraMetadata &frame) {
+        const CameraMetadata &frame) {
     ALOGV("%s: Listener found new frame", __FUNCTION__);
     ATRACE_CALL();
     Mutex::Autolock l(mInputMutex);
     mNewFrameId = frameId;
-    mNewFrame.acquire(frame);
+    mNewFrame = frame;
     if (!mNewFrameReceived) {
         mNewFrameReceived = true;
         mNewFrameSignal.signal();
@@ -203,7 +203,9 @@
     status_t res = OK;
     ATRACE_CALL();
     mCaptureId++;
-
+    if (mCaptureId >= Camera2Client::kCaptureRequestIdEnd) {
+        mCaptureId = Camera2Client::kCaptureRequestIdStart;
+    }
     {
         Mutex::Autolock l(mInputMutex);
         mBusy = false;
@@ -286,7 +288,7 @@
         return DONE;
     }
 
-    client->registerFrameListener(mCaptureId,
+    client->registerFrameListener(mCaptureId, mCaptureId + 1,
             this);
 
     // TODO: Actually select the right thing here.
@@ -326,7 +328,7 @@
 CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart(
         sp<Camera2Client> &client) {
     ATRACE_CALL();
-    client->registerFrameListener(mCaptureId,
+    client->registerFrameListener(mCaptureId, mCaptureId + 1,
             this);
     {
         SharedParameters::Lock l(client->getParameters());
@@ -421,7 +423,7 @@
     }
 
     if (l.mParameters.state == Parameters::STILL_CAPTURE) {
-        res = client->getCameraDevice()->clearStreamingRequest();
+        res = client->stopStream();
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to stop preview for still capture: "
                     "%s (%d)",
@@ -482,7 +484,7 @@
             ALOGW("Mismatched capture timestamps: Metadata frame %lld,"
                     " captured buffer %lld", entry.data.i64[0], mCaptureTimestamp);
         }
-        client->removeFrameListener(mCaptureId);
+        client->removeFrameListener(mCaptureId, mCaptureId + 1, this);
 
         mNewFrameReceived = false;
         mNewCaptureReceived = false;
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.h b/services/camera/libcameraservice/camera2/CaptureSequencer.h
index 27f3f1c..07e4c01 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.h
@@ -57,7 +57,7 @@
     void notifyAutoExposure(uint8_t newState, int triggerId);
 
     // Notifications from the frame processor
-    virtual void onFrameAvailable(int32_t frameId, CameraMetadata &frame);
+    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
 
     // Notifications from the JPEG processor
     void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer);
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
index 0c8560b..064607c 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
@@ -36,17 +36,30 @@
     ALOGV("%s: Exit", __FUNCTION__);
 }
 
-status_t FrameProcessor::registerListener(int32_t id,
-        wp<FilteredListener> listener) {
+status_t FrameProcessor::registerListener(int32_t minId,
+        int32_t maxId, wp<FilteredListener> listener) {
     Mutex::Autolock l(mInputMutex);
-    ALOGV("%s: Registering listener for frame id %d",
-            __FUNCTION__, id);
-    return mListeners.replaceValueFor(id, listener);
+    ALOGV("%s: Registering listener for frame id range %d - %d",
+            __FUNCTION__, minId, maxId);
+    RangeListener rListener = { minId, maxId, listener };
+    mRangeListeners.push_back(rListener);
+    return OK;
 }
 
-status_t FrameProcessor::removeListener(int32_t id) {
+status_t FrameProcessor::removeListener(int32_t minId,
+        int32_t maxId, wp<FilteredListener> listener) {
     Mutex::Autolock l(mInputMutex);
-    return mListeners.removeItem(id);
+    List<RangeListener>::iterator item = mRangeListeners.begin();
+    while (item != mRangeListeners.end()) {
+        if (item->minId == minId &&
+                item->maxId == maxId &&
+                item->listener == listener) {
+            item = mRangeListeners.erase(item);
+        } else {
+            item++;
+        }
+    }
+    return OK;
 }
 
 void FrameProcessor::dump(int fd, const Vector<String16>& args) {
@@ -97,8 +110,7 @@
         res = processFaceDetect(frame, client);
         if (res != OK) break;
 
-        // Must be last - listener can take ownership of frame
-        res = processListener(frame, client);
+        res = processListeners(frame, client);
         if (res != OK) break;
 
         if (!frame.isEmpty()) {
@@ -114,11 +126,11 @@
     return;
 }
 
-status_t FrameProcessor::processListener(CameraMetadata &frame,
+status_t FrameProcessor::processListeners(const CameraMetadata &frame,
         sp<Camera2Client> &client) {
     status_t res;
     ATRACE_CALL();
-    camera_metadata_entry_t entry;
+    camera_metadata_ro_entry_t entry;
 
     entry = frame.find(ANDROID_REQUEST_ID);
     if (entry.count == 0) {
@@ -127,22 +139,30 @@
         return BAD_VALUE;
     }
     int32_t frameId = entry.data.i32[0];
-    ALOGV("%s: Got frame with ID %d", __FUNCTION__, frameId);
 
-    sp<FilteredListener> listener;
+    List<sp<FilteredListener> > listeners;
     {
         Mutex::Autolock l(mInputMutex);
-        ssize_t listenerIndex = mListeners.indexOfKey(frameId);
-        if (listenerIndex != NAME_NOT_FOUND) {
-            listener = mListeners[listenerIndex].promote();
-            if (listener == 0) {
-                mListeners.removeItemsAt(listenerIndex, 1);
+
+        List<RangeListener>::iterator item = mRangeListeners.begin();
+        while (item != mRangeListeners.end()) {
+            if (frameId >= item->minId &&
+                    frameId < item->maxId) {
+                sp<FilteredListener> listener = item->listener.promote();
+                if (listener == 0) {
+                    item = mRangeListeners.erase(item);
+                    continue;
+                } else {
+                    listeners.push_back(listener);
+                }
             }
+            item++;
         }
     }
-
-    if (listener != 0) {
-        listener->onFrameAvailable(frameId, frame);
+    ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size());
+    List<sp<FilteredListener> >::iterator item = listeners.begin();
+    for (; item != listeners.end(); item++) {
+        (*item)->onFrameAvailable(frameId, frame);
     }
     return OK;
 }
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h
index cc8c128..3bd4e25 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.h
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.h
@@ -21,6 +21,7 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 #include <utils/KeyedVector.h>
+#include <utils/List.h>
 #include "CameraMetadata.h"
 
 struct camera_frame_metadata;
@@ -40,15 +41,14 @@
     ~FrameProcessor();
 
     struct FilteredListener: virtual public RefBase {
-        // Listener may take ownership of frame
-        virtual void onFrameAvailable(int32_t frameId, CameraMetadata &frame) = 0;
+        virtual void onFrameAvailable(int32_t frameId,
+                const CameraMetadata &frame) = 0;
     };
 
-    // Register a listener for a specific frame ID (android.request.id).
-    // De-registers any existing listeners for that ID
-    status_t registerListener(int32_t id, wp<FilteredListener> listener);
-
-    status_t removeListener(int32_t id);
+    // Register a listener for a range of IDs [minId, maxId). Multiple listeners
+    // can be listening to the same range
+    status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+    status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
 
     void dump(int fd, const Vector<String16>& args);
   private:
@@ -58,14 +58,20 @@
     virtual bool threadLoop();
 
     Mutex mInputMutex;
-    KeyedVector<int32_t, wp<FilteredListener> > mListeners;
+
+    struct RangeListener {
+        int32_t minId;
+        int32_t maxId;
+        wp<FilteredListener> listener;
+    };
+    List<RangeListener> mRangeListeners;
 
     void processNewFrames(sp<Camera2Client> &client);
 
     status_t processFaceDetect(const CameraMetadata &frame,
             sp<Camera2Client> &client);
 
-    status_t processListener(CameraMetadata &frame,
+    status_t processListeners(const CameraMetadata &frame,
             sp<Camera2Client> &client);
 
     CameraMetadata mLastFrame;
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
index 7740df2..1b15a1c 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -20,6 +20,8 @@
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
 
 #include <math.h>
 #include <stdlib.h>
@@ -171,16 +173,36 @@
     // still have to do something sane for them
 
     // NOTE: Not scaled like FPS range values are.
-    previewFps = previewFpsRange[0];
+    previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
     params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE,
-            previewFpsRange[0]);
+            previewFps);
 
     {
+        SortedVector<int32_t> sortedPreviewFrameRates;
+
         String8 supportedPreviewFrameRates;
         for (size_t i=0; i < availableFpsRanges.count; i += 2) {
-            if (i != 0) supportedPreviewFrameRates += ",";
+            // from the [min, max] fps range use the max value
+            int fps = fpsFromRange(availableFpsRanges.data.i32[i],
+                                   availableFpsRanges.data.i32[i+1]);
+
+            // de-dupe frame rates
+            if (sortedPreviewFrameRates.indexOf(fps) == NAME_NOT_FOUND) {
+                sortedPreviewFrameRates.add(fps);
+            }
+            else {
+                continue;
+            }
+
+            if (sortedPreviewFrameRates.size() > 1) {
+                supportedPreviewFrameRates += ",";
+            }
+
             supportedPreviewFrameRates += String8::format("%d",
-                    availableFpsRanges.data.i32[i]);
+                    fps);
+
+            ALOGV("%s: Supported preview frame rates: %s",
+                    __FUNCTION__, supportedPreviewFrameRates.string());
         }
         params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES,
                 supportedPreviewFrameRates);
@@ -607,6 +629,7 @@
         params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES,
                 supportedFocusModes);
     }
+    shadowFocusMode = FOCUS_MODE_INVALID;
 
     camera_metadata_ro_entry_t max3aRegions =
         staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1);
@@ -906,6 +929,11 @@
     ALOGV_IF(quirks.useZslFormat, "Camera %d: Quirk useZslFormat enabled",
             cameraId);
 
+    entry = info->find(ANDROID_QUIRKS_METERING_CROP_REGION);
+    quirks.meteringCropRegion = (entry.count != 0 && entry.data.u8[0] == 1);
+    ALOGV_IF(quirks.meteringCropRegion, "Camera %d: Quirk meteringCropRegion"
+                " enabled", cameraId);
+
     return OK;
 }
 
@@ -982,6 +1010,13 @@
         }
     }
 
+    // RECORDING_HINT (always supported)
+    validatedParams.recordingHint = boolFromString(
+        newParams.get(CameraParameters::KEY_RECORDING_HINT) );
+    bool recordingHintChanged = validatedParams.recordingHint != recordingHint;
+    ALOGV_IF(recordingHintChanged, "%s: Recording hint changed to %d",
+            __FUNCTION__, recordingHintChanged);
+
     // PREVIEW_FPS_RANGE
     bool fpsRangeChanged = false;
     newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0],
@@ -1008,7 +1043,9 @@
                     validatedParams.previewFpsRange[1]);
             return BAD_VALUE;
         }
-        validatedParams.previewFps = validatedParams.previewFpsRange[0];
+        validatedParams.previewFps =
+            fpsFromRange(validatedParams.previewFpsRange[0],
+                         validatedParams.previewFpsRange[1]);
         newParams.setPreviewFrameRate(validatedParams.previewFps);
     }
 
@@ -1040,27 +1077,78 @@
     // The single-value FPS is the same as the minimum of the range.
     if (!fpsRangeChanged) {
         validatedParams.previewFps = newParams.getPreviewFrameRate();
-        if (validatedParams.previewFps != previewFps) {
+        if (validatedParams.previewFps != previewFps || recordingHintChanged) {
             camera_metadata_ro_entry_t availableFrameRates =
                 staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+            /**
+              * If recording hint is set, find the range that encompasses
+              * previewFps with the largest min index.
+              *
+              * If recording hint is not set, find the range with previewFps
+              * with the smallest min index.
+              *
+              * Either way, in case of multiple ranges, break the tie by
+              * selecting the smaller range.
+              */
+            int targetFps = validatedParams.previewFps;
+            // all ranges which have targetFps
+            Vector<Range> candidateRanges;
             for (i = 0; i < availableFrameRates.count; i+=2) {
-                if (availableFrameRates.data.i32[i] ==
-                        validatedParams.previewFps) break;
+                Range r = {
+                            availableFrameRates.data.i32[i],
+                            availableFrameRates.data.i32[i+1]
+                };
+
+                if (r.min <= targetFps && targetFps <= r.max) {
+                    candidateRanges.push(r);
+                }
             }
-            if (i == availableFrameRates.count) {
+            if (candidateRanges.isEmpty()) {
                 ALOGE("%s: Requested preview frame rate %d is not supported",
                         __FUNCTION__, validatedParams.previewFps);
                 return BAD_VALUE;
             }
+            // most applicable range with targetFps
+            Range bestRange = candidateRanges[0];
+            for (i = 1; i < candidateRanges.size(); ++i) {
+                Range r = candidateRanges[i];
+
+                // Find by largest minIndex in recording mode
+                if (validatedParams.recordingHint) {
+                    if (r.min > bestRange.min) {
+                        bestRange = r;
+                    }
+                    else if (r.min == bestRange.min && r.max < bestRange.max) {
+                        bestRange = r;
+                    }
+                }
+                // Find by smallest minIndex in preview mode
+                else {
+                    if (r.min < bestRange.min) {
+                        bestRange = r;
+                    }
+                    else if (r.min == bestRange.min && r.max < bestRange.max) {
+                        bestRange = r;
+                    }
+                }
+            }
+
             validatedParams.previewFpsRange[0] =
-                    availableFrameRates.data.i32[i];
+                    bestRange.min;
             validatedParams.previewFpsRange[1] =
-                    availableFrameRates.data.i32[i+1];
+                    bestRange.max;
+
+            ALOGV("%s: New preview FPS range: %d, %d, recordingHint = %d",
+                __FUNCTION__,
+                validatedParams.previewFpsRange[0],
+                validatedParams.previewFpsRange[1],
+                validatedParams.recordingHint);
         }
         newParams.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
                 String8::format("%d,%d",
                         validatedParams.previewFpsRange[0] * kFpsToApiScale,
                         validatedParams.previewFpsRange[1] * kFpsToApiScale));
+
     }
 
     // PICTURE_SIZE
@@ -1363,6 +1451,8 @@
                 }
             }
         }
+        // Always reset shadow focus mode to avoid reverting settings
+        shadowFocusMode = FOCUS_MODE_INVALID;
         // Update in case of override
         newParams.set(CameraParameters::KEY_FOCUS_MODE,
                 focusModeEnumToString(validatedParams.focusMode));
@@ -1452,10 +1542,6 @@
         }
     }
 
-    // RECORDING_HINT (always supported)
-    validatedParams.recordingHint = boolFromString(
-        newParams.get(CameraParameters::KEY_RECORDING_HINT) );
-
     // VIDEO_STABILIZATION
     validatedParams.videoStabilization = boolFromString(
         newParams.get(CameraParameters::KEY_VIDEO_STABILIZATION) );
@@ -1645,7 +1731,12 @@
     if (res != OK) return res;
     delete[] reqMeteringAreas;
 
-    CropRegion crop = calculateCropRegion();
+    /* don't include jpeg thumbnail size - it's valid for
+       it to be set to (0,0), meaning 'no thumbnail' */
+    CropRegion crop = calculateCropRegion( (CropRegion::Outputs)(
+            CropRegion::OUTPUT_PREVIEW     |
+            CropRegion::OUTPUT_VIDEO       |
+            CropRegion::OUTPUT_PICTURE    ));
     int32_t reqCropRegion[3] = { crop.left, crop.top, crop.width };
     res = request->update(ANDROID_SCALER_CROP_REGION,
             reqCropRegion, 3);
@@ -2080,23 +2171,123 @@
     return -1;
 }
 
+int Parameters::cropXToArray(int x) const {
+    ALOG_ASSERT(x >= 0, "Crop-relative X coordinate = '%d' is out of bounds"
+                         "(lower = 0)", x);
+
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    ALOG_ASSERT(x < previewCrop.width, "Crop-relative X coordinate = '%d' "
+                    "is out of bounds (upper = %d)", x, previewCrop.width);
+
+    int ret = x + previewCrop.left;
+
+    ALOG_ASSERT( (ret >= 0 && ret < fastInfo.arrayWidth),
+        "Calculated pixel array value X = '%d' is out of bounds (upper = %d)",
+        ret, fastInfo.arrayWidth);
+    return ret;
+}
+
+int Parameters::cropYToArray(int y) const {
+    ALOG_ASSERT(y >= 0, "Crop-relative Y coordinate = '%d' is out of bounds "
+        "(lower = 0)", y);
+
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    ALOG_ASSERT(y < previewCrop.height, "Crop-relative Y coordinate = '%d' is "
+                "out of bounds (upper = %d)", y, previewCrop.height);
+
+    int ret = y + previewCrop.top;
+
+    ALOG_ASSERT( (ret >= 0 && ret < fastInfo.arrayHeight),
+        "Calculated pixel array value Y = '%d' is out of bounds (upper = %d)",
+        ret, fastInfo.arrayHeight);
+
+    return ret;
+
+}
+
+int Parameters::normalizedXToCrop(int x) const {
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    return (x + 1000) * (previewCrop.width - 1) / 2000;
+}
+
+int Parameters::normalizedYToCrop(int y) const {
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    return (y + 1000) * (previewCrop.height - 1) / 2000;
+}
+
+int Parameters::arrayXToCrop(int x) const {
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    return x - previewCrop.left;
+}
+
+int Parameters::arrayYToCrop(int y) const {
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    return y - previewCrop.top;
+}
+
+int Parameters::cropXToNormalized(int x) const {
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    return x * 2000 / (previewCrop.width - 1) - 1000;
+}
+
+int Parameters::cropYToNormalized(int y) const {
+    CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+    return y * 2000 / (previewCrop.height - 1) - 1000;
+}
+
 int Parameters::arrayXToNormalized(int width) const {
-    return width * 2000 / (fastInfo.arrayWidth - 1) - 1000;
+    int ret = cropXToNormalized(arrayXToCrop(width));
+
+    ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of "
+        "lower bounds %d", ret);
+    ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of "
+        "upper bounds %d", ret);
+
+    // Work-around for HAL pre-scaling the coordinates themselves
+    if (quirks.meteringCropRegion) {
+        return width * 2000 / (fastInfo.arrayWidth - 1) - 1000;
+    }
+
+    return ret;
 }
 
 int Parameters::arrayYToNormalized(int height) const {
-    return height * 2000 / (fastInfo.arrayHeight - 1) - 1000;
+    int ret = cropYToNormalized(arrayYToCrop(height));
+
+    ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of lower bounds"
+        " %d", ret);
+    ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of upper bounds"
+        " %d", ret);
+
+    // Work-around for HAL pre-scaling the coordinates themselves
+    if (quirks.meteringCropRegion) {
+        return height * 2000 / (fastInfo.arrayHeight - 1) - 1000;
+    }
+
+    return ret;
 }
 
 int Parameters::normalizedXToArray(int x) const {
-    return (x + 1000) * (fastInfo.arrayWidth - 1) / 2000;
+
+    // Work-around for HAL pre-scaling the coordinates themselves
+    if (quirks.meteringCropRegion) {
+        return (x + 1000) * (fastInfo.arrayWidth - 1) / 2000;
+    }
+
+    return cropXToArray(normalizedXToCrop(x));
 }
 
 int Parameters::normalizedYToArray(int y) const {
-    return (y + 1000) * (fastInfo.arrayHeight - 1) / 2000;
+    // Work-around for HAL pre-scaling the coordinates themselves
+    if (quirks.meteringCropRegion) {
+        return (y + 1000) * (fastInfo.arrayHeight - 1) / 2000;
+    }
+
+    return cropYToArray(normalizedYToCrop(y));
 }
 
-Parameters::CropRegion Parameters::calculateCropRegion(void) const {
+Parameters::CropRegion Parameters::calculateCropRegion(
+                            Parameters::CropRegion::Outputs outputs) const {
 
     float zoomLeft, zoomTop, zoomWidth, zoomHeight;
 
@@ -2139,9 +2330,7 @@
         float outputSizes[][2] = {
             { previewWidth,     previewHeight },
             { videoWidth,       videoHeight },
-            /* don't include jpeg thumbnail size - it's valid for
-               it to be set to (0,0), meaning 'no thumbnail' */
-        //  { jpegThumbSize[0], jpegThumbSize[1] },
+            { jpegThumbSize[0], jpegThumbSize[1] },
             { pictureWidth,     pictureHeight },
         };
 
@@ -2152,6 +2341,11 @@
              i < sizeof(outputSizes) / sizeof(outputSizes[0]);
              ++i) {
 
+            // skip over outputs we don't want to consider for the crop region
+            if ( !((1 << i) & outputs) ) {
+                continue;
+            }
+
             float outputWidth = outputSizes[i][0];
             float outputHeight = outputSizes[i][1];
             float outputRatio = outputWidth / outputHeight;
@@ -2201,5 +2395,9 @@
     return crop;
 }
 
+int32_t Parameters::fpsFromRange(int32_t min, int32_t max) const {
+    return max;
+}
+
 }; // namespace camera2
 }; // namespace android
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index a85500f..daae7c4 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -88,6 +88,9 @@
         FOCUS_MODE_INVALID = -1
     } focusMode;
 
+    // For use with triggerAfWithAuto quirk
+    focusMode_t shadowFocusMode;
+
     struct Area {
         int left, top, right, bottom;
         int weight;
@@ -183,6 +186,7 @@
     struct Quirks {
         bool triggerAfWithAuto;
         bool useZslFormat;
+        bool meteringCropRegion;
     } quirks;
 
     /**
@@ -225,8 +229,15 @@
         float top;
         float width;
         float height;
+
+        enum Outputs {
+            OUTPUT_PREVIEW         = 0x01,
+            OUTPUT_VIDEO           = 0x02,
+            OUTPUT_JPEG_THUMBNAIL  = 0x04,
+            OUTPUT_PICTURE         = 0x08,
+        };
     };
-    CropRegion calculateCropRegion(void) const;
+    CropRegion calculateCropRegion(CropRegion::Outputs outputs) const;
 
     // Static methods for debugging and converting between camera1 and camera2
     // parameters
@@ -264,6 +275,29 @@
     int arrayYToNormalized(int height) const;
     int normalizedXToArray(int x) const;
     int normalizedYToArray(int y) const;
+
+    struct Range {
+        int min;
+        int max;
+    };
+
+    int32_t fpsFromRange(int32_t min, int32_t max) const;
+
+private:
+
+    // Convert between HAL2 sensor array coordinates and
+    // viewfinder crop-region relative array coordinates
+    int cropXToArray(int x) const;
+    int cropYToArray(int y) const;
+    int arrayXToCrop(int x) const;
+    int arrayYToCrop(int y) const;
+
+    // Convert between viewfinder crop-region relative array coordinates
+    // and camera API (-1000,1000)-(1000,1000) normalized coords
+    int cropXToNormalized(int x) const;
+    int cropYToNormalized(int y) const;
+    int normalizedXToCrop(int x) const;
+    int normalizedYToCrop(int y) const;
 };
 
 // This class encapsulates the Parameters class so that it can only be accessed
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
index 8921172..207f780 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
@@ -33,7 +33,10 @@
 
 StreamingProcessor::StreamingProcessor(wp<Camera2Client> client):
         mClient(client),
+        mActiveRequest(NONE),
+        mPreviewRequestId(Camera2Client::kPreviewRequestIdStart),
         mPreviewStreamId(NO_STREAM),
+        mRecordingRequestId(Camera2Client::kRecordingRequestIdStart),
         mRecordingStreamId(NO_STREAM),
         mRecordingHeapCount(kDefaultRecordingHeapCount)
 {
@@ -90,7 +93,12 @@
     }
 
     res = mPreviewRequest.update(ANDROID_REQUEST_ID,
-            &Camera2Client::kPreviewRequestId, 1);
+            &mPreviewRequestId, 1);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update request id for preview: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
 
     return OK;
 }
@@ -190,7 +198,7 @@
     return OK;
 }
 
-status_t StreamingProcessor::getPreviewStreamId() const {
+int StreamingProcessor::getPreviewStreamId() const {
     Mutex::Autolock m(mMutex);
     return mPreviewStreamId;
 }
@@ -246,6 +254,14 @@
         return res;
     }
 
+    res = mRecordingRequest.update(ANDROID_REQUEST_ID,
+            &mRecordingRequestId, 1);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update request id for request: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return res;
+    }
+
     return OK;
 }
 
@@ -342,7 +358,7 @@
     return OK;
 }
 
-status_t StreamingProcessor::getRecordingStreamId() const {
+int StreamingProcessor::getRecordingStreamId() const {
     return mRecordingStreamId;
 }
 
@@ -351,6 +367,8 @@
     ATRACE_CALL();
     status_t res;
 
+    if (type == NONE) return INVALID_OPERATION;
+
     sp<Camera2Client> client = mClient.promote();
     if (client == 0) return INVALID_OPERATION;
 
@@ -384,6 +402,7 @@
                 __FUNCTION__, client->getCameraId(), strerror(-res), res);
         return res;
     }
+    mActiveRequest = type;
 
     return OK;
 }
@@ -392,6 +411,8 @@
     ATRACE_CALL();
     status_t res;
 
+    Mutex::Autolock m(mMutex);
+
     sp<Camera2Client> client = mClient.promote();
     if (client == 0) return INVALID_OPERATION;
     sp<Camera2Device> device = client->getCameraDevice();
@@ -402,11 +423,38 @@
                 __FUNCTION__, client->getCameraId(), strerror(-res), res);
         return res;
     }
-    res = device->waitUntilDrained();
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
-                __FUNCTION__, client->getCameraId(), strerror(-res), res);
-        return res;
+    mActiveRequest = NONE;
+
+    return OK;
+}
+
+int32_t StreamingProcessor::getActiveRequestId() const {
+    Mutex::Autolock m(mMutex);
+    switch (mActiveRequest) {
+        case NONE:
+            return 0;
+        case PREVIEW:
+            return mPreviewRequestId;
+        case RECORD:
+            return mRecordingRequestId;
+        default:
+            ALOGE("%s: Unexpected mode %d", __FUNCTION__, mActiveRequest);
+            return 0;
+    }
+}
+
+status_t StreamingProcessor::incrementStreamingIds() {
+    ATRACE_CALL();
+    Mutex::Autolock m(mMutex);
+
+    status_t res;
+    mPreviewRequestId++;
+    if (mPreviewRequestId >= Camera2Client::kPreviewRequestIdEnd) {
+        mPreviewRequestId = Camera2Client::kPreviewRequestIdStart;
+    }
+    mRecordingRequestId++;
+    if (mRecordingRequestId >= Camera2Client::kRecordingRequestIdEnd) {
+        mRecordingRequestId = Camera2Client::kRecordingRequestIdStart;
     }
     return OK;
 }
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/camera2/StreamingProcessor.h
index ac58614..96b100f 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.h
@@ -57,6 +57,7 @@
     int getRecordingStreamId() const;
 
     enum StreamType {
+        NONE,
         PREVIEW,
         RECORD
     };
@@ -65,6 +66,11 @@
 
     status_t stopStream();
 
+    // Returns the request ID for the currently streaming request
+    // Returns 0 if there is no active request.
+    status_t getActiveRequestId() const;
+    status_t incrementStreamingIds();
+
     // Callback for new recording frames from HAL
     virtual void onFrameAvailable();
     // Callback from stagefright which returns used recording frames
@@ -81,12 +87,16 @@
 
     wp<Camera2Client> mClient;
 
+    StreamType mActiveRequest;
+
     // Preview-related members
+    int32_t mPreviewRequestId;
     int mPreviewStreamId;
     CameraMetadata mPreviewRequest;
     sp<ANativeWindow> mPreviewWindow;
 
     // Recording-related members
+    int32_t mRecordingRequestId;
     int mRecordingStreamId;
     int mRecordingFrameCount;
     sp<BufferItemConsumer> mRecordingConsumer;
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.cpp b/services/camera/libcameraservice/camera2/ZslProcessor.cpp
index 56e9743..1937955 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.cpp
@@ -69,16 +69,16 @@
     }
 }
 
-void ZslProcessor::onFrameAvailable(int32_t frameId, CameraMetadata &frame) {
+void ZslProcessor::onFrameAvailable(int32_t frameId, const CameraMetadata &frame) {
     Mutex::Autolock l(mInputMutex);
-    camera_metadata_entry_t entry;
+    camera_metadata_ro_entry_t entry;
     entry = frame.find(ANDROID_SENSOR_TIMESTAMP);
     nsecs_t timestamp = entry.data.i64[0];
     ALOGVV("Got preview frame for timestamp %lld", timestamp);
 
     if (mState != RUNNING) return;
 
-    mFrameList.editItemAt(mFrameListHead).acquire(frame);
+    mFrameList.editItemAt(mFrameListHead) = frame;
     mFrameListHead = (mFrameListHead + 1) % kFrameListDepth;
 
     findMatchesLocked();
@@ -185,7 +185,9 @@
             return res;
         }
     }
-    client->registerFrameListener(Camera2Client::kPreviewRequestId, this);
+    client->registerFrameListener(Camera2Client::kPreviewRequestIdStart,
+            Camera2Client::kPreviewRequestIdEnd,
+            this);
 
     return OK;
 }
@@ -252,11 +254,14 @@
     if (mZslQueueTail != mZslQueueHead) {
         CameraMetadata request;
         size_t index = mZslQueueTail;
-        while (request.isEmpty() && index != mZslQueueHead) {
-            request = mZslQueue[index].frame;
+        while (index != mZslQueueHead) {
+            if (!mZslQueue[index].frame.isEmpty()) {
+                request = mZslQueue[index].frame;
+                break;
+            }
             index = (index + 1) % kZslBufferDepth;
         }
-        if (request.isEmpty()) {
+        if (index == mZslQueueHead) {
             ALOGV("%s: ZSL queue has no valid frames to send yet.",
                   __FUNCTION__);
             return NOT_ENOUGH_DATA;
@@ -297,7 +302,7 @@
             return INVALID_OPERATION;
         }
 
-        res = client->getCameraDevice()->clearStreamingRequest();
+        res = client->stopStream();
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: "
                 "%s (%d)",
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.h b/services/camera/libcameraservice/camera2/ZslProcessor.h
index 1f433ce..c80e7f4 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.h
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.h
@@ -52,7 +52,7 @@
     // From mZslConsumer
     virtual void onFrameAvailable();
     // From FrameProcessor
-    virtual void onFrameAvailable(int32_t frameId, CameraMetadata &frame);
+    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
 
     virtual void onBufferReleased(buffer_handle_t *handle);