Camera2: Add trigger support, and implement autofocus

- Add skeletons for all triggering actions into Camera2Device
- Add support for AF triggers to HAL
- Add support for AF notifications from HAL

Bug: 6243944
Change-Id: I21025440849ae41f7083e1dcb72c99f8e5b2d5f7
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index cd74e6d..4566c75 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -88,6 +88,8 @@
         return NO_INIT;
     }
 
+    res = mDevice->setNotifyCallback(this);
+
     res = buildDefaultParameters();
     if (res != OK) {
         ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
@@ -846,6 +848,15 @@
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
 
+    int triggerId;
+    {
+        LockedParameters::Key k(mParameters);
+        k.mParameters.currentAfTriggerId = ++k.mParameters.afTriggerCounter;
+        triggerId = k.mParameters.currentAfTriggerId;
+    }
+
+    mDevice->triggerAutofocus(triggerId);
+
     return OK;
 }
 
@@ -855,6 +866,14 @@
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
 
+    int triggerId;
+    {
+        LockedParameters::Key k(mParameters);
+        triggerId = ++k.mParameters.afTriggerCounter;
+    }
+
+    mDevice->triggerCancelAutofocus(triggerId);
+
     return OK;
 }
 
@@ -1453,6 +1472,9 @@
     k.mParameters.sceneMode = sceneMode;
 
     k.mParameters.flashMode = flashMode;
+    if (focusMode != k.mParameters.focusMode) {
+        k.mParameters.currentAfTriggerId = -1;
+    }
     k.mParameters.focusMode = focusMode;
 
     k.mParameters.focusingAreas = focusingAreas;
@@ -1625,7 +1647,9 @@
 }
 
 status_t Camera2Client::commandEnableFocusMoveMsgL(bool enable) {
-    ALOGE("%s: Unimplemented!", __FUNCTION__);
+    LockedParameters::Key k(mParameters);
+    k.mParameters.enableFocusMoveMessages = enable;
+
     return OK;
 }
 
@@ -1678,6 +1702,102 @@
 void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
     ALOGV("%s: Autofocus state now %d, last trigger %d",
             __FUNCTION__, newState, triggerId);
+    bool sendCompletedMessage = false;
+    bool sendMovingMessage = false;
+
+    bool success = false;
+    bool afInMotion = false;
+    {
+        LockedParameters::Key k(mParameters);
+        switch (k.mParameters.focusMode) {
+            case Parameters::FOCUS_MODE_AUTO:
+            case Parameters::FOCUS_MODE_MACRO:
+                // Don't send notifications upstream if they're not for the current AF
+                // trigger. For example, if cancel was called in between, or if we
+                // already sent a notification about this AF call.
+                if (triggerId != k.mParameters.currentAfTriggerId) break;
+                switch (newState) {
+                    case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                        success = true;
+                        // no break
+                    case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                        sendCompletedMessage = true;
+                        k.mParameters.currentAfTriggerId = -1;
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+                        // Just starting focusing, ignore
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                    default:
+                        // Unexpected in AUTO/MACRO mode
+                        ALOGE("%s: Unexpected AF state transition in AUTO/MACRO mode: %d",
+                                __FUNCTION__, newState);
+                        break;
+                }
+                break;
+            case Parameters::FOCUS_MODE_CONTINUOUS_VIDEO:
+            case Parameters::FOCUS_MODE_CONTINUOUS_PICTURE:
+                switch (newState) {
+                    case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                        success = true;
+                        // no break
+                    case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                        // Don't send notifications upstream if they're not for
+                        // the current AF trigger. For example, if cancel was
+                        // called in between, or if we already sent a
+                        // notification about this AF call.
+                        // Send both a 'AF done' callback and a 'AF move' callback
+                        if (triggerId != k.mParameters.currentAfTriggerId) break;
+                        sendCompletedMessage = true;
+                        afInMotion = false;
+                        if (k.mParameters.enableFocusMoveMessages &&
+                                k.mParameters.afInMotion) {
+                            sendMovingMessage = true;
+                        }
+                        k.mParameters.currentAfTriggerId = -1;
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                        // Cancel was called, or we switched state; care if
+                        // currently moving
+                        afInMotion = false;
+                        if (k.mParameters.enableFocusMoveMessages &&
+                                k.mParameters.afInMotion) {
+                            sendMovingMessage = true;
+                        }
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                        // Start passive scan, inform upstream
+                        afInMotion = true;
+                        // no break
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                        // Stop passive scan, inform upstream
+                        if (k.mParameters.enableFocusMoveMessages) {
+                            sendMovingMessage = true;
+                        }
+                        break;
+                }
+                k.mParameters.afInMotion = afInMotion;
+                break;
+            case Parameters::FOCUS_MODE_EDOF:
+            case Parameters::FOCUS_MODE_INFINITY:
+            case Parameters::FOCUS_MODE_FIXED:
+            default:
+                if (newState != ANDROID_CONTROL_AF_STATE_INACTIVE) {
+                    ALOGE("%s: Unexpected AF state change %d (ID %d) in focus mode %d",
+                          __FUNCTION__, newState, triggerId, k.mParameters.focusMode);
+                }
+        }
+    }
+    if (sendCompletedMessage) {
+        mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, success ? 1 : 0, 0);
+    }
+    if (sendMovingMessage) {
+        mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
+                afInMotion ? 1 : 0, 0);
+    }
+
 }
 
 void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) {
@@ -2394,9 +2514,7 @@
         k.mParameters.focusMode = Parameters::FOCUS_MODE_AUTO;
         params.set(CameraParameters::KEY_FOCUS_MODE,
                 CameraParameters::FOCUS_MODE_AUTO);
-        String8 supportedFocusModes(CameraParameters::FOCUS_MODE_FIXED);
-        supportedFocusModes = supportedFocusModes + "," +
-            CameraParameters::FOCUS_MODE_INFINITY;
+        String8 supportedFocusModes(CameraParameters::FOCUS_MODE_INFINITY);
         bool addComma = true;
 
         for (size_t i=0; i < availableAfModes.count; i++) {
@@ -2573,6 +2691,8 @@
 
     k.mParameters.storeMetadataInBuffers = true;
     k.mParameters.playShutterSound = true;
+    k.mParameters.afTriggerCounter = 0;
+    k.mParameters.currentAfTriggerId = -1;
 
     k.mParameters.paramsFlattened = params.flatten();
 
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 8c10d6b..dffd4ab 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -200,6 +200,11 @@
         // listed in Camera.Parameters
         bool storeMetadataInBuffers;
         bool playShutterSound;
+        bool enableFocusMoveMessages;
+
+        int afTriggerCounter;
+        int currentAfTriggerId;
+        bool afInMotion;
     };
 
     class LockedParameters {
@@ -313,9 +318,6 @@
     camera_metadata_t *mRecordingRequest;
     sp<Camera2Heap> mRecordingHeap;
 
-    // TODO: This needs to be queried from somewhere, or the BufferQueue needs
-    // to be passed all the way to stagefright. Right now, set to a large number
-    // to avoid starvation of the video encoders.
     static const size_t kDefaultRecordingHeapCount = 8;
     size_t mRecordingHeapCount;
     size_t mRecordingHeapHead, mRecordingHeapFree;
@@ -325,6 +327,10 @@
     status_t updateRecordingRequest(const Parameters &params);
     status_t updateRecordingStream(const Parameters &params);
 
+    /** Notification-related members */
+
+    bool mAfInMotion;
+
     /** Camera2Device instance wrapping HAL2 entry */
 
     sp<Camera2Device> mDevice;
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index d48c163..a009636 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -339,6 +339,42 @@
     }
 }
 
