Merge "Camera2: Skeleton for output frame processing, plus face detect" into jb-mr1-dev
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 9c2fbcb..08a54c5 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -53,6 +53,7 @@
         Client(cameraService, cameraClient,
                 cameraId, cameraFacing, clientPid),
         mState(DISCONNECTED),
+        mDeviceInfo(NULL),
         mPreviewStreamId(NO_STREAM),
         mPreviewRequest(NULL),
         mCaptureStreamId(NO_STREAM),
@@ -89,7 +90,9 @@
     }
 
     res = mDevice->setNotifyCallback(this);
+    res = mDevice->setFrameListener(this);
 
+    res = buildDeviceInfo();
     res = buildDefaultParameters();
     if (res != OK) {
         ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
@@ -377,6 +380,11 @@
     mDevice.clear();
     mState = DISCONNECTED;
 
+    if (mDeviceInfo != NULL) {
+        delete mDeviceInfo;
+        mDeviceInfo = NULL;
+    }
+
     CameraService::Client::disconnect();
 }
 
@@ -393,6 +401,8 @@
     }
 
     mClientPid = getCallingPid();
+
+    Mutex::Autolock iccl(mICameraClientLock);
     mCameraClient = client;
 
     return OK;
@@ -429,6 +439,8 @@
     // TODO: Check for uninterruptable conditions
 
     if (mClientPid == getCallingPid()) {
+        Mutex::Autolock iccl(mICameraClientLock);
+
         mClientPid = 0;
         mCameraClient.clear();
         return OK;
@@ -637,6 +649,7 @@
             mDevice->waitUntilDrained();
         case WAITING_FOR_PREVIEW_WINDOW:
             mState = STOPPED;
+            commandStopFaceDetectionL();
             break;
         default:
             ALOGE("%s: Camera %d: Unknown state %d", __FUNCTION__, mCameraId,
@@ -901,22 +914,27 @@
             return INVALID_OPERATION;
     }
 
-    LockedParameters::Key k(mParameters);
 
-    res = updateCaptureStream(k.mParameters);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
+    ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
 
-    if (mCaptureRequest == NULL) {
-        res = updateCaptureRequest(k.mParameters);
+    {
+        LockedParameters::Key k(mParameters);
+
+        res = updateCaptureStream(k.mParameters);
         if (res != OK) {
-            ALOGE("%s: Camera %d: Can't create still image capture request: "
-                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+            ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
             return res;
         }
+
+        if (mCaptureRequest == NULL) {
+            res = updateCaptureRequest(k.mParameters);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Can't create still image capture request: "
+                        "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+                return res;
+            }
+        }
     }
 
     camera_metadata_entry_t outputStreams;
@@ -972,6 +990,12 @@
     switch (mState) {
         case PREVIEW:
             mState = STILL_CAPTURE;
+            res = commandStopFaceDetectionL();
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Unable to stop face detection for still capture",
+                        __FUNCTION__, mCameraId);
+                return res;
+            }
             break;
         case RECORD:
             mState = VIDEO_SNAPSHOT;
@@ -1490,45 +1514,11 @@
     k.mParameters.recordingHint = recordingHint;
     k.mParameters.videoStabilization = videoStabilization;
 
-    res = updatePreviewRequest(k.mParameters);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
-    res = updateCaptureRequest(k.mParameters);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to update capture request: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
-
-    res = updateRecordingRequest(k.mParameters);
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
-                __FUNCTION__, mCameraId, strerror(-res), res);
-        return res;
-    }
-
-    if (mState == PREVIEW) {
-        res = mDevice->setStreamingRequest(mPreviewRequest);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Error streaming new preview request: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    } else if (mState == RECORD || mState == VIDEO_SNAPSHOT) {
-        res = mDevice->setStreamingRequest(mRecordingRequest);
-        if (res != OK) {
-            ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)",
-                    __FUNCTION__, mCameraId, strerror(-res), res);
-            return res;
-        }
-    }
-
     k.mParameters.paramsFlattened = params;
 
