Merge "probes_producer: Add inode filemap for /system"
diff --git a/Android.bp b/Android.bp
index 16fefe9..a451202 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@
     "src/base/thread_checker.cc",
     "src/base/unix_task_runner.cc",
     "src/base/watchdog.cc",
+    "src/ftrace_reader/atrace_wrapper.cc",
     "src/ftrace_reader/cpu_reader.cc",
     "src/ftrace_reader/event_info.cc",
     "src/ftrace_reader/event_info_constants.cc",
@@ -227,6 +228,7 @@
     "src/base/thread_checker.cc",
     "src/base/unix_task_runner.cc",
     "src/base/watchdog.cc",
+    "src/ftrace_reader/atrace_wrapper.cc",
     "src/ftrace_reader/cpu_reader.cc",
     "src/ftrace_reader/end_to_end_integrationtest.cc",
     "src/ftrace_reader/event_info.cc",
@@ -2958,6 +2960,7 @@
     "src/base/watchdog.cc",
     "src/base/watchdog_unittest.cc",
     "src/base/weak_ptr_unittest.cc",
+    "src/ftrace_reader/atrace_wrapper.cc",
     "src/ftrace_reader/cpu_reader.cc",
     "src/ftrace_reader/cpu_reader_unittest.cc",
     "src/ftrace_reader/event_info.cc",
@@ -3120,7 +3123,9 @@
     "src/ftrace_reader/ftrace_procfs.cc",
     "src/ftrace_reader/proto_translation_table.cc",
     "src/traced/probes/probes_producer.cc",
+    "test/fake_producer.cc",
     "test/fake_consumer.cc",
+    "test/task_runner_thread.cc",
     "test/end_to_end_integrationtest.cc",
   ],
   export_include_dirs: [
@@ -3133,6 +3138,9 @@
     "perfetto_src_tracing_ipc",
     "libgtest_prod",
   ],
+  cflags: [
+    "-DPERFETTO_BUILD_WITH_ANDROID",
+  ],
 }
 
 cc_library_static {
@@ -3151,4 +3159,7 @@
   static_libs: [
     "perfetto_src_tracing_ipc",
   ],
+  cflags: [
+    "-DPERFETTO_BUILD_WITH_ANDROID",
+  ],
 }
\ No newline at end of file
diff --git a/Android.bp.extras b/Android.bp.extras
index b882eaa..0b8563f 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -1,5 +1,4 @@
 // These targets are appended to the autogenerated Android.bp by tools/gen_android_bp.
-// TODO(lalitm): find a better way to do this sharing.
 cc_library_static {
   name: "perfetto_cts_deps",
   srcs: [
@@ -12,7 +11,9 @@
     "src/ftrace_reader/ftrace_procfs.cc",
     "src/ftrace_reader/proto_translation_table.cc",
     "src/traced/probes/probes_producer.cc",
+    "test/fake_producer.cc",
     "test/fake_consumer.cc",
+    "test/task_runner_thread.cc",
     "test/end_to_end_integrationtest.cc",
   ],
   export_include_dirs: [
diff --git a/include/perfetto/ftrace_reader/ftrace_controller.h b/include/perfetto/ftrace_reader/ftrace_controller.h
index 3644ec1..f5df1b2 100644
--- a/include/perfetto/ftrace_reader/ftrace_controller.h
+++ b/include/perfetto/ftrace_reader/ftrace_controller.h
@@ -165,7 +165,7 @@
                        uint32_t drain_period_ms);
 
   static void DrainCPUs(base::WeakPtr<FtraceController>, size_t generation);
-  static void UnblockReaders(base::WeakPtr<FtraceController>);
+  static void UnblockReaders(const base::WeakPtr<FtraceController>&);
 
   uint32_t GetDrainPeriodMs();
 
diff --git a/include/perfetto/tracing/core/trace_config.h b/include/perfetto/tracing/core/trace_config.h
index e57f728..bac3218 100644
--- a/include/perfetto/tracing/core/trace_config.h
+++ b/include/perfetto/tracing/core/trace_config.h
@@ -126,6 +126,11 @@
     std::string unknown_fields_;
   };
 
+  enum LockdownModeOperation {
+    LOCKDOWN_UNCHANGED = 0,
+    LOCKDOWN_CLEAR = 1,
+    LOCKDOWN_SET = 2,
+  };
   TraceConfig();
   ~TraceConfig();
   TraceConfig(TraceConfig&&) noexcept;
@@ -161,11 +166,17 @@
     enable_extra_guardrails_ = value;
   }
 
+  LockdownModeOperation lockdown_mode() const { return lockdown_mode_; }
+  void set_lockdown_mode(LockdownModeOperation value) {
+    lockdown_mode_ = value;
+  }
+
  private:
   std::vector<BufferConfig> buffers_;
   std::vector<DataSource> data_sources_;
   uint32_t duration_ms_ = {};
   bool enable_extra_guardrails_ = {};
+  LockdownModeOperation lockdown_mode_ = {};
 
   // Allows to preserve unknown protobuf fields for compatibility
   // with future versions of .proto files.
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index d35ac8c..bf659ba 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -132,6 +132,15 @@
   // and enables guardrails that limit resource usage for traces requested
   // by statsd.
   optional bool enable_extra_guardrails = 4;
+
+  enum LockdownModeOperation {
+    LOCKDOWN_UNCHANGED = 0;
+    LOCKDOWN_CLEAR = 1;
+    LOCKDOWN_SET = 2;
+  }
+  // Reject producers that are not running under the same UID as the tracing
+  // service.
+  optional LockdownModeOperation lockdown_mode = 5;
 }
 
 // End of protos/perfetto/config/trace_config.proto
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index 41349a8..57bc741 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -92,4 +92,13 @@
   // and enables guardrails that limit resource usage for traces requested
   // by statsd.
   optional bool enable_extra_guardrails = 4;
