Add the ability to send triggers to the Perfetto Service.

This enables Perfetto to receive Triggers from connected producers.
Extending the API to let producers start a pending Trace. Currently
only supports START_TRACING mode.

Bug: 128966650
Change-Id: I4cfd2c64f46e54ec76a3200ab304e817d456ee0f
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index 857efa6..cb52f79 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -59,6 +59,28 @@
 namespace {
 constexpr size_t kDefaultShmSizeKb = TracingServiceImpl::kDefaultShmSize / 1024;
 constexpr size_t kMaxShmSizeKb = TracingServiceImpl::kMaxShmSize / 1024;
+
+::testing::AssertionResult HasTriggerModeInternal(
+    const std::vector<protos::TracePacket>& packets,
+    protos::TraceConfig::TriggerConfig::TriggerMode mode) {
+  ::testing::StringMatchResultListener matcher_result_string;
+  bool contains = ::testing::ExplainMatchResult(
+      Contains(Property(
+          &protos::TracePacket::trace_config,
+          Property(&protos::TraceConfig::trigger_config,
+                   Property(&protos::TraceConfig::TriggerConfig::trigger_mode,
+                            Eq(mode))))),
+      packets, &matcher_result_string);
+  if (contains) {
+    return ::testing::AssertionSuccess();
+  }
+  return ::testing::AssertionFailure() << matcher_result_string.str();
+}
+
+MATCHER_P(HasTriggerMode, mode, "") {
+  return HasTriggerModeInternal(arg, mode);
+}
+
 }  // namespace
 
 class TracingServiceImplTest : public testing::Test {
@@ -91,12 +113,20 @@
     return svc->GetProducer(producer_id)->uid_;
   }
 
-  TracingServiceImpl::TracingSession* tracing_session() {
-    auto* session = svc->GetTracingSession(svc->last_tracing_session_id_);
+  TracingServiceImpl::TracingSession* GetTracingSession(TracingSessionID tsid) {
+    auto* session = svc->GetTracingSession(tsid);
     EXPECT_NE(nullptr, session);
     return session;
   }
 
+  TracingServiceImpl::TracingSession* tracing_session() {
+    return GetTracingSession(GetTracingSessionID());
+  }
+
+  TracingSessionID GetTracingSessionID() {
+    return svc->last_tracing_session_id_;
+  }
+
   const std::set<BufferID>& GetAllowedTargetBuffers(ProducerID producer_id) {
     return svc->GetProducer(producer_id)->allowed_target_buffers_;
   }
@@ -212,6 +242,432 @@
   consumer->WaitForTracingDisabled();
 }
 