-    return OK;
+    res = updateRequests(k.mParameters);
+
+    return res;
 }
 
 String8 Camera2Client::getParameters() const {
@@ -1637,13 +1627,55 @@
 }
 
 status_t Camera2Client::commandStartFaceDetectionL(int type) {
-    ALOGE("%s: Unimplemented!", __FUNCTION__);
-    return OK;
+    ALOGV("%s: Camera %d: Starting face detection",
+          __FUNCTION__, mCameraId);
+    status_t res;
+    switch (mState) {
+        case DISCONNECTED:
+        case STOPPED:
+        case WAITING_FOR_PREVIEW_WINDOW:
+        case STILL_CAPTURE:
+            ALOGE("%s: Camera %d: Cannot start face detection without preview active",
+                    __FUNCTION__, mCameraId);
+            return INVALID_OPERATION;
+        case PREVIEW:
+        case RECORD:
+        case VIDEO_SNAPSHOT:
+            // Good to go for starting face detect
+            break;
+    }
+    // Ignoring type
+    if (mDeviceInfo->bestFaceDetectMode == ANDROID_STATS_FACE_DETECTION_OFF) {
+        ALOGE("%s: Camera %d: Face detection not supported",
+                __FUNCTION__, mCameraId);
+        return INVALID_OPERATION;
+    }
+
+    LockedParameters::Key k(mParameters);
+    if (k.mParameters.enableFaceDetect) return OK;
+
+    k.mParameters.enableFaceDetect = true;
+
+    res = updateRequests(k.mParameters);
+
+    return res;
 }
 
 status_t Camera2Client::commandStopFaceDetectionL() {
-    ALOGE("%s: Unimplemented!", __FUNCTION__);
-    return OK;
+    status_t res = OK;
+    ALOGV("%s: Camera %d: Stopping face detection",
+          __FUNCTION__, mCameraId);
+
+    LockedParameters::Key k(mParameters);
+    if (!k.mParameters.enableFaceDetect) return OK;
+
+    k.mParameters.enableFaceDetect = false;
+
+    if (mState == PREVIEW || mState == RECORD || mState == VIDEO_SNAPSHOT) {
+        res = updateRequests(k.mParameters);
+    }
+
+    return res;
 }
 
 status_t Camera2Client::commandEnableFocusMoveMsgL(bool enable) {
@@ -1791,11 +1823,17 @@
         }
     }
     if (sendMovingMessage) {
-        mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
-                afInMotion ? 1 : 0, 0);
+        Mutex::Autolock iccl(mICameraClientLock);
+        if (mCameraClient != 0) {
+            mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
+                    afInMotion ? 1 : 0, 0);
+        }
     }
     if (sendCompletedMessage) {
-        mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, success ? 1 : 0, 0);
+        Mutex::Autolock iccl(mICameraClientLock);
+        if (mCameraClient != 0) {
+            mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, success ? 1 : 0, 0);
+        }
     }
 }
 
@@ -1809,6 +1847,149 @@
             __FUNCTION__, newState, triggerId);
 }
 