+
+  enum LockdownModeOperation {
+    LOCKDOWN_UNCHANGED = 0;
+    LOCKDOWN_CLEAR = 1;
+    LOCKDOWN_SET = 2;
+  }
+  // Reject producers that are not running under the same UID as the tracing
+  // service.
+  optional LockdownModeOperation lockdown_mode = 5;
 }
diff --git a/src/base/page_allocator.cc b/src/base/page_allocator.cc
index ac26c36..86aec38 100644
--- a/src/base/page_allocator.cc
+++ b/src/base/page_allocator.cc
@@ -26,7 +26,7 @@
 
 namespace {
 
-static constexpr size_t kGuardSize = kPageSize;
+constexpr size_t kGuardSize = kPageSize;
 
 // static
 PageAllocator::UniquePtr AllocateInternal(size_t size, bool unchecked) {
diff --git a/src/base/watchdog.cc b/src/base/watchdog.cc
index 97ece02..991e82f 100644
--- a/src/base/watchdog.cc
+++ b/src/base/watchdog.cc
@@ -30,13 +30,13 @@
 
 namespace {
 
-static constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
+constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
 
 bool IsMultipleOf(uint32_t number, uint32_t divisor) {
   return number >= divisor && number % divisor == 0;
 }
 
-double MeanForArray(uint64_t array[], size_t size) {
+double MeanForArray(const uint64_t array[], size_t size) {
   uint64_t total = 0;
   for (size_t i = 0; i < size; i++) {
     total += array[i];
diff --git a/src/base/watchdog_unittest.cc b/src/base/watchdog_unittest.cc
index d383735..3adc261 100644
--- a/src/base/watchdog_unittest.cc
+++ b/src/base/watchdog_unittest.cc
@@ -29,7 +29,8 @@
 
 class TestWatchdog : public Watchdog {
  public:
-  TestWatchdog(uint32_t polling_interval_ms) : Watchdog(polling_interval_ms) {}
+  explicit TestWatchdog(uint32_t polling_interval_ms)
+      : Watchdog(polling_interval_ms) {}
   ~TestWatchdog() override {}
   TestWatchdog(TestWatchdog&& other) noexcept = default;
 };
diff --git a/src/ftrace_reader/BUILD.gn b/src/ftrace_reader/BUILD.gn
index 5fffdb5..29a24b3 100644
--- a/src/ftrace_reader/BUILD.gn
+++ b/src/ftrace_reader/BUILD.gn
@@ -112,6 +112,8 @@
     "../protozero",
   ]
   sources = [
+    "atrace_wrapper.cc",
+    "atrace_wrapper.h",
     "cpu_reader.cc",
     "cpu_reader.h",
     "event_info.cc",
diff --git a/src/ftrace_reader/atrace_wrapper.cc b/src/ftrace_reader/atrace_wrapper.cc
new file mode 100644
index 0000000..8ca9b7a
--- /dev/null
+++ b/src/ftrace_reader/atrace_wrapper.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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 "atrace_wrapper.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+
+namespace {
+
+RunAtraceFunction g_run_atrace_for_testing = nullptr;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Args should include "atrace" for argv[0].
+bool ExecvAtrace(const std::vector<std::string>& args) {
+  int status = 1;
+
+  std::vector<char*> argv;
+  // args, and then a null.
+  argv.reserve(1 + args.size());
+  for (const auto& arg : args)
+    argv.push_back(const_cast<char*>(arg.c_str()));
+  argv.push_back(nullptr);
+
+  pid_t pid = fork();
+  PERFETTO_CHECK(pid >= 0);
+  if (pid == 0) {
+    // Close stdin/out/err + any file descriptor that we might have mistakenly
+    // not marked as FD_CLOEXEC.
+    for (int i = 0; i < 128; i++)
+      close(i);
+    execv("/system/bin/atrace", &argv[0]);
+    // Reached only if execv fails.
+    _exit(1);
+  }
+  PERFETTO_EINTR(waitpid(pid, &status, 0));
+  return status == 0;
+}
+#endif
+
+}  // namespace
+
+bool RunAtrace(const std::vector<std::string>& args) {
+  if (g_run_atrace_for_testing)
+    return g_run_atrace_for_testing(args);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  return ExecvAtrace(args);
+#else
+  PERFETTO_LOG("Atrace only supported on Android.");
+  return false;
+#endif
+}
+
+void SetRunAtraceForTesting(RunAtraceFunction f) {
+  g_run_atrace_for_testing = f;
+}
+
+}  // namespace perfetto
diff --git a/src/ftrace_reader/atrace_wrapper.h b/src/ftrace_reader/atrace_wrapper.h
new file mode 100644
index 0000000..1984525
--- /dev/null
+++ b/src/ftrace_reader/atrace_wrapper.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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 SRC_FTRACE_READER_ATRACE_WRAPPER_H_
+#define SRC_FTRACE_READER_ATRACE_WRAPPER_H_
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace perfetto {
+
+using RunAtraceFunction =
+    std::add_pointer<bool(const std::vector<std::string>& /*args*/)>::type;
+
+bool RunAtrace(const std::vector<std::string>& args);
+void SetRunAtraceForTesting(RunAtraceFunction);
+
+}  // namespace perfetto
+
+#endif  // SRC_FTRACE_READER_ATRACE_WRAPPER_H_
diff --git a/src/ftrace_reader/cpu_reader.cc b/src/ftrace_reader/cpu_reader.cc
index e3d1a5e..bb617cc 100644
--- a/src/ftrace_reader/cpu_reader.cc
+++ b/src/ftrace_reader/cpu_reader.cc
@@ -157,10 +157,11 @@
 }
 
 // static
-void CpuReader::RunWorkerThread(size_t cpu,
-                                int trace_fd,
-                                int staging_write_fd,
-                                std::function<void()> on_data_available) {
+void CpuReader::RunWorkerThread(
+    size_t cpu,
+    int trace_fd,
+    int staging_write_fd,
+    const std::function<void()>& on_data_available) {
   // This thread is responsible for moving data from the trace pipe into the
   // staging pipe at least one page at a time. This is done using the splice(2)
   // system call, which unlike poll/select makes it possible to block until at
diff --git a/src/ftrace_reader/cpu_reader.h b/src/ftrace_reader/cpu_reader.h
index 9379028..d3dbf80 100644
--- a/src/ftrace_reader/cpu_reader.h
+++ b/src/ftrace_reader/cpu_reader.h
@@ -170,7 +170,7 @@
   static void RunWorkerThread(size_t cpu,
                               int trace_fd,
                               int staging_write_fd,
-                              std::function<void()> on_data_available);
+                              const std::function<void()>& on_data_available);
 
   uint8_t* GetBuffer();
   CpuReader(const CpuReader&) = delete;
diff --git a/src/ftrace_reader/cpu_reader_unittest.cc b/src/ftrace_reader/cpu_reader_unittest.cc
index 8340706..5904f5e 100644
--- a/src/ftrace_reader/cpu_reader_unittest.cc
+++ b/src/ftrace_reader/cpu_reader_unittest.cc
@@ -57,14 +57,13 @@
   // Round to closest us.
   uint64_t actual_us = (actual_ns + kNanoInMicro / 2) / kNanoInMicro;
   uint64_t total_expected_us = expected_s * 1000 * 1000 + expected_us;
-  if (actual_us == total_expected_us) {
+  if (actual_us == total_expected_us)
     return ::testing::AssertionSuccess();
-  } else {
-    return ::testing::AssertionFailure()
-           << actual_ns / kNanoInSecond << "."
-           << (actual_ns % kNanoInSecond) / kNanoInMicro << " vs. "
-           << expected_s << "." << expected_us;
-  }
+
+  return ::testing::AssertionFailure()
+         << actual_ns / kNanoInSecond << "."
+         << (actual_ns % kNanoInSecond) / kNanoInMicro << " vs. " << expected_s
+         << "." << expected_us;
 }
 
 // Single class to manage the whole protozero -> scattered stream -> chunks ->
diff --git a/src/ftrace_reader/format_parser.cc b/src/ftrace_reader/format_parser.cc
index 4a265f9..ff516e7 100644
--- a/src/ftrace_reader/format_parser.cc
+++ b/src/ftrace_reader/format_parser.cc
@@ -45,7 +45,7 @@
     if (!(std::isalnum(c) || c == '_'))
       return false;
   }
-  return s.size() > 0 && !std::isdigit(s[0]);
+  return !s.empty() && !std::isdigit(s[0]);
 }
 
 }  // namespace
@@ -140,7 +140,7 @@
     return false;
   }
 
