[camera] Add low-frequency client state logging

This change adds INFO-level logging for most low-frequency client
methods on the Device and Stream protocol. It also adds a util template
extension for fsl::Get[Related]Koid that accepts any of the fidl library
types that derive from zx::channels.

Bug: b/223483476
Test: ran product scenarios and observed expected log messages
Change-Id: I77bf7633e7f3306b927e64484f6c06c4a85d73e9
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/657593
Reviewed-by: Marcus Jang <marcusjang@google.com>
Reviewed-by: Ernest Hua <ernesthua@google.com>
Commit-Queue: Matt Sandy <msandy@google.com>
diff --git a/src/camera/bin/device/device_impl.cc b/src/camera/bin/device/device_impl.cc
index c3e5223..f83945ec 100644
--- a/src/camera/bin/device/device_impl.cc
+++ b/src/camera/bin/device/device_impl.cc
@@ -233,10 +233,19 @@
 
 void DeviceImpl::UpdateControllerStreamingState() {
   if (mute_state_.muted() && controller_streaming_) {
+    std::string detail = "hardware + software";
+    if (!mute_state_.hardware_muted) {
+      detail = "software";
+    }
+    if (!mute_state_.software_muted) {
+      detail = "hardware";
+    }
+    FX_LOGS(INFO) << "disabling streaming because device is muted (" << detail << ")";
     controller_->DisableStreaming();
     controller_streaming_ = false;
   }
   if (!mute_state_.muted() && !controller_streaming_) {
+    FX_LOGS(INFO) << "enabling streaming because device is unmuted";
     controller_->EnableStreaming();
     controller_streaming_ = true;
   }
@@ -356,6 +365,10 @@
             buffers.buffer_count - configs_[current_configuration_index_]
                                        .stream_configs[index]
                                        .constraints.min_buffer_count_for_camping;
+        FX_LOGS(INFO) << "c" << current_configuration_index_ << "s" << index
+                      << ": collection resolved: at most " << max_camping_buffers << " of "
+                      << buffers.buffer_count
+                      << " buffers will be made available to clients concurrently";
         max_camping_buffers_callback(max_camping_buffers);
       };
 
diff --git a/src/camera/bin/device/device_impl.h b/src/camera/bin/device/device_impl.h
index caebff6..0082126 100644
--- a/src/camera/bin/device/device_impl.h
+++ b/src/camera/bin/device/device_impl.h
@@ -128,6 +128,7 @@
     void Rebind(fidl::InterfaceRequest<fuchsia::camera3::Device> request) override;
 
     DeviceImpl& device_;
+    std::string log_prefix_;
     uint64_t id_;
     fidl::Binding<fuchsia::camera3::Device> binding_;
     HangingGetHelper<uint32_t> configuration_;
diff --git a/src/camera/bin/device/device_impl_client.cc b/src/camera/bin/device/device_impl_client.cc
index 4fc3e27..3a571a5 100644
--- a/src/camera/bin/device/device_impl_client.cc
+++ b/src/camera/bin/device/device_impl_client.cc
@@ -15,7 +15,8 @@
 DeviceImpl::Client::Client(DeviceImpl& device, uint64_t id,
                            fidl::InterfaceRequest<fuchsia::camera3::Device> request)
     : device_(device), id_(id), binding_(this, std::move(request)) {
-  FX_LOGS(INFO) << "Device client " << id << " connected.";
+  log_prefix_ = "device client (koid = " + std::to_string(GetRelatedKoid(binding_)) + "): ";
+  FX_LOGS(INFO) << log_prefix_ << "new device client, id = " << id_;
   binding_.set_error_handler(fit::bind_member(this, &DeviceImpl::Client::OnClientDisconnected));
 }
 
@@ -26,7 +27,7 @@
 void DeviceImpl::Client::MuteUpdated(MuteState mute_state) { mute_state_.Set(mute_state); }
 
 void DeviceImpl::Client::OnClientDisconnected(zx_status_t status) {
-  FX_PLOGS(INFO, status) << "Device client " << id_ << " disconnected.";
+  FX_PLOGS(INFO, status) << log_prefix_ << "closed connection";
   device_.RemoveClient(id_);
 }
 