+void Camera2Client::onNewFrameAvailable() {
+    status_t res;
+    camera_metadata_t *frame = NULL;
+    do {
+        res = mDevice->getNextFrame(&frame);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error getting next frame: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return;
+        }
+        if (frame != NULL) {
+            camera_metadata_entry_t entry;
+            res = find_camera_metadata_entry(frame, ANDROID_REQUEST_FRAME_COUNT,
+                    &entry);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Error reading frame number: %s (%d)",
+                        __FUNCTION__, mCameraId, strerror(-res), res);
+                break;
+            }
+
+            res = processFrameFaceDetect(frame);
+            if (res != OK) break;
+
+            free_camera_metadata(frame);
+        }
+    } while (frame != NULL);
+
+    if (frame != NULL) {
+        free_camera_metadata(frame);
+    }
+    return;
+}
+
+status_t Camera2Client::processFrameFaceDetect(camera_metadata_t *frame) {
+    status_t res;
+    camera_metadata_entry_t entry;
+    bool enableFaceDetect;
+    {
+        LockedParameters::Key k(mParameters);
+        enableFaceDetect = k.mParameters.enableFaceDetect;
+    }
+    res = find_camera_metadata_entry(frame, ANDROID_STATS_FACE_DETECT_MODE,
+            &entry);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Error reading face mode: %s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+    uint8_t faceDetectMode = entry.data.u8[0];
+
+    if (enableFaceDetect && faceDetectMode != ANDROID_STATS_FACE_DETECTION_OFF) {
+        res = find_camera_metadata_entry(frame, ANDROID_STATS_FACE_RECTANGLES,
+                &entry);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error reading face rectangles: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+        camera_frame_metadata metadata;
+        metadata.number_of_faces = entry.count / 4;
+        if (metadata.number_of_faces >
+                mDeviceInfo->maxFaces) {
+            ALOGE("%s: Camera %d: More faces than expected! (Got %d, max %d)",
+                    __FUNCTION__, mCameraId,
+                    metadata.number_of_faces, mDeviceInfo->maxFaces);
+            return res;
+        }
+        int32_t *faceRects = entry.data.i32;
+
+        res = find_camera_metadata_entry(frame, ANDROID_STATS_FACE_SCORES,
+                &entry);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error reading face scores: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+        uint8_t *faceScores = entry.data.u8;
+
+        int32_t *faceLandmarks = NULL;
+        int32_t *faceIds = NULL;
+
+        if (faceDetectMode == ANDROID_STATS_FACE_DETECTION_FULL) {
+            res = find_camera_metadata_entry(frame, ANDROID_STATS_FACE_LANDMARKS,
+                    &entry);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Error reading face landmarks: %s (%d)",
+                        __FUNCTION__, mCameraId, strerror(-res), res);
+                return res;
+            }
+            faceLandmarks = entry.data.i32;
+
+            res = find_camera_metadata_entry(frame, ANDROID_STATS_FACE_IDS,
+                    &entry);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Error reading face IDs: %s (%d)",
+                        __FUNCTION__, mCameraId, strerror(-res), res);
+                return res;
+            }
+            faceIds = entry.data.i32;
+        }
+
+        Vector<camera_face_t> faces;
+        faces.setCapacity(metadata.number_of_faces);
+
+        for (int i = 0; i < metadata.number_of_faces; i++) {
+            camera_face_t face;
+
+            face.rect[0] = arrayXToNormalized(faceRects[i*4 + 0]);
+            face.rect[1] = arrayYToNormalized(faceRects[i*4 + 1]);
+            face.rect[2] = arrayXToNormalized(faceRects[i*4 + 2]);
+            face.rect[3] = arrayYToNormalized(faceRects[i*4 + 3]);
+
+            face.score = faceScores[i];
+            if (faceDetectMode == ANDROID_STATS_FACE_DETECTION_FULL) {
+                face.id = faceIds[i];
+                face.left_eye[0] = arrayXToNormalized(faceLandmarks[i*6 + 0]);
+                face.left_eye[1] = arrayYToNormalized(faceLandmarks[i*6 + 1]);
+                face.right_eye[0] = arrayXToNormalized(faceLandmarks[i*6 + 2]);
+                face.right_eye[1] = arrayYToNormalized(faceLandmarks[i*6 + 3]);
+                face.mouth[0] = arrayXToNormalized(faceLandmarks[i*6 + 4]);
+                face.mouth[1] = arrayYToNormalized(faceLandmarks[i*6 + 5]);
+            } else {
+                face.id = 0;
+                face.left_eye[0] = face.left_eye[1] = -2000;
+                face.right_eye[0] = face.right_eye[1] = -2000;
+                face.mouth[0] = face.mouth[1] = -2000;
+            }
+            faces.push_back(face);
+        }
+
+        metadata.faces = faces.editArray();
+        {
+            Mutex::Autolock iccl(mICameraClientLock);
+            if (mCameraClient != NULL) {
+                mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_METADATA,
+                        NULL, &metadata);
+            }
+        }
+    }
+    return OK;
+}
+
+
 void Camera2Client::onCaptureAvailable() {
     ATRACE_CALL();
     status_t res;
@@ -1849,7 +2030,6 @@
 
         mCaptureConsumer->unlockBuffer(imgBuffer);
 
-        currentClient = mCameraClient;
         switch (mState) {
             case STILL_CAPTURE:
                 mState = STOPPED;
@@ -1862,6 +2042,9 @@
                         mCameraId, mState);
                 break;
         }
+
+        Mutex::Autolock iccl(mICameraClientLock);
+        currentClient = mCameraClient;
     }
     // Call outside mICameraLock to allow re-entrancy from notification
     if (currentClient != 0) {
@@ -1951,6 +2134,7 @@
         memcpy(data + 4, &imgBuffer, sizeof(buffer_handle_t));
         ALOGV("%s: Camera %d: Sending out buffer_handle_t %p",
                 __FUNCTION__, mCameraId, imgBuffer);
+        Mutex::Autolock iccl(mICameraClientLock);
         currentClient = mCameraClient;
     }
     // Call outside mICameraLock to allow re-entrancy from notification
@@ -1997,6 +2181,56 @@
 
 /** Utility methods */
 
