Refactor capture processing to be async friendly

Set up a series of methods that can be producers/consumers/callbacks.
For now, they call each other synchronously.

TEST: manually tested with app
BUG: 29334616

Change-Id: Icb6cf9d9970521d5291c54f02dc5032f43b26616
diff --git a/modules/camera/3_4/camera.cpp b/modules/camera/3_4/camera.cpp
index a1f7e86..7a34af5 100644
--- a/modules/camera/3_4/camera.cpp
+++ b/modules/camera/3_4/camera.cpp
@@ -383,121 +383,119 @@
     return mTemplates[type]->getAndLock();
 }
 
-int Camera::processCaptureRequest(camera3_capture_request_t *request)
+int Camera::processCaptureRequest(camera3_capture_request_t *temp_request)
 {
     int res;
 
-    ALOGV("%s:%d: request=%p", __func__, mId, request);
+    ALOGV("%s:%d: request=%p", __func__, mId, temp_request);
     ATRACE_CALL();
 
-    if (request == NULL) {
+    if (temp_request == NULL) {
         ALOGE("%s:%d: NULL request recieved", __func__, mId);
         return -EINVAL;
     }
 
-    ALOGV("%s:%d: Request Frame:%d Settings:%p", __func__, mId,
-            request->frame_number, request->settings);
+    // Make a persistent copy of request, since otherwise it won't live
+    // past the end of this method.
+    std::shared_ptr<CaptureRequest> request = std::make_shared<CaptureRequest>(temp_request);
 
-    // NULL indicates use last settings
-    if (request->settings == NULL && !mSettingsSet) {
-        ALOGE("%s:%d: NULL settings without previous set Frame:%d Req:%p",
-              __func__, mId, request->frame_number, request);
+    ALOGV("%s:%d: Request Frame:%d", __func__, mId,
+            request->frame_number);
+
+    // Null/Empty indicates use last settings
+    if (request->settings.isEmpty() && !mSettingsSet) {
+        ALOGE("%s:%d: NULL settings without previous set Frame:%d",
+              __func__, mId, request->frame_number);
         return -EINVAL;
     }
 
     if (request->input_buffer != NULL) {
         ALOGV("%s:%d: Reprocessing input buffer %p", __func__, mId,
-                request->input_buffer);
-
-        if (!isValidReprocessSettings(request->settings)) {
-            ALOGE("%s:%d: Invalid settings for reprocess request: %p",
-                    __func__, mId, request->settings);
-            return -EINVAL;
-        }
+              request->input_buffer.get());
     } else {
         ALOGV("%s:%d: Capturing new frame.", __func__, mId);
-
-        // Make a copy since CameraMetadata doesn't support weak ownership,
-        // but |request| is supposed to maintain ownership.
-        android::CameraMetadata request_settings;
-        request_settings = request->settings;
-        if (!isValidCaptureSettings(request_settings)) {
-            ALOGE("%s:%d: Invalid settings for capture request: %p",
-                    __func__, mId, request->settings);
-            return -EINVAL;
-        }
-        // Settings are valid, go ahead and set them.
-        res = setSettings(request_settings);
-        if (res) {
-          ALOGE("%s:%d: Failed to set valid settings for capture request: %p",
-                __func__, mId, request->settings);
-          return res;
-        }
-        mSettingsSet = true;
     }
 
-    // Setup and process output buffers.
-    if (request->num_output_buffers <= 0) {
-        ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId,
-                request->num_output_buffers);
+    if (!isValidRequest(*request)) {
+        ALOGE("%s:%d: Invalid request.", __func__, mId);
         return -EINVAL;
     }
-    camera3_capture_result result;
-    result.num_output_buffers = request->num_output_buffers;
-    std::vector<camera3_stream_buffer_t> output_buffers(
-        result.num_output_buffers);
-    for (unsigned int i = 0; i < request->num_output_buffers; i++) {
-        res = processCaptureBuffer(&request->output_buffers[i],
-                                   &output_buffers[i]);
+    // Valid settings have been provided (mSettingsSet is a misnomer;
+    // all that matters is that a previous request with valid settings
+    // has been passed to the device, not that they've been set).
+    mSettingsSet = true;
+
+    // Pre-process output buffers.
+    if (request->output_buffers.size() <= 0) {
+        ALOGE("%s:%d: Invalid number of output buffers: %d", __func__, mId,
+              request->output_buffers.size());
+        return -EINVAL;
+    }
+    for (auto& output_buffer : request->output_buffers) {
+        res = preprocessCaptureBuffer(&output_buffer);
         if (res)
             return -ENODEV;
     }
-    result.output_buffers = &output_buffers[0];
 
-    // Get metadata for this frame. Since the framework guarantees only
-    // one call to process_capture_request at a time, this call is guaranteed
-    // to correspond with the most recently enqueued buffer.
+    // Send the request off to the device for completion.
+    enqueueRequest(request);
 
-    android::CameraMetadata result_metadata;
-    uint64_t timestamp = 0;
-    // TODO(b/29334616): this may also want to use a callback, since
-    // the shutter may not happen immediately.
-    res = getResultSettings(&result_metadata, &timestamp);
-    if (res) {
-        return res;
-    }
-    result.result = result_metadata.release();
-
-    // Notify the framework with the shutter time.
-    result.frame_number = request->frame_number;
-    notifyShutter(result.frame_number, timestamp);
-
-    // TODO(b/29334616): asynchronously return results (the following should
-    // be done once all enqueued buffers for the request complete and callback).
-    result.partial_result = 1;
-    mCallbackOps->process_capture_result(mCallbackOps, &result);
-
+    // Request is now in flight. The device will call completeRequest
+    // asynchronously when it is done filling buffers and metadata.
+    // TODO(b/31653306): Track requests in flight to ensure not too many are
+    // sent at a time, and so they can be dumped even if the device loses them.
     return 0;
 }
 
-bool Camera::isValidReprocessSettings(const camera_metadata_t* /*settings*/)
+void Camera::completeRequest(std::shared_ptr<CaptureRequest> request, int err)
 {
-    // TODO: reject settings that cannot be reprocessed
-    // input buffers unimplemented, use this to reject reprocessing requests
-    ALOGE("%s:%d: Input buffer reprocessing not implemented", __func__, mId);
-    return false;
+    // TODO(b/31653306): make sure this is actually a request in flight,
+    // and not a random new one or a cancelled one. If so, stop tracking.
+
+    if (err) {
+        ALOGE("%s:%d: Error completing request for frame %d.",
+              __func__, mId, request->frame_number);
+        // TODO(b/31653322): Send REQUEST error.
+        return;
+    }
+
+    // Notify the framework with the shutter time (extracted from the result).
+    int64_t timestamp = 0;
+    // TODO(b/31360070): The general metadata methods should be part of the
+    // default_camera_hal namespace, not the v4l2_camera_hal namespace.
+    int res = v4l2_camera_hal::SingleTagValue(
+        request->settings, ANDROID_SENSOR_TIMESTAMP, &timestamp);
+    if (res) {
+        // TODO(b/31653322): Send RESULT error.
+        ALOGE("%s:%d: Request for frame %d is missing required metadata.",
+              __func__, mId, request->frame_number);
+        return;
+    }
+    notifyShutter(request->frame_number, timestamp);
+
+    // TODO(b/31653322): Check all returned buffers for errors
+    // (if any, send BUFFER error).
+
+    // Fill in the result struct
+    // (it only needs to live until the end of the framework callback).
+    camera3_capture_result_t result {
+        request->frame_number,
+        request->settings.getAndLock(),
+        request->output_buffers.size(),
+        request->output_buffers.data(),
+        request->input_buffer.get(),
+        1  // Total result; only 1 part.
+    };
+    mCallbackOps->process_capture_result(mCallbackOps, &result);
 }
 
-int Camera::processCaptureBuffer(const camera3_stream_buffer_t *in,
-        camera3_stream_buffer_t *out)
+int Camera::preprocessCaptureBuffer(camera3_stream_buffer_t *buffer)
 {
     int res;
-    // TODO(b/29334616): This probably should be non-blocking (currently blocks
-    // here and on gralloc lock). Perhaps caller should put "in" in a queue
-    // initially, then have a thread that dequeues from there and calls this
-    // function.
-    if (in->acquire_fence != -1) {
-        res = sync_wait(in->acquire_fence, CAMERA_SYNC_TIMEOUT);
+    // TODO(b/29334616): This probably should be non-blocking; part
+    // of the asynchronous request processing.
+    if (buffer->acquire_fence != -1) {
+        res = sync_wait(buffer->acquire_fence, CAMERA_SYNC_TIMEOUT);
         if (res == -ETIME) {
             ALOGE("%s:%d: Timeout waiting on buffer acquire fence",
                     __func__, mId);
@@ -509,41 +507,17 @@
         }
     }
 
-    out->stream = in->stream;
-    out->buffer = in->buffer;
-    out->status = CAMERA3_BUFFER_STATUS_OK;
+    // Acquire fence has been waited upon.
+    buffer->acquire_fence = -1;
+    // No release fence waiting unless the device sets it.
+    buffer->release_fence = -1;
 
-    // Enqueue buffer for software-painting
-    res = enqueueBuffer(out);
-    if (res) {
-      return res;
-    }
-
-    // TODO(b/29334616): This should be part of a callback made when the
-    // enqueued buffer finishes painting.
-    // TODO: use driver-backed release fences
-    out->acquire_fence = -1;
-    out->release_fence = -1;
+    buffer->status = CAMERA3_BUFFER_STATUS_OK;
     return 0;
 }
 
 void Camera::notifyShutter(uint32_t frame_number, uint64_t timestamp)
 {
-    int res;
-    struct timespec ts;
-
-    // If timestamp is 0, get timestamp from right now instead
-    if (timestamp == 0) {
-        ALOGW("%s:%d: No timestamp provided, using CLOCK_BOOTTIME",
-                __func__, mId);
-        res = clock_gettime(CLOCK_BOOTTIME, &ts);
-        if (res == 0) {
-            timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
-        } else {
-            ALOGE("%s:%d: No timestamp and failed to get CLOCK_BOOTTIME %s(%d)",
-                    __func__, mId, strerror(errno), errno);
-        }
-    }
     camera3_notify_msg_t m;
     memset(&m, 0, sizeof(m));
     m.type = CAMERA3_MSG_SHUTTER;