@@ -36,6 +37,7 @@
 }
 
 void DeviceImpl::Client::GetIdentifier(GetIdentifierCallback callback) {
+  FX_LOGS(INFO) << log_prefix_ << "called GetIdentifier()";
   if (!device_.device_info_.has_vendor_id() || !device_.device_info_.has_product_id()) {
     callback(cpp17::nullopt);
     return;
@@ -49,6 +51,7 @@
 }
 
 void DeviceImpl::Client::GetConfigurations(GetConfigurationsCallback callback) {
+  FX_LOGS(INFO) << log_prefix_ << "called GetConfigurations()";
   std::vector<fuchsia::camera3::Configuration> configurations;
   for (const auto& configuration : device_.configurations_) {
     std::vector<fuchsia::camera3::StreamProperties> streams;
@@ -61,16 +64,19 @@
 }
 
 void DeviceImpl::Client::GetConfigurations2(GetConfigurations2Callback callback) {
+  FX_LOGS(INFO) << log_prefix_ << "called GetConfigurations2()";
   callback(fidl::Clone(device_.configurations_));
 }
 
 void DeviceImpl::Client::WatchCurrentConfiguration(WatchCurrentConfigurationCallback callback) {
+  FX_LOGS(INFO) << log_prefix_ << "called WatchCurrentConfiguration()";
   if (configuration_.Get(std::move(callback))) {
     CloseConnection(ZX_ERR_BAD_STATE);
   }
 }
 
 void DeviceImpl::Client::SetCurrentConfiguration(uint32_t index) {
+  FX_LOGS(INFO) << log_prefix_ << "called SetCurrentConfiguration(" << index << ")";
   if (index < 0 || index >= device_.configurations_.size()) {
     CloseConnection(ZX_ERR_OUT_OF_RANGE);
     return;
@@ -80,6 +86,7 @@
 }
 
 void DeviceImpl::Client::WatchMuteState(WatchMuteStateCallback callback) {
+  FX_LOGS(INFO) << log_prefix_ << "called WatchMuteState()";
   if (mute_state_.Get([callback = std::move(callback)](MuteState mute_state) {
         callback(mute_state.software_muted, mute_state.hardware_muted);
       })) {
@@ -88,6 +95,7 @@
 }
 
 void DeviceImpl::Client::SetSoftwareMuteState(bool muted, SetSoftwareMuteStateCallback callback) {
+  FX_LOGS(INFO) << log_prefix_ << "called SetSoftwareMuteState(" << muted << ")";
   if (!muted) {
     callback();
     callback = [] {};
@@ -97,10 +105,12 @@
 
 void DeviceImpl::Client::ConnectToStream(uint32_t index,
                                          fidl::InterfaceRequest<fuchsia::camera3::Stream> request) {
+  FX_LOGS(INFO) << log_prefix_ << "called ConnectToStream(" << index << ")";
   device_.ConnectToStream(index, std::move(request));
 }
 
 void DeviceImpl::Client::Rebind(fidl::InterfaceRequest<fuchsia::camera3::Device> request) {
+  FX_LOGS(INFO) << log_prefix_ << "called Rebind(koid = " << GetRelatedKoid(request) << ")";
   device_.Bind(std::move(request));
 }
 
diff --git a/src/camera/bin/device/stream_impl.h b/src/camera/bin/device/stream_impl.h
index d0f9d886..3422521 100644
--- a/src/camera/bin/device/stream_impl.h
+++ b/src/camera/bin/device/stream_impl.h
@@ -129,6 +129,20 @@
     void Rebind(fidl::InterfaceRequest<Stream> request) override;
 
     StreamImpl& stream_;
+    std::string log_prefix_;
+    // Tracking for whether a message has already been logged for the named stage of client
+    // progress. This is used to ensure that only one such message is logged per transition per
+    // client, as they are high-frequency events that would otherwise spam syslog.
+    struct {
+      // The client has called camera3::Stream::GetNextFrame.
+      bool requested = false;
+      // The parent stream has added a frame to this client's available frame queue by calling
+      // AddFrame.
+      bool available = false;
+      // A frame has been sent to the client by invoking the callback provided in a previous call to
+      // GetNextFrame.
+      bool sent = false;
+    } frame_logging_state_;
     uint64_t id_;
     fidl::Binding<fuchsia::camera3::Stream> binding_;
     HangingGetHelper<fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>> buffers_;
diff --git a/src/camera/bin/device/stream_impl_client.cc b/src/camera/bin/device/stream_impl_client.cc
index 214fc9b..6b0945e 100644
--- a/src/camera/bin/device/stream_impl_client.cc
+++ b/src/camera/bin/device/stream_impl_client.cc
@@ -19,7 +19,9 @@
 StreamImpl::Client::Client(StreamImpl& stream, uint64_t id,
                            fidl::InterfaceRequest<fuchsia::camera3::Stream> request)
     : stream_(stream), id_(id), binding_(this, std::move(request)), resolution_(SizeEqual) {
-  FX_LOGS(INFO) << stream_.description_ << ": Stream client " << id << " connected.";
+  log_prefix_ = "stream " + stream_.description_ +
+                " client (koid = " + std::to_string(GetRelatedKoid(binding_)) + "): ";
+  FX_LOGS(INFO) << log_prefix_ << "new stream client, id = " << id_;
   binding_.set_error_handler(fit::bind_member(this, &StreamImpl::Client::OnClientDisconnected));
 }
 
@@ -28,6 +30,10 @@
 void StreamImpl::Client::AddFrame(fuchsia::camera3::FrameInfo2 frame) {
   TRACE_DURATION("camera", "StreamImpl::Client::AddFrame");
   frames_.push(std::move(frame));
+  if (!frame_logging_state_.available) {
+    FX_LOGS(INFO) << log_prefix_ << "frame available";
+    frame_logging_state_.available = true;
+  }
   MaybeSendFrame();
 }
 
@@ -41,6 +47,12 @@
   // into the client.
   TRACE_FLOW_BEGIN("camera", "camera3::Stream::GetNextFrame",
                    fsl::GetKoid(frame.release_fence().get()));
+  // Note: this message is logged immediately prior to invoking the frame callback to ensure that no
+  // client logs emitted as a result will have earlier timestamps than this message.
+  if (!frame_logging_state_.sent) {
+    FX_LOGS(INFO) << log_prefix_ << "sent frame";
+    frame_logging_state_.sent = true;
+  }
   frame_callback_(std::move(frame));
   frames_.pop();
   frame_callback_ = nullptr;
@@ -74,7 +86,7 @@
 }
 
 void StreamImpl::Client::OnClientDisconnected(zx_status_t status) {
-  FX_PLOGS(INFO, status) << stream_.description_ << ": Stream client " << id_ << " disconnected.";
+  FX_PLOGS(INFO, status) << log_prefix_ << "closed connection";
   stream_.RemoveClient(id_);
 }
 
@@ -85,11 +97,13 @@
 
 void StreamImpl::Client::GetProperties(GetPropertiesCallback callback) {
   TRACE_DURATION("camera", "StreamImpl::Client::GetProperties");
+  FX_LOGS(INFO) << log_prefix_ << "called GetProperties()";
   callback(Convert(stream_.properties_));
 }
 
 void StreamImpl::Client::GetProperties2(GetProperties2Callback callback) {
   TRACE_DURATION("camera", "StreamImpl::Client::GetProperties2");
+  FX_LOGS(INFO) << log_prefix_ << "called GetProperties2()";
   callback(fidl::Clone(stream_.properties_));
 }
 
@@ -134,11 +148,14 @@
 void StreamImpl::Client::SetBufferCollection(
     fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
   TRACE_DURATION("camera", "StreamImpl::Client::SetBufferCollection");
+  FX_LOGS(INFO) << log_prefix_ << "called SetBufferCollection(koid = " << GetRelatedKoid(token)
+                << ")";
   stream_.SetBufferCollection(id_, std::move(token));
 }
 
 void StreamImpl::Client::WatchBufferCollection(WatchBufferCollectionCallback callback) {
   TRACE_DURATION("camera", "StreamImpl::Client::WatchBufferCollection");
+  FX_LOGS(INFO) << log_prefix_ << "called WatchBufferCollection()";
   if (buffers_.Get(std::move(callback))) {
     CloseConnection(ZX_ERR_BAD_STATE);
   }
@@ -177,6 +194,10 @@
 
 void StreamImpl::Client::GetNextFrame2(GetNextFrame2Callback callback) {
   TRACE_DURATION("camera", "StreamImpl::Client::GetNextFrame2");
+  if (!frame_logging_state_.requested) {
+    FX_LOGS(INFO) << log_prefix_ << "called GetNextFrame2()";
+    frame_logging_state_.requested = true;
+  }
   if (frame_callback_) {
     FX_LOGS(INFO) << stream_.description_ << ": " << id_
                   << ": Client called GetNextFrame while a previous call was still pending.";
@@ -188,6 +209,7 @@
 }
 
 void StreamImpl::Client::Rebind(fidl::InterfaceRequest<Stream> request) {
+  FX_LOGS(INFO) << log_prefix_ << "called Rebind(koid = " << GetRelatedKoid(request) << ")";
   stream_.OnNewRequest(std::move(request));
 }
 
diff --git a/src/camera/bin/device/util.h b/src/camera/bin/device/util.h
index 5a149a0..386a9dfe 100644
--- a/src/camera/bin/device/util.h
+++ b/src/camera/bin/device/util.h
@@ -10,12 +10,19 @@
 #include <lib/async/cpp/task.h>
 #include <lib/async/cpp/wait.h>
 #include <lib/async/default.h>
+#include <lib/fidl/cpp/binding.h>
 #include <lib/fidl/cpp/interface_ptr.h>
+#include <lib/fpromise/promise.h>
 #include <lib/fpromise/result.h>
 #include <lib/syslog/cpp/macros.h>
+#include <lib/trace/event.h>
 #include <zircon/status.h>
 #include <zircon/types.h>
 
+#include "src/lib/fsl/handles/object_info.h"
+
+namespace camera {
+
 // Safely unbinds a client connection, doing so on the connection's thread if it differs from the
 // caller's thread.
 template <class T>
@@ -127,4 +134,48 @@
   fit::closure signaled_;
 };
 
+template <typename T, typename Enable = void>
+struct IsFidlChannelWrapper : std::false_type {};
+
+template <typename T>
+struct IsFidlChannelWrapper<fidl::InterfaceHandle<T>> : std::true_type {};
+
+template <typename T>
+struct IsFidlChannelWrapper<fidl::InterfacePtr<T>> : std::true_type {};
+
+template <typename T>
+struct IsFidlChannelWrapper<fidl::SynchronousInterfacePtr<T>> : std::true_type {};
+
+template <typename T>
+struct IsFidlChannelWrapper<fidl::InterfaceRequest<T>> : std::true_type {};
+
+template <typename T>
+struct IsFidlChannelWrapper<fidl::Binding<T>> : std::true_type {};
+
+template <typename T>
+inline constexpr bool IsFidlChannelWrapperV = IsFidlChannelWrapper<T>::value;
+
+template <typename T>
+inline zx_handle_t GetFidlChannelHandle(const T& fidl) {
+  static_assert(IsFidlChannelWrapperV<T>, "'fidl' must be one one of the fidl channel wrappers");
+  return fidl.channel().get();
+}
+
+template <typename T>
+inline zx_koid_t GetKoid(const T& fidl) {
+  return fsl::GetKoid(GetFidlChannelHandle(fidl));
+}
+
+template <typename T>
+inline zx_koid_t GetRelatedKoid(const T& fidl) {
+  return fsl::GetRelatedKoid(GetFidlChannelHandle(fidl));
+}
+
+template <typename T>
+inline std::pair<zx_koid_t, zx_koid_t> GetKoids(const T& fidl) {
+  return fsl::GetKoids(GetFidlChannelHandle(fidl));
+}
+
+}  // namespace camera
+
 #endif  // SRC_CAMERA_BIN_DEVICE_UTIL_H_