+status_t Camera2Client::buildDeviceInfo() {
+    if (mDeviceInfo != NULL) {
+        delete mDeviceInfo;
+    }
+    DeviceInfo *deviceInfo = new DeviceInfo;
+    mDeviceInfo = deviceInfo;
+
+    camera_metadata_entry_t activeArraySize =
+        staticInfo(ANDROID_SENSOR_ACTIVE_ARRAY_SIZE, 2, 2);
+    if (!activeArraySize.count) return NO_INIT;
+    deviceInfo->arrayWidth = activeArraySize.data.i32[0];
+    deviceInfo->arrayHeight = activeArraySize.data.i32[1];
+
+    camera_metadata_entry_t availableFaceDetectModes =
+        staticInfo(ANDROID_STATS_AVAILABLE_FACE_DETECT_MODES);
+    if (!availableFaceDetectModes.count) return NO_INIT;
+
+    deviceInfo->bestFaceDetectMode =
+        ANDROID_STATS_FACE_DETECTION_OFF;
+    for (size_t i = 0 ; i < availableFaceDetectModes.count; i++) {
+        switch (availableFaceDetectModes.data.u8[i]) {
+            case ANDROID_STATS_FACE_DETECTION_OFF:
+                break;
+            case ANDROID_STATS_FACE_DETECTION_SIMPLE:
+                if (deviceInfo->bestFaceDetectMode !=
+                        ANDROID_STATS_FACE_DETECTION_FULL) {
+                    deviceInfo->bestFaceDetectMode =
+                        ANDROID_STATS_FACE_DETECTION_SIMPLE;
+                }
+                break;
+            case ANDROID_STATS_FACE_DETECTION_FULL:
+                deviceInfo->bestFaceDetectMode =
+                    ANDROID_STATS_FACE_DETECTION_FULL;
+                break;
+            default:
+                ALOGE("%s: Camera %d: Unknown face detect mode %d:",
+                        __FUNCTION__, mCameraId,
+                        availableFaceDetectModes.data.u8[i]);
+                return NO_INIT;
+        }
+    }
+
+    camera_metadata_entry_t maxFacesDetected =
+        staticInfo(ANDROID_STATS_MAX_FACE_COUNT, 1, 1);
+    if (!maxFacesDetected.count) return NO_INIT;
+
+    deviceInfo->maxFaces = maxFacesDetected.data.i32[0];
+
+    return OK;
+}
 
 status_t Camera2Client::buildDefaultParameters() {
     ATRACE_CALL();
@@ -2655,10 +2889,8 @@
     params.set(CameraParameters::KEY_FOCUS_DISTANCES,
             "Infinity,Infinity,Infinity");
 
-    camera_metadata_entry_t maxFacesDetected =
-        staticInfo(ANDROID_STATS_MAX_FACE_COUNT, 1, 1);
     params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW,
-            maxFacesDetected.data.i32[0]);
+            mDeviceInfo->maxFaces);
     params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW,
             0);
 
@@ -2690,6 +2922,7 @@
 
     k.mParameters.storeMetadataInBuffers = true;
     k.mParameters.playShutterSound = true;
+    k.mParameters.enableFaceDetect = false;
     k.mParameters.afTriggerCounter = 0;
     k.mParameters.currentAfTriggerId = -1;
 
@@ -2698,6 +2931,47 @@
     return OK;
 }
 