+// Creates a tracing session with a START_TRACING trigger and checks that data
+// sources are started only after the service receives a trigger.
+TEST_F(TracingServiceImplTest, StartTracingTriggerDeferredStart) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but enable only one of them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(1);
+
+  trigger_config->set_trigger_timeout_ms(8.64e+7);
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet.
+  EXPECT_CALL(*producer, StartDataSource(_, _)).Times(0);
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // The trace won't start until we send the trigger. since we have a
+  // START_TRACING trigger defined.
+  std::vector<std::string> req;
+  req.push_back("trigger_name");
+  producer->endpoint()->ActivateTriggers(req);
+
+  producer->WaitForDataSourceStart("ds_1");
+
+  auto writer1 = producer->CreateTraceWriter("ds_1");
+  producer->WaitForFlush(writer1.get());
+
+  producer->WaitForDataSourceStop("ds_1");
+  consumer->WaitForTracingDisabled();
+
+  ASSERT_EQ(1u, tracing_session()->received_triggers.size());
+  EXPECT_EQ("trigger_name",
+            tracing_session()->received_triggers[0].second.name());
+
+  EXPECT_THAT(
+      consumer->ReadBuffers(),
+      HasTriggerMode(protos::TraceConfig::TriggerConfig::START_TRACING));
+}
+
+// Creates a tracing session with a START_TRACING trigger and checks that the
+// session is cleaned up when no trigger is received after |trigger_timeout_ms|.
+TEST_F(TracingServiceImplTest, StartTracingTriggerTimeOut) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but enable only one of them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(8.64e+7);
+
+  trigger_config->set_trigger_timeout_ms(1);
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet.
+  EXPECT_CALL(*producer, StartDataSource(_, _)).Times(0);
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // The trace won't start until we send the trigger. since we have a
+  // START_TRACING trigger defined. This is where we'd expect to have an
+  // ActivateTriggers call to the producer->endpoint().
+
+  producer->WaitForDataSourceStop("ds_1");
+  consumer->WaitForTracingDisabled();
+  EXPECT_THAT(consumer->ReadBuffers(), ::testing::IsEmpty());
+}
+
+// Creates a tracing session with a START_TRACING trigger and checks that
+// the session is not started when the configured trigger producer is different
+// than the producer that sent the trigger.
+TEST_F(TracingServiceImplTest, StartTracingTriggerDifferentProducer) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but enable only one of them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(8.64e+7);
+  trigger->set_producer_name_regex("correct_name");
+
+  trigger_config->set_trigger_timeout_ms(1);
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet.
+  EXPECT_CALL(*producer, StartDataSource(_, _)).Times(0);
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // The trace won't start until we send the trigger called "trigger_name"
+  // coming from a producer called "correct_name", since we have a
+  // START_TRACING trigger defined. This is where we'd expect to have an
+  // ActivateTriggers call to the producer->endpoint(), but we send the trigger
+  // from a different producer so it is ignored.
+  std::vector<std::string> req;
+  req.push_back("trigger_name");
+  producer->endpoint()->ActivateTriggers(req);
+
+  producer->WaitForDataSourceStop("ds_1");
+  consumer->WaitForTracingDisabled();
+  EXPECT_THAT(consumer->ReadBuffers(), ::testing::IsEmpty());
+}
+
+// Creates a tracing session with a START_TRACING trigger and checks that the
+// session is started when the trigger is received from the correct producer.
+TEST_F(TracingServiceImplTest, StartTracingTriggerCorrectProducer) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but enable only one of them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(1);
+  trigger->set_producer_name_regex("mock_produc[e-r]+");
+
+  trigger_config->set_trigger_timeout_ms(8.64e+7);
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // Start the trace at this point with ActivateTriggers.
+  std::vector<std::string> req;
+  req.push_back("trigger_name");
+  producer->endpoint()->ActivateTriggers(req);
+
+  producer->WaitForDataSourceStart("ds_1");
+
+  auto writer = producer->CreateTraceWriter("ds_1");
+  producer->WaitForFlush(writer.get());
+
+  producer->WaitForDataSourceStop("ds_1");
+  consumer->WaitForTracingDisabled();
+  EXPECT_THAT(
+      consumer->ReadBuffers(),
+      HasTriggerMode(protos::TraceConfig::TriggerConfig::START_TRACING));
+}
+
+// Creates a tracing session with a START_TRACING trigger and checks that the
+// session is cleaned up even when a different trigger is received.
+TEST_F(TracingServiceImplTest, StartTracingTriggerDifferentTrigger) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but enable only one of them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(8.64e+7);
+
+  trigger_config->set_trigger_timeout_ms(1);
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet.
+  EXPECT_CALL(*producer, StartDataSource(_, _)).Times(0);
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // The trace won't start until we send the trigger called "trigger_name",
+  // since we have a START_TRACING trigger defined. This is where we'd expect to
+  // have an ActivateTriggers call to the producer->endpoint(), but we send a
+  // different trigger.
+  std::vector<std::string> req;
+  req.push_back("not_correct_trigger");
+  producer->endpoint()->ActivateTriggers(req);
+
+  producer->WaitForDataSourceStop("ds_1");
+  consumer->WaitForTracingDisabled();
+  EXPECT_THAT(consumer->ReadBuffers(), ::testing::IsEmpty());
+}
+
+// Creates a tracing session with a START_TRACING trigger and checks that any
+// trigger can start the TracingSession.
+TEST_F(TracingServiceImplTest, StartTracingTriggerMultipleTriggers) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but enable only one of them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(1);
+
+  trigger_config->set_trigger_timeout_ms(8.64e+7);
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet.
+  task_runner.RunUntilIdle();
+
+  std::vector<std::string> req;
+  req.push_back("not_correct_trigger");
+  req.push_back("trigger_name");
+  producer->endpoint()->ActivateTriggers(req);
+
+  producer->WaitForDataSourceStart("ds_1");
+
+  auto writer = producer->CreateTraceWriter("ds_1");
+  producer->WaitForFlush(writer.get());
+
+  producer->WaitForDataSourceStop("ds_1");
+  consumer->WaitForTracingDisabled();
+  EXPECT_THAT(
+      consumer->ReadBuffers(),
+      HasTriggerMode(protos::TraceConfig::TriggerConfig::START_TRACING));
+}
+
+// Creates two tracing sessions with a START_TRACING trigger and checks that
+// both are able to be triggered simultaneously.
+TEST_F(TracingServiceImplTest, StartTracingTriggerMultipleTraces) {
+  std::unique_ptr<MockConsumer> consumer_1 = CreateMockConsumer();
+  consumer_1->Connect(svc.get());
+  std::unique_ptr<MockConsumer> consumer_2 = CreateMockConsumer();
+  consumer_2->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources but each TracingSession will only enable one of
+  // them.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(128);
+  trace_config.add_data_sources()->mutable_config()->set_name("ds_1");
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::START_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(1);
+
+  trigger_config->set_trigger_timeout_ms(8.64e+7);
+
+  consumer_1->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet from
+  // consumer_1.
+  task_runner.RunUntilIdle();
+  auto tracing_session_1_id = GetTracingSessionID();
+
+  (*trace_config.mutable_data_sources())[0].mutable_config()->set_name("ds_2");
+  trigger = trace_config.mutable_trigger_config()->add_triggers();
+  trigger->set_name("trigger_name_2");
+  trigger->set_stop_delay_ms(8.64e+7);
+
+  consumer_2->EnableTracing(trace_config);
+
+  producer->WaitForDataSourceSetup("ds_2");
+
+  // Make sure we don't get unexpected DataSourceStart() notifications yet from
+  // consumer_2.
+  task_runner.RunUntilIdle();
+  auto tracing_session_2_id = GetTracingSessionID();
+  EXPECT_NE(tracing_session_1_id, tracing_session_2_id);
+
+  const DataSourceInstanceID id1 = producer->GetDataSourceInstanceId("ds_1");
+  const DataSourceInstanceID id2 = producer->GetDataSourceInstanceId("ds_2");
+
+  std::vector<std::string> req;
+  req.push_back("not_correct_trigger");
+  req.push_back("trigger_name");
+  req.push_back("trigger_name_2");
+  producer->endpoint()->ActivateTriggers(req);
+
+  // The order has to be the same as the triggers or else we're incorrectly wait
+  // on the wrong checkpoint in the |task_runner|.
+  producer->WaitForDataSourceStart("ds_1");
+  producer->WaitForDataSourceStart("ds_2");
+
+  // Now that they've started we can check the triggers they've seen.
+  auto* tracing_session_1 = GetTracingSession(tracing_session_1_id);
+  ASSERT_EQ(1u, tracing_session_1->received_triggers.size());
+  EXPECT_EQ("trigger_name",
+            tracing_session_1->received_triggers[0].second.name());
+
+  // This is actually dependent on the order in which the triggers were received
+  // but there isn't really a better way than iteration order so probably not to
+  // brittle of a test. And this caught a real bug in implementation.
+  auto* tracing_session_2 = GetTracingSession(tracing_session_2_id);
+  ASSERT_EQ(2u, tracing_session_2->received_triggers.size());
+
+  EXPECT_EQ("trigger_name",
+            tracing_session_2->received_triggers[0].second.name());
+  EXPECT_EQ(1, tracing_session_2->received_triggers[0].second.stop_delay_ms());
+
+  EXPECT_EQ("trigger_name_2",
+            tracing_session_2->received_triggers[1].second.name());
+  EXPECT_EQ(8.64e+7,
+            tracing_session_2->received_triggers[1].second.stop_delay_ms());
+
+  auto writer1 = producer->CreateTraceWriter("ds_1");
+  auto writer2 = producer->CreateTraceWriter("ds_2");
+
+  // We can't use the standard WaitForX in the MockProducer and MockConsumer
+  // because they assume only a single trace is going on. So we perform our own
+  // expectations and wait at the end for the two consumers to receive
+  // OnTracingDisabled.
+  bool flushed_writer_1 = false;
+  bool flushed_writer_2 = false;
+  auto flush_correct_writer = [&](FlushRequestID flush_req_id,
+                                  const DataSourceInstanceID* id, size_t) {
+    if (*id == id1) {
+      flushed_writer_1 = true;
+      writer1->Flush();
+      producer->endpoint()->NotifyFlushComplete(flush_req_id);
+    } else if (*id == id2) {
+      flushed_writer_2 = true;
+      writer2->Flush();
+      producer->endpoint()->NotifyFlushComplete(flush_req_id);
+    }
+  };
+  EXPECT_CALL(*producer, Flush(_, _, _))
+      .WillOnce(Invoke(flush_correct_writer))
+      .WillOnce(Invoke(flush_correct_writer));
+
+  auto checkpoint_name = "on_tracing_disabled_consumer_1_and_2";
+  auto on_tracing_disabled = task_runner.CreateCheckpoint(checkpoint_name);
+  std::atomic<size_t> counter(0);
+  EXPECT_CALL(*consumer_1, OnTracingDisabled()).WillOnce(Invoke([&]() {
+    if (++counter == 2u) {
+      on_tracing_disabled();
+    }
+  }));
+  EXPECT_CALL(*consumer_2, OnTracingDisabled()).WillOnce(Invoke([&]() {
+    if (++counter == 2u) {
+      on_tracing_disabled();
+    }
+  }));
+
+  EXPECT_CALL(*producer, StopDataSource(id1));
+  EXPECT_CALL(*producer, StopDataSource(id2));
+
+  task_runner.RunUntilCheckpoint(checkpoint_name, 1000);
+
+  EXPECT_TRUE(flushed_writer_1);
+  EXPECT_TRUE(flushed_writer_2);
+  EXPECT_THAT(
+      consumer_1->ReadBuffers(),
+      HasTriggerMode(protos::TraceConfig::TriggerConfig::START_TRACING));
+  EXPECT_THAT(
+      consumer_2->ReadBuffers(),
+      HasTriggerMode(protos::TraceConfig::TriggerConfig::START_TRACING));
+}
+
 TEST_F(TracingServiceImplTest, LockdownMode) {
   std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
   consumer->Connect(svc.get());
@@ -1057,8 +1513,8 @@
 
   producer->WaitForDataSourceStart("ds_1");
 
-  auto writer1 = producer->CreateTraceWriter("ds_1");
-  producer->WaitForFlush(writer1.get());
+  auto writer = producer->CreateTraceWriter("ds_1");
+  producer->WaitForFlush(writer.get());
 
   producer->WaitForDataSourceStop("ds_1");
   consumer->WaitForTracingDisabled();