+status_t Camera2Device::triggerAutofocus(uint32_t id) {
+    status_t res;
+    ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
+    res = mDevice->ops->trigger_action(mDevice,
+            CAMERA2_TRIGGER_AUTOFOCUS, id, 0);
+    if (res != OK) {
+        ALOGE("%s: Error triggering autofocus (id %d)",
+                __FUNCTION__, id);
+    }
+    return res;
+}
+
+status_t Camera2Device::triggerCancelAutofocus(uint32_t id) {
+    status_t res;
+    ALOGV("%s: Canceling autofocus, id %d", __FUNCTION__, id);
+    res = mDevice->ops->trigger_action(mDevice,
+            CAMERA2_TRIGGER_CANCEL_AUTOFOCUS, id, 0);
+    if (res != OK) {
+        ALOGE("%s: Error canceling autofocus (id %d)",
+                __FUNCTION__, id);
+    }
+    return res;
+}
+
+status_t Camera2Device::triggerPrecaptureMetering(uint32_t id) {
+    status_t res;
+    ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
+    res = mDevice->ops->trigger_action(mDevice,
+            CAMERA2_TRIGGER_PRECAPTURE_METERING, id, 0);
+    if (res != OK) {
+        ALOGE("%s: Error triggering precapture metering (id %d)",
+                __FUNCTION__, id);
+    }
+    return res;
+}
+
 /**
  * Camera2Device::NotificationListener
  */
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 91a3fbd..9be370f 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -123,6 +123,27 @@
      */
     status_t setNotifyCallback(NotificationListener *listener);
 
+    /**
+     * 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.
+     */
+    status_t triggerAutofocus(uint32_t id);
+
+    /**
+     * Cancel auto-focus. The latest ID used in a trigger autofocus/cancel
+     * autofocus call will be returned by the HAL in all subsequent AF
+     * notifications.
+     */
+    status_t triggerCancelAutofocus(uint32_t id);
+
+    /**
+     * Trigger pre-capture metering. The latest ID used in a trigger pre-capture
+     * call will be returned by the HAL in all subsequent AE and AWB
+     * notifications.
+     */
+    status_t triggerPrecaptureMetering(uint32_t id);
+
   private:
 
     const int mId;