Prevent duplicate buffer indices

Track which buffer indices are in use to prevent duplicates.

BUG: 31831808
TEST: Test app runs, enqueue errors no longer showing up in logs.
Change-Id: I319f9a1906170c861e194e357bdb57fab81d8c34
diff --git a/modules/camera/3_4/v4l2_wrapper.cpp b/modules/camera/3_4/v4l2_wrapper.cpp
index 40db4f6..a0c5f04 100644
--- a/modules/camera/3_4/v4l2_wrapper.cpp
+++ b/modules/camera/3_4/v4l2_wrapper.cpp
@@ -56,7 +56,6 @@
                          std::unique_ptr<V4L2Gralloc> gralloc)
     : device_path_(std::move(device_path)),
       gralloc_(std::move(gralloc)),
-      max_buffers_(0),
       connection_count_(0) {
   HAL_LOG_ENTER();
 }
@@ -117,7 +116,7 @@
 
   device_fd_.reset();  // Includes close().
   format_.reset();
-  max_buffers_ = 0;
+  buffers_.clear();
   // Closing the device releases all queued buffers back to the user.
   gralloc_->unlockAllBuffers();
 }
@@ -493,7 +492,7 @@
     HAL_LOGE("Failed to set up buffers for new format.");
     return res;
   }
-  *result_max_buffers = max_buffers_;
+  *result_max_buffers = buffers_.size();
   return 0;
 }
 
@@ -526,12 +525,12 @@
   }
 
   // V4L2 will set req_buffers.count to a number of buffers it can handle.
-  max_buffers_ = req_buffers.count;
-  // Sanity check.
-  if (max_buffers_ < 1) {
+  if (req_buffers.count < 1) {
     HAL_LOGE("REQBUFS claims it can't handle any buffers.");
     return -ENODEV;
   }
+  buffers_.resize(req_buffers.count, false);
+
   return 0;
 }
 
@@ -543,14 +542,31 @@
     return -ENODEV;
   }
 
+  // Find a free buffer index. Could use some sort of persistent hinting
+  // here to improve expected efficiency, but buffers_.size() is expected
+  // to be low enough (<10 experimentally) that it's not worth it.
+  int index = -1;
+  {
+    std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+    for (int i = 0; i < buffers_.size(); ++i) {
+      if (!buffers_[i]) {
+        index = i;
+        break;
+      }
+    }
+  }
+  if (index < 0) {
+    // Note: The HAL should be tracking the number of buffers in flight
+    // for each stream, and should never overflow the device.
+    HAL_LOGE("Cannot enqueue buffer: stream is already full.");
+    return -ENODEV;
+  }
+
   // Set up a v4l2 buffer struct.
   v4l2_buffer device_buffer;
   memset(&device_buffer, 0, sizeof(device_buffer));
   device_buffer.type = format_->type();
-  // TODO(b/29334616): when this is async, actually limit the number
-  // of buffers used to the known max, and set this according to the
-  // queue length.
-  device_buffer.index = 0;
+  device_buffer.index = index;
 
   // Use QUERYBUF to ensure our buffer/device is in good shape,
   // and fill out remaining fields.
@@ -572,6 +588,10 @@
     return -ENODEV;
   }
 
+  // Mark the buffer as in flight.
+  std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+  buffers_[index] = true;
+
   return 0;
 }
 
@@ -591,6 +611,12 @@
     return -ENODEV;
   }
 
+  // Mark the buffer as no longer in flight.
+  {
+    std::lock_guard<std::mutex> guard(buffer_queue_lock_);
+    buffers_[buffer->index] = false;
+  }
+
   // Now that we're done painting the buffer, we can unlock it.
   int res = gralloc_->unlock(buffer);
   if (res) {