-  if (!has_id || !has_name || fields.size() == 0) {
+  if (!has_id || !has_name || fields.empty()) {
     if (output)
       fprintf(stderr, "Could not parse format file: %s.\n",
               !has_id ? "no ID found"
diff --git a/src/ftrace_reader/ftrace_config_muxer.cc b/src/ftrace_reader/ftrace_config_muxer.cc
index 7ec1f34..5a1ed14 100644
--- a/src/ftrace_reader/ftrace_config_muxer.cc
+++ b/src/ftrace_reader/ftrace_config_muxer.cc
@@ -16,16 +16,14 @@
 
 #include "ftrace_config_muxer.h"
 
-#include <fcntl.h>
 #include <stdint.h>
 #include <string.h>
-#include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <algorithm>
 
+#include "atrace_wrapper.h"
 #include "perfetto/base/utils.h"
 #include "proto_translation_table.h"
 
@@ -47,28 +45,6 @@
   return result;
 }
 
-// Including "atrace" for argv[0].
-bool RunAtrace(const std::vector<std::string>& args) {
-  int status = 1;
-
-  std::vector<char*> argv;
-  // args, and then a null.
-  argv.reserve(1 + args.size());
-  for (const auto& arg : args)
-    argv.push_back(const_cast<char*>(arg.c_str()));
-  argv.push_back(nullptr);
-
-  pid_t pid = fork();
-  PERFETTO_CHECK(pid >= 0);
-  if (pid == 0) {
-    execv("/system/bin/atrace", &argv[0]);
-    // Reached only if execv fails.
-    _exit(1);
-  }
-  PERFETTO_EINTR(waitpid(pid, &status, 0));
-  return status == 0;
-}
-
 }  // namespace
 
 std::set<std::string> GetFtraceEvents(const FtraceConfig& request) {
@@ -115,7 +91,7 @@
 
     // If we're about to turn tracing on use this opportunity do some setup:
     if (RequiresAtrace(request))
-      EnableAtraceOnAndroid(request);
+      EnableAtrace(request);
     SetupClock(request);
     SetupBufferSize(request);
   } else {
@@ -184,7 +160,7 @@
     ftrace_->ClearTrace();
     current_state_.tracing_on = false;
     if (current_state_.atrace_on)
-      DisableAtraceOnAndroid();
+      DisableAtrace();
   }
 
   return true;
@@ -217,19 +193,11 @@
   current_state_.cpu_buffer_size_pages = pages;
 }
 