+status_t Camera2Client::updateRequests(const Parameters &params) {
+    status_t res;
+
+    res = updatePreviewRequest(params);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+    res = updateCaptureRequest(params);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update capture request: %s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+
+    res = updateRecordingRequest(params);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+
+    if (mState == PREVIEW) {
+        res = mDevice->setStreamingRequest(mPreviewRequest);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error streaming new preview request: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+    } else if (mState == RECORD || mState == VIDEO_SNAPSHOT) {
+        res = mDevice->setStreamingRequest(mRecordingRequest);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+    }
+    return res;
+}
+
 status_t Camera2Client::updatePreviewStream(const Parameters &params) {
     ATRACE_CALL();
     status_t res;
@@ -3126,22 +3400,18 @@
             (NUM_ZOOM_STEPS-1);
     float zoomRatio = 1 + zoomIncrement * params.zoom;
 
-    camera_metadata_entry_t activePixelArraySize =
-            staticInfo(ANDROID_SENSOR_ACTIVE_ARRAY_SIZE, 2, 2);
-    int32_t arrayWidth = activePixelArraySize.data.i32[0];
-    int32_t arrayHeight = activePixelArraySize.data.i32[1];
     float zoomLeft, zoomTop, zoomWidth, zoomHeight;
     if (params.previewWidth >= params.previewHeight) {
-        zoomWidth =  arrayWidth / zoomRatio;
+        zoomWidth =  mDeviceInfo->arrayWidth / zoomRatio;
         zoomHeight = zoomWidth *
                 params.previewHeight / params.previewWidth;
     } else {
-        zoomHeight = arrayHeight / zoomRatio;
+        zoomHeight = mDeviceInfo->arrayHeight / zoomRatio;
         zoomWidth = zoomHeight *
                 params.previewWidth / params.previewHeight;
     }
-    zoomLeft = (arrayWidth - zoomWidth) / 2;
-    zoomTop = (arrayHeight - zoomHeight) / 2;
+    zoomLeft = (mDeviceInfo->arrayWidth - zoomWidth) / 2;
+    zoomTop = (mDeviceInfo->arrayHeight - zoomHeight) / 2;
 
     int32_t cropRegion[3] = { zoomLeft, zoomTop, zoomWidth };
     res = updateEntry(request,
@@ -3158,9 +3428,25 @@
             &vstabMode, 1);
     if (res != OK) return res;
 
+    uint8_t faceDetectMode = params.enableFaceDetect ?
+            mDeviceInfo->bestFaceDetectMode :
+            (uint8_t)ANDROID_STATS_FACE_DETECTION_OFF;
+    res = updateEntry(request,
+            ANDROID_STATS_FACE_DETECT_MODE,
+            &faceDetectMode, 1);
+    if (res != OK) return res;
+
     return OK;
 }
 
+int Camera2Client::arrayXToNormalized(int width) const {
+    return width * 2000 / (mDeviceInfo->arrayWidth - 1) - 1000;
+}
+
+int Camera2Client::arrayYToNormalized(int height) const {
+    return height * 2000 / (mDeviceInfo->arrayHeight - 1) - 1000;
+}
+
 status_t Camera2Client::updateEntry(camera_metadata_t *buffer,
         uint32_t tag, const void *data, size_t data_count) {
     camera_metadata_entry_t entry;
@@ -3477,4 +3763,5 @@
     return -1;
 }
 
+
 } // namespace android
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index dffd4ab..ca2edfab 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -31,8 +31,10 @@
  * Implements the android.hardware.camera API on top of
  * camera device HAL version 2.
  */
-class Camera2Client : public CameraService::Client,
-                      public Camera2Device::NotificationListener
+class Camera2Client :
+        public CameraService::Client,
+        public Camera2Device::NotificationListener,
+        public Camera2Device::FrameListener
 {
 public:
     // ICamera interface (see ICamera for details)
@@ -81,6 +83,7 @@
     virtual void notifyAutoExposure(uint8_t newState, int triggerId);
     virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId);
 
+    virtual void onNewFrameAvailable();
 private:
     enum State {
         DISCONNECTED,
@@ -102,6 +105,11 @@
     // they're called
     mutable Mutex mICameraLock;
 
+    // Mutex that must be locked by methods accessing the base Client's
+    // mCameraClient ICameraClient interface member, for sending notifications
+    // up to the camera user
+    mutable Mutex mICameraClientLock;
+
     status_t setPreviewWindowL(const sp<IBinder>& binder,
             sp<ANativeWindow> window);
 
@@ -200,13 +208,16 @@
         // listed in Camera.Parameters
         bool storeMetadataInBuffers;
         bool playShutterSound;
-        bool enableFocusMoveMessages;
+        bool enableFaceDetect;
 
+        bool enableFocusMoveMessages;
         int afTriggerCounter;
         int currentAfTriggerId;
         bool afInMotion;
     };
 
