[controller][mic] Implement Enable/DisableStreaming()

When a mute button event happens, the controller would
enable and disable all the streams depending on the state
of the mute button.

The demo was calling the HAL APIs for Enabling/Disabling
streaming which was not necessary so removed those calls.

Fixed: 44011

Test: Added test
      fx test camera_tests

Change-Id: I685b1a3301e0ec7226fa4567725e4a5d8f3e90c3
diff --git a/src/camera/drivers/controller/controller-protocol.cc b/src/camera/drivers/controller/controller-protocol.cc
index fb4147e..0108379 100644
--- a/src/camera/drivers/controller/controller-protocol.cc
+++ b/src/camera/drivers/controller/controller-protocol.cc
@@ -119,9 +119,9 @@
   cleanup.cancel();
 }
 
-void ControllerImpl::EnableStreaming() {}
+void ControllerImpl::EnableStreaming() { pipeline_manager_.StartStreaming(); }
 
-void ControllerImpl::DisableStreaming() {}
+void ControllerImpl::DisableStreaming() { pipeline_manager_.StopStreaming(); }
 
 void ControllerImpl::GetDeviceInfo(GetDeviceInfoCallback callback) {
   fuchsia::camera2::DeviceInfo camera_device_info;
diff --git a/src/camera/drivers/controller/pipeline_manager.cc b/src/camera/drivers/controller/pipeline_manager.cc
index ab1f239..41cb4ef 100644
--- a/src/camera/drivers/controller/pipeline_manager.cc
+++ b/src/camera/drivers/controller/pipeline_manager.cc
@@ -54,11 +54,14 @@
         // TODO(braval): Handle already configured nodes
         return result;
       }
+      output_nodes_info_[info->stream_config->properties.stream_type()] = result.value();
       break;
     }
+      // clang-format off
     default: {
       return fit::error(ZX_ERR_NOT_SUPPORTED);
     }
+      // clang-format on
   }
   return result;
 }
@@ -291,6 +294,7 @@
                                        fuchsia::camera2::CameraStreamType stream_to_disconnect) {
   auto shutdown_callback = [this, input_stream_type, stream_to_disconnect]() {
     ProcessNode* graph_head = nullptr;
+    output_nodes_info_.erase(stream_to_disconnect);
 
     switch (input_stream_type) {
       case fuchsia::camera2::CameraStreamType::FULL_RESOLUTION: {
@@ -315,6 +319,7 @@
       }
       default: {
         ZX_ASSERT_MSG(false, "Invalid input stream type\n");
+        break;
       }
     }
   };
@@ -354,10 +359,26 @@
     }
     default: {
       ZX_ASSERT_MSG(false, "Invalid input stream type\n");
-      return;
+      break;
     }
   }
   DisconnectStream(graph_head, input_stream_type, stream_to_disconnect);
 }
 
+void PipelineManager::StopStreaming() {
+  for (auto output_node_info : output_nodes_info_) {
+    if (output_node_info.second) {
+      output_node_info.second->client_stream()->Stop();
+    }
+  }
+}
+
+void PipelineManager::StartStreaming() {
+  for (auto output_node_info : output_nodes_info_) {
+    if (output_node_info.second) {
+      output_node_info.second->client_stream()->Start();
+    }
+  }
+}
+
 }  // namespace camera
diff --git a/src/camera/drivers/controller/pipeline_manager.h b/src/camera/drivers/controller/pipeline_manager.h
index 8e4ed4c..3cff37f 100644
--- a/src/camera/drivers/controller/pipeline_manager.h
+++ b/src/camera/drivers/controller/pipeline_manager.h
@@ -8,6 +8,7 @@
 #include <fuchsia/camera2/cpp/fidl.h>
 #include <fuchsia/camera2/hal/cpp/fidl.h>
 
+#include <map>
 #include <vector>
 
 #include "fbl/macros.h"
@@ -83,6 +84,9 @@
   ProcessNode* full_resolution_stream() { return full_resolution_stream_.get(); }
   ProcessNode* downscaled_resolution_stream() { return downscaled_resolution_stream_.get(); }
 