-void FtraceConfigMuxer::EnableAtraceOnAndroid(const FtraceConfig& request) {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  EnableAtrace(request);
-#else
-  PERFETTO_LOG("Atrace only supported on Android.");
-#endif
-}
-
 void FtraceConfigMuxer::EnableAtrace(const FtraceConfig& request) {
   PERFETTO_DCHECK(!current_state_.atrace_on);
-  current_state_.atrace_on = true;
 
   PERFETTO_DLOG("Start atrace...");
+
   std::vector<std::string> args;
   args.push_back("atrace");  // argv0 for exec()
   args.push_back("--async_start");
@@ -241,26 +209,21 @@
       args.push_back(app);
   }
 
-  PERFETTO_CHECK(RunAtrace(args));
-  PERFETTO_DLOG("...done");
-}
+  if (RunAtrace(args))
+    current_state_.atrace_on = true;
 
-void FtraceConfigMuxer::DisableAtraceOnAndroid() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  DisableAtrace();
-#else
-  PERFETTO_LOG("Atrace only supported on Android.");
-#endif
+  PERFETTO_DLOG("...done");
 }
 
 void FtraceConfigMuxer::DisableAtrace() {
-  PERFETTO_DCHECK(!current_state_.atrace_on);
+  PERFETTO_DCHECK(current_state_.atrace_on);
 
   PERFETTO_DLOG("Stop atrace...");
-  PERFETTO_CHECK(RunAtrace({"atrace", "--async_stop"}));
-  PERFETTO_DLOG("...done");
 
-  current_state_.atrace_on = false;
+  if (RunAtrace({"atrace", "--async_stop"}))
+    current_state_.atrace_on = false;
+
+  PERFETTO_DLOG("...done");
 }
 
 }  // namespace perfetto
diff --git a/src/ftrace_reader/ftrace_config_muxer.h b/src/ftrace_reader/ftrace_config_muxer.h
index 37d031b..c24609c 100644
--- a/src/ftrace_reader/ftrace_config_muxer.h
+++ b/src/ftrace_reader/ftrace_config_muxer.h
@@ -38,7 +38,8 @@
 // |RemoveConfig|.
 class FtraceConfigMuxer {
  public:
-  // The ProtoTranslationTable table should outlive this instance.
+  // The FtraceConfigMuxer and ProtoTranslationTable
+  // should outlive this instance.
   FtraceConfigMuxer(FtraceProcfs* ftrace, const ProtoTranslationTable* table);
   virtual ~FtraceConfigMuxer();
 
@@ -79,8 +80,6 @@
 
   void SetupClock(const FtraceConfig& request);
   void SetupBufferSize(const FtraceConfig& request);
-  void EnableAtraceOnAndroid(const FtraceConfig& request);
-  void DisableAtraceOnAndroid();
   void EnableAtrace(const FtraceConfig& request);
   void DisableAtrace();
 
diff --git a/src/ftrace_reader/ftrace_config_muxer_unittest.cc b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
index f7e3e74..b0ca96c 100644
--- a/src/ftrace_reader/ftrace_config_muxer_unittest.cc
+++ b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
@@ -18,14 +18,18 @@
 
 #include <memory>
 
-#include "ftrace_procfs.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+
+#include "atrace_wrapper.h"
+#include "ftrace_procfs.h"
 #include "proto_translation_table.h"
 
 using testing::_;
 using testing::AnyNumber;
 using testing::Contains;
+using testing::ElementsAreArray;
+using testing::Eq;
 using testing::IsEmpty;
 using testing::NiceMock;
 using testing::Not;
@@ -52,6 +56,20 @@
   MOCK_CONST_METHOD0(NumberOfCpus, size_t());
 };
 
+struct MockRunAtrace {
+  MockRunAtrace() {
+    static MockRunAtrace* instance;
+    instance = this;
+    SetRunAtraceForTesting([](const std::vector<std::string>& args) {
+      return instance->RunAtrace(args);
+    });
+  }
+
+  ~MockRunAtrace() { SetRunAtraceForTesting(nullptr); }
+
+  MOCK_METHOD1(RunAtrace, bool(const std::vector<std::string>&));
+};
+
 std::unique_ptr<ProtoTranslationTable> CreateFakeTable() {
   std::vector<Field> common_fields;
   std::vector<Event> events;
@@ -157,15 +175,10 @@
   ASSERT_FALSE(id);
 }
 
-// TODO(hjd): Mock atrace on Android.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define MAYBE_Atrace DISABLED_Atrace
-#else
-#define MAYBE_Atrace Atrace
-#endif
-TEST(FtraceConfigMuxerTest, MAYBE_Atrace) {
+TEST(FtraceConfigMuxerTest, Atrace) {
   std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
   NiceMock<MockFtraceProcfs> ftrace;
+  MockRunAtrace atrace;
 
   FtraceConfig config = CreateFtraceConfig({"sched_switch"});
   *config.add_atrace_categories() = "sched";
@@ -174,6 +187,9 @@
 
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
+  EXPECT_CALL(atrace,
+              RunAtrace(ElementsAreArray({"atrace", "--async_start", "sched"})))
+      .WillOnce(Return(true));
 
   FtraceConfigId id = model.RequestConfig(config);
   ASSERT_TRUE(id);
@@ -183,6 +199,8 @@
   EXPECT_THAT(actual_config->ftrace_events(), Contains("sched_switch"));
   EXPECT_THAT(actual_config->ftrace_events(), Contains("print"));
 
+  EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_stop"})))
+      .WillOnce(Return(true));
   ASSERT_TRUE(model.RemoveConfig(id));
 }
 
diff --git a/src/ftrace_reader/ftrace_controller.cc b/src/ftrace_reader/ftrace_controller.cc
index 3818507..ccf2022 100644
--- a/src/ftrace_reader/ftrace_controller.cc
+++ b/src/ftrace_reader/ftrace_controller.cc
@@ -26,6 +26,7 @@
 
 #include <array>
 #include <string>
+#include <utility>
 
 #include "cpu_reader.h"
 #include "event_info.h"
@@ -85,7 +86,6 @@
 
 }  // namespace
 