+    // This class encapsulates the Parameters class so that it can only be accessed
+    // by constructing a Key object, which locks the LockedParameter's mutex.
     class LockedParameters {
       public:
         class Key {
@@ -258,15 +269,32 @@
 
     } mParameters;
 
+    // Static device information; this is a subset of the information
+    // available through the staticInfo() method, used for frequently-accessed
+    // values or values that have to be calculated from the static information.
+    struct DeviceInfo {
+        int32_t arrayWidth;
+        int32_t arrayHeight;
+        uint8_t bestFaceDetectMode;
+        int32_t maxFaces;
+    };
+    const DeviceInfo *mDeviceInfo;
+
     /** Camera device-related private members */
 
     class Camera2Heap;
 
+    status_t updateRequests(const Parameters &params);
+
     // Number of zoom steps to simulate
     static const unsigned int NUM_ZOOM_STEPS = 10;
     // Used with stream IDs
     static const int NO_STREAM = -1;
 
+    /* Output frame metadata processing methods */
+
+    status_t processFrameFaceDetect(camera_metadata_t *frame);
+
     /* Preview related members */
 
     int mPreviewStreamId;
@@ -373,6 +401,8 @@
     camera_metadata_entry_t staticInfo(uint32_t tag,
             size_t minCount=0, size_t maxCount=0);
 
+    // Extract frequently-used camera static information into mDeviceInfo
+    status_t buildDeviceInfo();
     // Convert static camera info from a camera2 device to the
     // old API parameter map.
     status_t buildDefaultParameters();
@@ -380,6 +410,12 @@
     // Update parameters all requests use, based on mParameters
     status_t updateRequestCommon(camera_metadata_t *request, const Parameters &params);
 
+    // Map from sensor active array pixel coordinates to normalized camera parameter coordinates
+    // The former are (0,0)-(array width - 1, array height - 1), the latter from
+    // (-1000,-1000)-(1000,1000)
+    int arrayXToNormalized(int width) const;
+    int arrayYToNormalized(int height) const;
+
     // Update specific metadata entry with new values. Adds entry if it does not
     // exist, which will invalidate sorting
     static status_t updateEntry(camera_metadata_t *buffer,
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 7c97e1e..bbdee39 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -340,6 +340,14 @@
     }
 }
 
+status_t Camera2Device::setFrameListener(FrameListener *listener) {
+    return mFrameQueue.setListener(listener);
+}
+
+status_t Camera2Device::getNextFrame(camera_metadata_t **frame) {
+    return mFrameQueue.dequeue(frame);
+}
+
 status_t Camera2Device::triggerAutofocus(uint32_t id) {
     status_t res;
     ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
@@ -384,6 +392,13 @@
 }
 
 /**
+ * Camera2Device::FrameListener
+ */
+
+Camera2Device::FrameListener::~FrameListener() {
+}
+
+/**
  * Camera2Device::MetadataQueue
  */
 
@@ -392,7 +407,8 @@
             mFrameCount(0),
             mCount(0),
             mStreamSlotCount(0),
-            mSignalConsumer(true)
+            mSignalConsumer(true),
+            mListener(NULL)
 {
     camera2_request_queue_src_ops::dequeue_request = consumer_dequeue;
     camera2_request_queue_src_ops::request_count = consumer_buffer_count;
@@ -510,6 +526,12 @@
     return OK;
 }
 
+status_t Camera2Device::MetadataQueue::setListener(FrameListener *listener) {
+    Mutex::Autolock l(mMutex);
+    mListener = listener;
+    return OK;
+}
+
 status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf)
 {
     ALOGV("%s: E", __FUNCTION__);
@@ -622,6 +644,13 @@
         res = mDevice->ops->notify_request_queue_not_empty(mDevice);
         mMutex.lock();
     }
+    if (mListener != NULL) {
+        FrameListener *listener = mListener;
+        mMutex.unlock();
+        ALOGVV("%s: Signaling listener", __FUNCTION__);
+        listener->onNewFrameAvailable();
+        mMutex.lock();
+    }
     return res;
 }
 
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 9be370f..790b946 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -124,6 +124,27 @@
     status_t setNotifyCallback(NotificationListener *listener);
 
     /**
+     * Abstract class for HAL frame available notifications
+     */
+    class FrameListener {
+      public:
+        virtual void onNewFrameAvailable() = 0;
+      protected:
+        virtual ~FrameListener();
+    };
+
+    /**
+     * Set a frame listener to be notified about new frames.
+     */
+    status_t setFrameListener(FrameListener *listener);
+
+    /**
+     * Get next metadata frame from the frame queue. Returns NULL if the queue
+     * is empty; caller takes ownership of the metadata buffer.
+     */
+    status_t getNextFrame(camera_metadata_t **frame);
+
+    /**
      * Trigger auto-focus. The latest ID used in a trigger autofocus or cancel
      * autofocus call will be returned by the HAL in all subsequent AF
      * notifications.
@@ -180,6 +201,7 @@
         status_t dequeue(camera_metadata_t **buf, bool incrementCount = true);
         int      getBufferCount();
         status_t waitForBuffer(nsecs_t timeout);
+        status_t setListener(FrameListener *listener);
 
         // 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
@@ -208,6 +230,7 @@
         List<camera_metadata_t*> mStreamSlot;
 
         bool mSignalConsumer;
+        FrameListener *mListener;
 
         static MetadataQueue* getInstance(
             const camera2_frame_queue_dst_ops_t *q);