Add request tracking.

RequestTracker keeps track of how many buffers are in flight for
each stream, and what frame numbers correspond to what requests/buffers.

BUG: 31044638
TEST: unit tests pass

Change-Id: I8ef3fcacdf8171514ea7f7eaf77301a641bff61e
diff --git a/modules/camera/3_4/Android.mk b/modules/camera/3_4/Android.mk
index 12e8436..1211a08 100644
--- a/modules/camera/3_4/Android.mk
+++ b/modules/camera/3_4/Android.mk
@@ -39,10 +39,12 @@
 
 v4l2_src_files := \
   camera.cpp \
+  capture_request.cpp \
   format_metadata_factory.cpp \
   metadata/enum_converter.cpp \
   metadata/metadata.cpp \
   metadata/metadata_reader.cpp \
+  request_tracker.cpp \
   static_properties.cpp \
   stream.cpp \
   stream_format.cpp \
@@ -70,6 +72,7 @@
   metadata/tagged_control_delegate_test.cpp \
   metadata/tagged_control_options_test.cpp \
   metadata/v4l2_control_delegate_test.cpp \
+  request_tracker_test.cpp \
   static_properties_test.cpp \
 
 # V4L2 Camera HAL.
diff --git a/modules/camera/3_4/capture_request.cpp b/modules/camera/3_4/capture_request.cpp
new file mode 100644
index 0000000..00b20cd
--- /dev/null
+++ b/modules/camera/3_4/capture_request.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "capture_request.h"
+
+#include <set>
+
+namespace default_camera_hal {
+
+CaptureRequest::CaptureRequest() : CaptureRequest(nullptr) {}
+
+CaptureRequest::CaptureRequest(const camera3_capture_request_t* request) {
+  if (!request) {
+    return;
+  }
+
+  frame_number = request->frame_number;
+
+  // CameraMetadata makes copies of camera_metadata_t through the
+  // assignment operator (the constructor taking a camera_metadata_t*
+  // takes ownership instead).
+  settings = request->settings;
+
+  // camera3_stream_buffer_t can be default copy constructed,
+  // as its pointer values are handles, not ownerships.
+
+  // Copy the input buffer.
+  if (request->input_buffer) {
+    input_buffer =
+        std::make_unique<camera3_stream_buffer_t>(*request->input_buffer);
+  }
+
+  // Safely copy all the output buffers.
+  uint32_t num_output_buffers = request->num_output_buffers;
+  if (num_output_buffers < 0 || !request->output_buffers) {
+    num_output_buffers = 0;
+  }
+  output_buffers.insert(output_buffers.end(),
+                        request->output_buffers,
+                        request->output_buffers + num_output_buffers);
+}
+
+}  // namespace default_camera_hal
diff --git a/modules/camera/3_4/capture_request.h b/modules/camera/3_4/capture_request.h
new file mode 100644
index 0000000..e2f95fa
--- /dev/null
+++ b/modules/camera/3_4/capture_request.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
+#define DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
+
+#include <memory>
+#include <vector>
+
+#include <camera/CameraMetadata.h>
+#include <hardware/camera3.h>
+
+namespace default_camera_hal {
+
+// A simple wrapper for camera3_capture_request_t,
+// with a constructor that makes a deep copy from the original struct.
+struct CaptureRequest {
+  uint32_t frame_number;
+  android::CameraMetadata settings;
+  std::unique_ptr<camera3_stream_buffer_t> input_buffer;
+  std::vector<camera3_stream_buffer_t> output_buffers;
+
+  CaptureRequest();
+  // Create a deep copy of |request|.
+  CaptureRequest(const camera3_capture_request_t* request);
+};
+
+}  // namespace default_camera_hal
+
+#endif  // DEFAULT_CAMERA_HAL_CAPTURE_REQUEST_H_
diff --git a/modules/camera/3_4/request_tracker.cpp b/modules/camera/3_4/request_tracker.cpp
new file mode 100644
index 0000000..60715b5
--- /dev/null
+++ b/modules/camera/3_4/request_tracker.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "request_tracker.h"
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "RequestTracker"
+#include <cutils/log.h>
+
+namespace default_camera_hal {
+
+RequestTracker::RequestTracker() {}
+
+RequestTracker::~RequestTracker() {}
+
+void RequestTracker::SetStreamConfiguration(
+    const camera3_stream_configuration_t& config) {
+  // Clear the old configuration.
+  ClearStreamConfiguration();
+  // Add an entry to the buffer tracking map for each configured stream.
+  for (size_t i = 0; i < config.num_streams; ++i) {
+    buffers_in_flight_.emplace(config.streams[i], 0);
+  }
+}
+
+void RequestTracker::ClearStreamConfiguration() {
+  // The keys of the in flight buffer map are the configured streams.
+  buffers_in_flight_.clear();
+}
+
+// Helper: get the streams used by a request.
+std::set<camera3_stream_t*> RequestStreams(const CaptureRequest& request) {
+  std::set<camera3_stream_t*> result;
+  if (request.input_buffer) {
+    result.insert(request.input_buffer->stream);
+  }
+  for (const auto& output_buffer : request.output_buffers) {
+    result.insert(output_buffer.stream);
+  }
+  return std::move(result);
+}
+
+bool RequestTracker::Add(std::unique_ptr<CaptureRequest> request) {
+  if (!CanAddRequest(*request)) {
+    return false;
+  }
+
+  // Add to the count for each stream used.
+  for (const auto stream : RequestStreams(*request)) {
+    ++buffers_in_flight_[stream];
+  }
+
+  // Store the request.
+  frames_in_flight_[request->frame_number] = std::move(request);
+
+  return true;
+}
+
+bool RequestTracker::Remove(uint32_t frame_number,
+                            std::unique_ptr<CaptureRequest>* request) {
+  // Get the request.
+  std::unique_ptr<CaptureRequest> stored_request =
+      std::move(frames_in_flight_[frame_number]);
+  if (!stored_request) {
+    ALOGE("%s: Frame %u is not in flight.", __func__, frame_number);
+    return false;
+  }
+  frames_in_flight_.erase(frame_number);
+
+  // Decrement the counts of used streams.
+  for (const auto stream : RequestStreams(*stored_request)) {
+    --buffers_in_flight_[stream];
+  }
+
+  // Return the request if requested.
+  if (request) {
+    *request = std::move(stored_request);
+  }
+  return true;
+}
+
+void RequestTracker::Clear(
+    std::set<std::unique_ptr<CaptureRequest>>* requests) {
+  // If desired, extract all the currently in-flight requests.
+  if (requests) {
+    for (auto& frame_number_request : frames_in_flight_) {
+      requests->insert(std::move(frame_number_request.second));
+    }
+  }
+
+  // Clear out all tracking.
+  frames_in_flight_.clear();
+  buffers_in_flight_.clear();
+}
+
+bool RequestTracker::CanAddRequest(const CaptureRequest& request) const {
+  // Check that it's not a duplicate.
+  if (frames_in_flight_.count(request.frame_number) > 0) {
+    ALOGE("%s: Already tracking a request with frame number %d.",
+          __func__,
+          request.frame_number);
+    return false;
+  }
+
+  // Check that each stream has space
+  // (which implicitly checks if it is configured).
+  bool result = true;
+  for (const auto stream : RequestStreams(request)) {
+    if (StreamFull(stream)) {
+      ALOGE("%s: Stream %p is full.", __func__, stream);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool RequestTracker::StreamFull(const camera3_stream_t* handle) const {
+  const auto it = buffers_in_flight_.find(handle);
+  if (it == buffers_in_flight_.end()) {
+    // Unconfigured streams are implicitly full.
+    ALOGV("%s: Stream %p is not a configured stream.", __func__, handle);
+    return true;
+  } else {
+    return it->second >= it->first->max_buffers;
+  }
+}
+
+bool RequestTracker::InFlight(uint32_t frame_number) const {
+  return frames_in_flight_.count(frame_number) > 0;
+}
+
+bool RequestTracker::Empty() const {
+  return frames_in_flight_.empty();
+}
+
+}  // namespace default_camera_hal
diff --git a/modules/camera/3_4/request_tracker.h b/modules/camera/3_4/request_tracker.h
new file mode 100644
index 0000000..864c2f7
--- /dev/null
+++ b/modules/camera/3_4/request_tracker.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
+#define DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include <hardware/camera3.h>
+
+#include "capture_request.h"
+#include "common.h"
+
+namespace default_camera_hal {
+
+// Keep track of what requests and streams are in flight.
+class RequestTracker {
+ public:
+  RequestTracker();
+  virtual ~RequestTracker();
+
+  // Configuration methods. Both have undefined effects on in-flight requests,
+  // and should only be called when empty.
+  // Add configured streams. Replaces the previous configuration if any.
+  virtual void SetStreamConfiguration(
+      const camera3_stream_configuration_t& config);
+  // Reset to no configured streams.
+  virtual void ClearStreamConfiguration();
+
+  // Tracking methods.
+  // Track a request.
+  // False if a request of the same frame number is already being tracked
+  virtual bool Add(std::unique_ptr<CaptureRequest> request);
+  // Stop tracking and retreive a request by frame number.
+  // False if the given frame number is not being tracked.
+  virtual bool Remove(uint32_t frame_number,
+                      std::unique_ptr<CaptureRequest>* request = nullptr);
+  // Empty out all requests being tracked.
+  virtual void Clear(
+      std::set<std::unique_ptr<CaptureRequest>>* requests = nullptr);
+
+  // Accessors to check availability.
+  // Check that a request isn't already in flight, and won't overflow any
+  // streams.
+  virtual bool CanAddRequest(const CaptureRequest& request) const;
+  // True if the given stream is already at max capacity.
+  virtual bool StreamFull(const camera3_stream_t* handle) const;
+  // True if a request is being tracked for the given frame number.
+  virtual bool InFlight(uint32_t frame_number) const;
+  // True if no requests being tracked.
+  virtual bool Empty() const;
+
+ private:
+  // Track for each stream, how many buffers are in flight.
+  std::map<const camera3_stream_t*, size_t> buffers_in_flight_;
+  // Track the frames in flight.
+  std::map<uint32_t, std::unique_ptr<CaptureRequest>> frames_in_flight_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestTracker);
+};
+
+}  // namespace default_camera_hal
+
+#endif  // DEFAULT_CAMERA_HAL_REQUEST_TRACKER_H_
diff --git a/modules/camera/3_4/request_tracker_test.cpp b/modules/camera/3_4/request_tracker_test.cpp
new file mode 100644
index 0000000..9a6313f
--- /dev/null
+++ b/modules/camera/3_4/request_tracker_test.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "request_tracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::AtMost;
+using testing::Expectation;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace default_camera_hal {
+
+class RequestTrackerTest : public Test {
+ protected:
+  void SetUp() {
+    stream1_.max_buffers = 3;
+    stream2_.max_buffers = 3;
+    dut_.reset(new RequestTracker());
+    streams_ = {&stream1_, &stream2_};
+    camera3_stream_configuration_t config{streams_.size(), streams_.data(), 0};
+    dut_->SetStreamConfiguration(config);
+  }
+
+  std::unique_ptr<CaptureRequest> GenerateCaptureRequest(
+      uint32_t frame, std::vector<camera3_stream_t*> streams) {
+    std::unique_ptr<CaptureRequest> request =
+        std::make_unique<CaptureRequest>();
+
+    // Set the frame number and buffers.
+    request->frame_number = frame;
+    for (const auto stream : streams) {
+      // All we really care about for the buffers is which stream they're for.
+      camera3_stream_buffer_t buffer{stream, nullptr, 0, -1, -1};
+      request->output_buffers.push_back(buffer);
+    }
+
+    return std::move(request);
+  }
+
+  void AddRequest(uint32_t frame,
+                  std::vector<camera3_stream_t*> streams,
+                  bool expected = true) {
+    std::unique_ptr<CaptureRequest> request =
+        GenerateCaptureRequest(frame, streams);
+    EXPECT_EQ(dut_->CanAddRequest(*request), expected);
+    if (expected) {
+      EXPECT_FALSE(dut_->InFlight(frame));
+    }
+    EXPECT_EQ(dut_->Add(std::move(request)), expected);
+    if (expected) {
+      EXPECT_TRUE(dut_->InFlight(frame));
+    }
+  }
+
+  camera3_stream_t stream1_;
+  camera3_stream_t stream2_;
+  std::vector<camera3_stream_t*> streams_;
+  std::unique_ptr<RequestTracker> dut_;
+};
+
+TEST_F(RequestTrackerTest, AddValid) {
+  uint32_t frame = 34;
+  EXPECT_FALSE(dut_->InFlight(frame));
+  AddRequest(frame, {&stream1_});
+}
+
+TEST_F(RequestTrackerTest, AddInput) {
+  EXPECT_TRUE(dut_->Empty());
+
+  // Add a request
+  uint32_t frame = 42;
+  std::unique_ptr<CaptureRequest> expected = GenerateCaptureRequest(frame, {});
+  // Set the input buffer instead of any outputs.
+  expected->input_buffer.reset(
+      new camera3_stream_buffer_t{&stream1_, nullptr, 0, -1, -1});
+  stream1_.max_buffers = 1;
+
+  EXPECT_TRUE(dut_->Add(std::move(expected)));
+  EXPECT_TRUE(dut_->InFlight(frame));
+  // Should have added to the count of buffers for stream 1.
+  EXPECT_TRUE(dut_->StreamFull(&stream1_));
+}
+
+TEST_F(RequestTrackerTest, AddMultipleStreams) {
+  stream1_.max_buffers = 1;
+  stream2_.max_buffers = 1;
+
+  EXPECT_FALSE(dut_->StreamFull(&stream1_));
+  EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+  // Add a request using both streams.
+  AddRequest(99, {&stream1_, &stream2_});
+
+  // Should both have been counted.
+  EXPECT_TRUE(dut_->StreamFull(&stream1_));
+  EXPECT_TRUE(dut_->StreamFull(&stream2_));
+}
+
+TEST_F(RequestTrackerTest, AddUnconfigured) {
+  camera3_stream_t stream;
+  // Unconfigured should be considered full.
+  EXPECT_TRUE(dut_->StreamFull(&stream));
+  AddRequest(1, {&stream}, false);
+}
+
+TEST_F(RequestTrackerTest, AddPastCapacity) {
+  // Set the limit of stream 2 to 1.
+  stream2_.max_buffers = 1;
+
+  for (size_t i = 0; i < stream1_.max_buffers; ++i) {
+    EXPECT_FALSE(dut_->StreamFull(&stream1_));
+    EXPECT_FALSE(dut_->StreamFull(&stream2_));
+    AddRequest(i, {&stream1_});
+  }
+  // Filled up stream 1.
+  EXPECT_TRUE(dut_->StreamFull(&stream1_));
+  // Stream 2 should still not be full since nothing was added.
+  EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+  // Limit has been hit, can't add more.
+  AddRequest(stream1_.max_buffers, {&stream1_, &stream2_}, false);
+  EXPECT_TRUE(dut_->StreamFull(&stream1_));
+  // Should not have added to the count of stream 2.
+  EXPECT_FALSE(dut_->StreamFull(&stream2_));
+}
+
+TEST_F(RequestTrackerTest, AddDuplicate) {
+  uint32_t frame = 42;
+  AddRequest(frame, {&stream1_});
+  // Can't add a duplicate.
+  AddRequest(frame, {&stream2_}, false);
+}
+
+TEST_F(RequestTrackerTest, RemoveValid) {
+  EXPECT_TRUE(dut_->Empty());
+
+  // Add a request
+  uint32_t frame = 42;
+  std::unique_ptr<CaptureRequest> request =
+      GenerateCaptureRequest(frame, {&stream1_});
+  CaptureRequest* expected = request.get();
+  EXPECT_TRUE(dut_->Add(std::move(request)));
+  EXPECT_TRUE(dut_->InFlight(frame));
+  AddRequest(frame + 1, {&stream1_});
+  EXPECT_FALSE(dut_->Empty());
+
+  // Remove it.
+  request.reset();
+  EXPECT_TRUE(dut_->Remove(frame, &request));
+  // Should have removed only the desired request.
+  EXPECT_FALSE(dut_->Empty());
+
+  // Should have returned the desired request.
+  EXPECT_EQ(request.get(), expected);
+}
+
+TEST_F(RequestTrackerTest, RemoveNoResult) {
+  EXPECT_TRUE(dut_->Empty());
+
+  // Add a request
+  uint32_t frame = 42;
+  AddRequest(frame, {&stream1_});
+  EXPECT_FALSE(dut_->Empty());
+
+  // Remove it, but don't ask for the returned request.
+  EXPECT_TRUE(dut_->Remove(frame));
+  EXPECT_TRUE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, RemoveInvalid) {
+  EXPECT_TRUE(dut_->Empty());
+
+  // Add a request
+  uint32_t frame = 42;
+  AddRequest(frame, {&stream1_});
+  EXPECT_FALSE(dut_->Empty());
+
+  // Try to remove a different one.
+  uint32_t bad_frame = frame + 1;
+  EXPECT_FALSE(dut_->InFlight(bad_frame));
+  EXPECT_FALSE(dut_->Remove(frame + 1));
+  EXPECT_FALSE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, ClearRequests) {
+  // Create some requests.
+  uint32_t frame1 = 42;
+  uint32_t frame2 = frame1 + 1;
+  std::unique_ptr<CaptureRequest> request1 =
+      GenerateCaptureRequest(frame1, {&stream1_});
+  std::unique_ptr<CaptureRequest> request2 =
+      GenerateCaptureRequest(frame2, {&stream2_});
+  std::set<CaptureRequest*> expected;
+  expected.insert(request1.get());
+  expected.insert(request2.get());
+
+  // Insert them.
+  EXPECT_TRUE(dut_->Add(std::move(request1)));
+  EXPECT_TRUE(dut_->Add(std::move(request2)));
+  EXPECT_TRUE(dut_->InFlight(frame1));
+  EXPECT_TRUE(dut_->InFlight(frame2));
+  EXPECT_FALSE(dut_->Empty());
+  std::set<std::unique_ptr<CaptureRequest>> actual_wrapped;
+
+  // Clear them out.
+  dut_->Clear(&actual_wrapped);
+  EXPECT_TRUE(dut_->Empty());
+
+  // Get the hard pointer values for comparison.
+  std::set<CaptureRequest*> actual;
+  for (const auto& actual_request : actual_wrapped) {
+    actual.insert(actual_request.get());
+  }
+  EXPECT_EQ(actual, expected);
+}
+
+TEST_F(RequestTrackerTest, ClearRequestsNoResult) {
+  // Add some requests.
+  EXPECT_TRUE(dut_->Empty());
+  AddRequest(1, {&stream1_});
+  AddRequest(2, {&stream2_});
+  EXPECT_FALSE(dut_->Empty());
+  // Don't bother getting the cleared requests.
+  dut_->Clear();
+  EXPECT_TRUE(dut_->Empty());
+}
+
+TEST_F(RequestTrackerTest, ClearConfiguration) {
+  EXPECT_FALSE(dut_->StreamFull(&stream1_));
+  EXPECT_FALSE(dut_->StreamFull(&stream2_));
+
+  // Clear the configuration.
+  dut_->ClearStreamConfiguration();
+
+  // Both streams should be considered full now, since neither is configured.
+  EXPECT_TRUE(dut_->StreamFull(&stream1_));
+  EXPECT_TRUE(dut_->StreamFull(&stream2_));
+}
+
+}  // namespace default_camera_hal