+  void StopStreaming();
+  void StartStreaming();
+
  private:
   fit::result<std::unique_ptr<InputNode>, zx_status_t> ConfigureStreamPipelineHelper(
       StreamCreationData* info, fidl::InterfaceRequest<fuchsia::camera2::Stream>& stream);
@@ -98,6 +102,7 @@
   std::unique_ptr<ProcessNode> full_resolution_stream_;
   std::unique_ptr<ProcessNode> downscaled_resolution_stream_;
   std::queue<async::TaskClosure> event_queue_ __TA_GUARDED(event_queue_lock_);
+  std::map<fuchsia::camera2::CameraStreamType, OutputNode*> output_nodes_info_;
 };
 
 }  // namespace camera
diff --git a/src/camera/drivers/controller/processing_node.cc b/src/camera/drivers/controller/processing_node.cc
index ad9c606..1916d59 100644
--- a/src/camera/drivers/controller/processing_node.cc
+++ b/src/camera/drivers/controller/processing_node.cc
@@ -46,7 +46,7 @@
 }
 
 void ProcessNode::OnStartStreaming() {
-  if (!enabled_) {
+  if (!shutdown_requested_ && !enabled_) {
     enabled_ = true;
     parent_node_->OnStartStreaming();
   }
@@ -62,7 +62,7 @@
 }
 
 void ProcessNode::OnStopStreaming() {
-  if (enabled_) {
+  if (!shutdown_requested_ && enabled_) {
     if (AllChildNodesDisabled()) {
       enabled_ = false;
       parent_node_->OnStopStreaming();
diff --git a/src/camera/drivers/controller/test/protocol_test.cc b/src/camera/drivers/controller/test/protocol_test.cc
index 7166dca..3ebca51 100644
--- a/src/camera/drivers/controller/test/protocol_test.cc
+++ b/src/camera/drivers/controller/test/protocol_test.cc
@@ -627,6 +627,49 @@
     EXPECT_TRUE(fake_gdc_.frame_released());
   }
 
+  void TestEnabledDisableStreaming() {
+    fuchsia::camera2::StreamPtr stream_ds;
+    fuchsia::camera2::StreamPtr stream_fr;
+
+    auto stream_type_ds = kStreamTypeDS | kStreamTypeML;
+    auto stream_type_fr = kStreamTypeFR | kStreamTypeML;
+
+    auto result_fr = SetupStream(kMonitorConfig, stream_type_fr, stream_fr);
+    ASSERT_EQ(ZX_OK, result_fr.error());
+
+    auto result_ds = SetupStream(kMonitorConfig, stream_type_ds, stream_ds);
+    ASSERT_EQ(ZX_OK, result_ds.error());
+
+    // Start streaming.
+    stream_fr->Start();
+    stream_ds->Start();
+    RunLoopUntilIdle();
+
+    auto fr_head_node = pipeline_manager_->full_resolution_stream();
+    auto fr_ml_output_node = static_cast<OutputNode*>(fr_head_node->child_nodes().at(0).get());
+    auto gdc_node = static_cast<GdcNode*>(fr_head_node->child_nodes().at(1).get());
+    auto ds_ml_output_node = static_cast<OutputNode*>(gdc_node->child_nodes().at(0).get());
+
+    EXPECT_TRUE(fr_head_node->enabled());
+    EXPECT_TRUE(fr_ml_output_node->enabled());
+    EXPECT_TRUE(gdc_node->enabled());
+    EXPECT_TRUE(ds_ml_output_node->enabled());
+
+    pipeline_manager_->StopStreaming();
+
+    EXPECT_FALSE(fr_head_node->enabled());
+    EXPECT_FALSE(fr_ml_output_node->enabled());
+    EXPECT_FALSE(gdc_node->enabled());
+    EXPECT_FALSE(ds_ml_output_node->enabled());
+
+    pipeline_manager_->StartStreaming();
+
+    EXPECT_TRUE(fr_head_node->enabled());
+    EXPECT_TRUE(fr_ml_output_node->enabled());
+    EXPECT_TRUE(gdc_node->enabled());
+    EXPECT_TRUE(ds_ml_output_node->enabled());
+  }
+
   FakeIsp fake_isp_;
   FakeGdc fake_gdc_;
   async::Loop loop_;
@@ -683,6 +726,8 @@
 
 TEST_F(ControllerProtocolTest, TestReleaseAfterStopStreaming) { TestReleaseAfterStopStreaming(); }
 
+TEST_F(ControllerProtocolTest, TestEnabledDisableStreaming) { TestEnabledDisableStreaming(); }
+
 TEST_F(ControllerProtocolTest, LoadGdcConfig) {
 #ifdef INTERNAL_ACCESS
   TestGdcConfigLoading();
diff --git a/src/camera/examples/camera_display/stream_provider/controller_stream_provider.cc b/src/camera/examples/camera_display/stream_provider/controller_stream_provider.cc
index 87c909e..789bc3f 100644
--- a/src/camera/examples/camera_display/stream_provider/controller_stream_provider.cc
+++ b/src/camera/examples/camera_display/stream_provider/controller_stream_provider.cc
@@ -23,12 +23,6 @@
 static constexpr uint32_t kMonitoringStream = 2;
 
 ControllerStreamProvider::~ControllerStreamProvider() {
-  if (controller_ && streaming_) {
-    zx_status_t status = controller_->DisableStreaming();
-    if (status != ZX_OK) {
-      FX_PLOGS(WARNING, status) << "Failed to stop streaming via the controller";
-    }
-  }
   for (auto& buffer_collection : buffer_collections_) {
     if (buffer_collection.second) {
       zx_status_t status = buffer_collection.second->Close();
@@ -89,13 +83,6 @@
     return nullptr;
   }
 
-  // Immediately enable streaming.
-  status = provider->controller_->EnableStreaming();
-  if (status != ZX_OK) {
-    FX_LOGS(WARNING) << "Failed to start streaming via the controller";
-  }
-  provider->streaming_ = true;
-
   return std::move(provider);
 }
 
diff --git a/src/camera/examples/camera_display/stream_provider/controller_stream_provider.h b/src/camera/examples/camera_display/stream_provider/controller_stream_provider.h
index a11a5b7..011364d 100644
--- a/src/camera/examples/camera_display/stream_provider/controller_stream_provider.h
+++ b/src/camera/examples/camera_display/stream_provider/controller_stream_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_CONTROLLER_STREAM_PROVIDER_H_
-#define SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_CONTROLLER_STREAM_PROVIDER_H_
+#ifndef SRC_CAMERA_EXAMPLES_CAMERA_DISPLAY_STREAM_PROVIDER_CONTROLLER_STREAM_PROVIDER_H_
+#define SRC_CAMERA_EXAMPLES_CAMERA_DISPLAY_STREAM_PROVIDER_CONTROLLER_STREAM_PROVIDER_H_
 
 #include <fuchsia/camera2/hal/cpp/fidl.h>
 #include <lib/sys/cpp/component_context.h>
@@ -24,11 +24,10 @@
   virtual std::string GetName() override { return "fuchsia.camera2.Controller service"; }
 
  private:
-  bool streaming_ = false;
   fuchsia::camera2::hal::ControllerSyncPtr controller_;
   fuchsia::sysmem::AllocatorSyncPtr allocator_;
   std::map<uint32_t, fuchsia::sysmem::BufferCollectionSyncPtr> buffer_collections_;
   fidl::VectorPtr<fuchsia::camera2::hal::Config> configs_;
 };
 
-#endif  // SRC_CAMERA_EXAMPLES_DEMO_STREAM_PROVIDER_CONTROLLER_STREAM_PROVIDER_H_
+#endif  // SRC_CAMERA_EXAMPLES_CAMERA_DISPLAY_STREAM_PROVIDER_CONTROLLER_STREAM_PROVIDER_H_