-// TODO(fmayer): Actually call this on shutdown.
 // Method of last resort to reset ftrace state.
 // We don't know what state the rest of the system and process is so as far
 // as possible avoid allocations.
@@ -184,7 +184,7 @@
 
 // static
 void FtraceController::UnblockReaders(
-    base::WeakPtr<FtraceController> weak_this) {
+    const base::WeakPtr<FtraceController>& weak_this) {
   if (!weak_this)
     return;
   // Unblock all waiting readers to start moving more data into their
@@ -195,7 +195,7 @@
 void FtraceController::StartIfNeeded() {
   if (sinks_.size() > 1)
     return;
-  PERFETTO_CHECK(sinks_.size() != 0);
+  PERFETTO_CHECK(!sinks_.empty());
   {
     std::unique_lock<std::mutex> lock(lock_);
     PERFETTO_CHECK(!listening_for_raw_trace_data_);
@@ -213,7 +213,7 @@
 }
 
 uint32_t FtraceController::GetDrainPeriodMs() {
-  if (sinks_.size() == 0)
+  if (sinks_.empty())
     return kDefaultDrainPeriodMs;
   uint32_t min_drain_period_ms = kMaxDrainPeriodMs + 1;
   for (const FtraceSink* sink : sinks_) {
@@ -236,7 +236,7 @@
 }
 
 void FtraceController::StopIfNeeded() {
-  if (sinks_.size() != 0)
+  if (!sinks_.empty())
     return;
   {
     // Unblock any readers that are waiting for us to drain data.
@@ -285,10 +285,11 @@
 
   auto controller_weak = weak_factory_.GetWeakPtr();
   auto filter = std::unique_ptr<EventFilter>(new EventFilter(
-      *table_.get(), FtraceEventsAsSet(*ftrace_config_muxer_->GetConfig(id))));
+      *table_, FtraceEventsAsSet(*ftrace_config_muxer_->GetConfig(id))));
 
-  auto sink = std::unique_ptr<FtraceSink>(new FtraceSink(
-      std::move(controller_weak), id, config, std::move(filter), delegate));
+  auto sink = std::unique_ptr<FtraceSink>(
+      new FtraceSink(std::move(controller_weak), id, std::move(config),
+                     std::move(filter), delegate));
   Register(sink.get());
   return sink;
 }
@@ -348,7 +349,7 @@
                        Delegate* delegate)
     : controller_weak_(std::move(controller_weak)),
       id_(id),
-      config_(config),
+      config_(std::move(config)),
       filter_(std::move(filter)),
       delegate_(delegate){};
 
@@ -370,7 +371,7 @@
 void FtraceMetadata::AddPid(int32_t pid) {
   // Speculative optimization aginst repated pid's while keeping
   // faster insertion than a set.
-  if (pids.size() && pids.back() == pid)
+  if (!pids.empty() && pids.back() == pid)
     return;
   pids.push_back(pid);
 }
diff --git a/src/ftrace_reader/ftrace_controller_unittest.cc b/src/ftrace_reader/ftrace_controller_unittest.cc
index e0f9b5a..964baff 100644
--- a/src/ftrace_reader/ftrace_controller_unittest.cc
+++ b/src/ftrace_reader/ftrace_controller_unittest.cc
@@ -135,7 +135,7 @@
 
 class MockFtraceProcfs : public FtraceProcfs {
  public:
-  MockFtraceProcfs(size_t cpu_count = 1) : FtraceProcfs("/root/") {
+  explicit MockFtraceProcfs(size_t cpu_count = 1) : FtraceProcfs("/root/") {
     ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(cpu_count));
     EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
 
@@ -260,8 +260,9 @@
         std::unique_ptr<MockFtraceProcfs>(new MockFtraceProcfs(cpu_count));
   }
 
-  MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
   auto model = FakeModel(ftrace_procfs.get(), table.get());
+
+  MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
   return std::unique_ptr<TestFtraceController>(new TestFtraceController(
       std::move(ftrace_procfs), std::move(table), std::move(model),
       std::move(runner), raw_procfs));
diff --git a/src/ftrace_reader/ftrace_procfs.cc b/src/ftrace_reader/ftrace_procfs.cc
index 1eef505..3af2c88 100644
--- a/src/ftrace_reader/ftrace_procfs.cc
+++ b/src/ftrace_reader/ftrace_procfs.cc
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "ftrace_procfs.h"
+#include "src/ftrace_reader/ftrace_procfs.h"
 
-#include <fcntl.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <fstream>
 #include <sstream>
@@ -42,25 +42,18 @@
 
 namespace {
 
-int OpenKmesgFD() {
-  // This environment variable gets set to a fd to /dev/kmsg opened for writing.
-  // The file gets opened by init as configured in perfetto.rc. We cannot open
-  // the file directly due to permissions.
-  const char* env = getenv("ANDROID_FILE__dev_kmsg");
-  return env != nullptr ? atoi(env) : -1;
-}
-
 void KernelLogWrite(const char* s) {
   PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
-  static int kmesg_fd = OpenKmesgFD();
-  if (kmesg_fd != -1) {
-    base::ignore_result(write(kmesg_fd, s, strlen(s)));
-  }
+  if (FtraceProcfs::g_kmesg_fd != -1)
+    base::ignore_result(write(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
 }
 
 }  // namespace
 
 // static
+int FtraceProcfs::g_kmesg_fd = -1;  // Set by ProbesMain() in probes.cc .
+
+// static
 std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
   if (!CheckRootPath(root)) {
     return nullptr;
@@ -148,11 +141,11 @@
   std::string path = root_ + "trace_clock";
   std::string s = ReadFileIntoString(path);
 
-  size_t start = s.find("[");
+  size_t start = s.find('[');
   if (start == std::string::npos)
     return "";
 
-  size_t end = s.find("]", start);
+  size_t end = s.find(']', start);
   if (end == std::string::npos)
     return "";
 
@@ -168,7 +161,7 @@
   size_t end = 0;
 
   while (true) {
-    end = s.find(" ", start);
+    end = s.find(' ', start);
     if (end == std::string::npos)
       end = s.size();
     if (start == end)
@@ -201,7 +194,7 @@
 
 bool FtraceProcfs::WriteToFile(const std::string& path,
                                const std::string& str) {
-  base::ScopedFile fd = base::OpenFile(path.c_str(), O_WRONLY);
+  base::ScopedFile fd = base::OpenFile(path, O_WRONLY);
   if (!fd)
     return false;
   ssize_t written = PERFETTO_EINTR(write(fd.get(), str.c_str(), str.length()));
@@ -214,11 +207,11 @@
 base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) {
   std::string path =
       root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw";
-  return base::OpenFile(path.c_str(), O_RDONLY | O_NONBLOCK);
+  return base::OpenFile(path, O_RDONLY | O_NONBLOCK);
 }
 
 char FtraceProcfs::ReadOneCharFromFile(const std::string& path) {
-  base::ScopedFile fd = base::OpenFile(path.c_str(), O_RDONLY);
+  base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
   PERFETTO_CHECK(fd);
   char result = '\0';
   ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
@@ -227,7 +220,7 @@
 }
 
 bool FtraceProcfs::ClearFile(const std::string& path) {
-  base::ScopedFile fd = base::OpenFile(path.c_str(), O_WRONLY | O_TRUNC);
+  base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
   return !!fd;
 }
 
diff --git a/src/ftrace_reader/ftrace_procfs.h b/src/ftrace_reader/ftrace_procfs.h
index 10fbda9..faaca88 100644
--- a/src/ftrace_reader/ftrace_procfs.h
+++ b/src/ftrace_reader/ftrace_procfs.h
@@ -28,6 +28,7 @@
 class FtraceProcfs {
  public:
   static std::unique_ptr<FtraceProcfs> Create(const std::string& root);
+  static int g_kmesg_fd;
 
   explicit FtraceProcfs(const std::string& root);
   virtual ~FtraceProcfs();
diff --git a/src/ftrace_reader/proto_translation_table.cc b/src/ftrace_reader/proto_translation_table.cc
index 254e710..d5b5ec4 100644
--- a/src/ftrace_reader/proto_translation_table.cc
+++ b/src/ftrace_reader/proto_translation_table.cc
@@ -229,7 +229,7 @@
     std::string contents =
         ftrace_procfs->ReadEventFormat(event.group, event.name);
     FtraceEvent ftrace_event;
-    if (contents == "" || !ParseFtraceEvent(contents, &ftrace_event)) {
+    if (contents.empty() || !ParseFtraceEvent(contents, &ftrace_event)) {
       continue;
     }
 
diff --git a/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc b/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc
index fa6e178..305f561 100644
--- a/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc
+++ b/src/ftrace_reader/test/scattered_stream_delegate_for_testing.cc
@@ -27,7 +27,7 @@
 protozero::ContiguousMemoryRange
 ScatteredStreamDelegateForTesting::GetNewBuffer() {
   PERFETTO_CHECK(writer_);
-  if (chunks_.size()) {
+  if (!chunks_.empty()) {
     size_t used = chunk_size_ - writer_->bytes_available();
     chunks_used_size_.push_back(used);
   }
diff --git a/src/ipc/client_impl.cc b/src/ipc/client_impl.cc
index 4ef600b..0560244 100644
--- a/src/ipc/client_impl.cc
+++ b/src/ipc/client_impl.cc
@@ -16,7 +16,9 @@
 
 #include "src/ipc/client_impl.h"
 
+#include <fcntl.h>
 #include <inttypes.h>
+#include <unistd.h>
 
 #include <utility>
 
@@ -156,6 +158,8 @@
     rsize = sock_->Receive(buf.data, buf.size, &fd);
     if (fd) {
       PERFETTO_DCHECK(!received_fd_);
+      int res = fcntl(*fd, F_SETFD, FD_CLOEXEC);
+      PERFETTO_DCHECK(res == 0);
       received_fd_ = std::move(fd);
     }
     if (!frame_deserializer_.EndReceive(rsize)) {
diff --git a/src/ipc/client_impl_unittest.cc b/src/ipc/client_impl_unittest.cc
index cc98ffe..10f59f9 100644
--- a/src/ipc/client_impl_unittest.cc
+++ b/src/ipc/client_impl_unittest.cc
@@ -151,7 +151,7 @@
         method->set_name(method_it.first);
         method->set_id(method_it.second->id);
       }
-      return Reply(reply);
+      Reply(reply);
     } else if (req.msg_case() == Frame::kMsgInvokeMethod) {
       // Lookup the service and method.
       bool has_more = false;
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 4d1bfae..c423269 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -123,11 +123,11 @@
 
 void HostImpl::OnReceivedFrame(ClientConnection* client,
                                const Frame& req_frame) {
-  if (req_frame.msg_case() == Frame::kMsgBindService) {
+  if (req_frame.msg_case() == Frame::kMsgBindService)
     return OnBindService(client, req_frame);
-  } else if (req_frame.msg_case() == Frame::kMsgInvokeMethod) {
+  if (req_frame.msg_case() == Frame::kMsgInvokeMethod)
     return OnInvokeMethod(client, req_frame);
-  }
+
   PERFETTO_DLOG("Received invalid RPC frame %u from client %" PRIu64,
                 req_frame.msg_case(), client->id);
   Frame reply_frame;
diff --git a/src/process_stats/file_utils.cc b/src/process_stats/file_utils.cc
index 633e400..1ddd1c8 100644
--- a/src/process_stats/file_utils.cc
+++ b/src/process_stats/file_utils.cc
@@ -24,7 +24,7 @@
 }
 
 void ForEachPidInProcPath(const char* proc_path,
-                          std::function<void(int)> predicate) {
+                          const std::function<void(int)>& predicate) {
   DIR* root_dir = opendir(proc_path);
   ScopedDir autoclose(root_dir);
   struct dirent* child_dir;
@@ -48,7 +48,7 @@
       break;
     if (rsize == -1 && errno == EINTR)
       continue;
-    else if (rsize < 0)
+    if (rsize < 0)
       return -1;
     tot_read += static_cast<size_t>(rsize);
   } while (tot_read < length);
diff --git a/src/process_stats/file_utils.h b/src/process_stats/file_utils.h
index a853ecd..9c160da 100644
--- a/src/process_stats/file_utils.h
+++ b/src/process_stats/file_utils.h
@@ -34,7 +34,7 @@
 // Invokes predicate(pid) for each folder in |proc_path|/[0-9]+ which has
 // a numeric name (typically pids and tids).
 void ForEachPidInProcPath(const char* proc_path,
-                          std::function<void(int)> predicate);
+                          const std::function<void(int)>& predicate);
 
 // Reads the contents of |path| fully into |buf| up to |length| chars.
 // |buf| is guaranteed to be null terminated.
diff --git a/src/traced/probes/probes.cc b/src/traced/probes/probes.cc
index 78ccea6..f8dea99 100644
--- a/src/traced/probes/probes.cc
+++ b/src/traced/probes/probes.cc
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
 #include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/unix_task_runner.h"
 #include "perfetto/traced/traced.h"
 
+#include "src/ftrace_reader/ftrace_procfs.h"
 #include "src/traced/probes/probes_producer.h"
 
 namespace perfetto {
@@ -48,6 +52,19 @@
   watchdog->Start();
 
   PERFETTO_LOG("Starting %s service", argv[0]);
+
+  // This environment variable is set by Android's init to a fd to /dev/kmsg
+  // opened for writing (see perfetto.rc). We cannot open the file directly
+  // due to permissions.
+  const char* env = getenv("ANDROID_FILE__dev_kmsg");
+  if (env) {
+    FtraceProcfs::g_kmesg_fd = atoi(env);
+    // The file descriptor passed by init doesn't have the FD_CLOEXEC bit set.
+    // Set it so we don't leak this fd while invoking atrace.
+    int res = fcntl(FtraceProcfs::g_kmesg_fd, F_SETFD, FD_CLOEXEC);
+    PERFETTO_DCHECK(res == 0);
+  }
+
   base::UnixTaskRunner task_runner;
   ProbesProducer producer;
   producer.ConnectWithRetries(PERFETTO_PRODUCER_SOCK_NAME, &task_runner);
diff --git a/src/tracing/core/service_impl.cc b/src/tracing/core/service_impl.cc
index 7da32f7..aa53a26 100644
--- a/src/tracing/core/service_impl.cc
+++ b/src/tracing/core/service_impl.cc
@@ -86,6 +86,12 @@
     size_t shared_buffer_size_hint_bytes) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
+  if (lockdown_mode_ && uid != geteuid()) {
+    PERFETTO_DLOG("Lockdown mode. Rejecting producer with UID %ld",
+                  static_cast<unsigned long>(uid));
+    return nullptr;
+  }
+
   if (producers_.size() >= kMaxProducerID) {
     PERFETTO_DCHECK(false);
     return nullptr;
@@ -165,6 +171,10 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   PERFETTO_DLOG("Enabling tracing for consumer %p",
                 reinterpret_cast<void*>(consumer));
+  if (cfg.lockdown_mode() == TraceConfig::LockdownModeOperation::LOCKDOWN_SET)
+    lockdown_mode_ = true;
+  if (cfg.lockdown_mode() == TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR)
+    lockdown_mode_ = false;
   if (consumer->tracing_session_id_) {
     PERFETTO_DLOG(
         "A Consumer is trying to EnableTracing() but another tracing session "
@@ -341,7 +351,7 @@
         continue;
       const uid_t page_owner = tbuf.get_page_owner(page_idx);
       uint32_t layout = abi.page_layout_dbg(page_idx);
-      size_t num_chunks = abi.GetNumChunksForLayout(layout);
+      size_t num_chunks = SharedMemoryABI::GetNumChunksForLayout(layout);
       for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
         if (abi.GetChunkState(page_idx, chunk_idx) ==
             SharedMemoryABI::kChunkFree) {
@@ -516,6 +526,12 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   ProducerEndpointImpl* producer = GetProducer(data_source.producer_id);
   PERFETTO_DCHECK(producer);
+  // An existing producer that is not ftrace could have registered itself as
+  // ftrace, we must not enable it in that case.
+  if (lockdown_mode_ && producer->uid_ != getuid()) {
+    PERFETTO_DLOG("Lockdown mode: not enabling producer %hu", producer->id_);
+    return;
+  }
   // TODO(primiano): match against |producer_name_filter| and add tests
   // for registration ordering (data sources vs consumers).
 
diff --git a/src/tracing/core/service_impl.h b/src/tracing/core/service_impl.h
index 2717d2d..eae6364 100644
--- a/src/tracing/core/service_impl.h
+++ b/src/tracing/core/service_impl.h
@@ -262,6 +262,8 @@
   std::map<TracingSessionID, TracingSession> tracing_sessions_;
   std::map<BufferID, TraceBuffer> buffers_;
 
+  bool lockdown_mode_ = false;
+
   PERFETTO_THREAD_CHECKER(thread_checker_)
 
   base::WeakPtrFactory<ServiceImpl> weak_ptr_factory_;  // Keep at the end.
diff --git a/src/tracing/core/service_impl_unittest.cc b/src/tracing/core/service_impl_unittest.cc
index 7c9f0a4..043eae2 100644
--- a/src/tracing/core/service_impl_unittest.cc
+++ b/src/tracing/core/service_impl_unittest.cc
@@ -174,6 +174,49 @@
   Mock::VerifyAndClearExpectations(&mock_consumer);
 }
 
+TEST_F(ServiceImplTest, LockdownMode) {
+  MockConsumer mock_consumer;
+  EXPECT_CALL(mock_consumer, OnConnect());
+  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
+      svc->ConnectConsumer(&mock_consumer);
+
+  TraceConfig trace_config;
+  trace_config.set_lockdown_mode(
+      TraceConfig::LockdownModeOperation::LOCKDOWN_SET);
+  consumer_endpoint->EnableTracing(trace_config);
+  task_runner.RunUntilIdle();
+
+  InSequence seq;
+
+  MockProducer mock_producer;
+  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
+      svc->ConnectProducer(&mock_producer, geteuid() + 1 /* uid */);
+
+  MockProducer mock_producer_sameuid;
+  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_sameuid =
+      svc->ConnectProducer(&mock_producer_sameuid, geteuid() /* uid */);
+
+  EXPECT_CALL(mock_producer, OnConnect()).Times(0);
+  EXPECT_CALL(mock_producer_sameuid, OnConnect());
+  task_runner.RunUntilIdle();
+
+  Mock::VerifyAndClearExpectations(&mock_producer);
+
+  consumer_endpoint->DisableTracing();
+  task_runner.RunUntilIdle();
+
+  trace_config.set_lockdown_mode(
+      TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR);
+  consumer_endpoint->EnableTracing(trace_config);
+  task_runner.RunUntilIdle();
+
+  EXPECT_CALL(mock_producer, OnConnect());
+  producer_endpoint_sameuid =
+      svc->ConnectProducer(&mock_producer, geteuid() + 1);
+
+  task_runner.RunUntilIdle();
+}
+
 TEST_F(ServiceImplTest, DisconnectConsumerWhileTracing) {
   MockProducer mock_producer;
   std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
diff --git a/src/tracing/core/trace_config.cc b/src/tracing/core/trace_config.cc
index f17663f..7c397a7 100644
--- a/src/tracing/core/trace_config.cc
+++ b/src/tracing/core/trace_config.cc
@@ -61,6 +61,10 @@
                 "size mismatch");
   enable_extra_guardrails_ = static_cast<decltype(enable_extra_guardrails_)>(
       proto.enable_extra_guardrails());
+
+  static_assert(sizeof(lockdown_mode_) == sizeof(proto.lockdown_mode()),
+                "size mismatch");
+  lockdown_mode_ = static_cast<decltype(lockdown_mode_)>(proto.lockdown_mode());
   unknown_fields_ = proto.unknown_fields();
 }
 
@@ -88,6 +92,11 @@
   proto->set_enable_extra_guardrails(
       static_cast<decltype(proto->enable_extra_guardrails())>(
           enable_extra_guardrails_));
+
+  static_assert(sizeof(lockdown_mode_) == sizeof(proto->lockdown_mode()),
+                "size mismatch");
+  proto->set_lockdown_mode(
+      static_cast<decltype(proto->lockdown_mode())>(lockdown_mode_));
   *(proto->mutable_unknown_fields()) = unknown_fields_;
 }
 
diff --git a/test/cts/Android.bp b/test/cts/Android.bp
index 18395f1..f22c193 100644
--- a/test/cts/Android.bp
+++ b/test/cts/Android.bp
@@ -5,16 +5,16 @@
   ],
   static_libs: [
     "libgmock",
-    "libgtest",
-    "libgtest_main",
     "libprotobuf-cpp-lite",
     "perfetto_src_tracing_ipc",
-    "perfetto_cts_deps",
   ],
   shared_libs: [
     "libandroid",
     "liblog",
   ],
+  whole_static_libs: [
+    "perfetto_cts_deps"
+  ],
   test_suites: [
     "cts",
     "vts",
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index 38b6b72..05b0d80 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -24,6 +24,7 @@
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsPerfettoTestCases->/data/local/tmp/CtsPerfettoTestCases" />
+        <option name="append-bitness" value="true" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="am start -n android.perfetto.producer/.ProducerActivity" />