Add libtracing_perfetto for tracing using Perfetto SDK

libtracing_perfetto internally uses the Perfetto SDK or
libcutils based on an aconfig flag.

Test: atest libtracing_perfetto_tests
Bug: 303199244
Ignore-AOSP-First: Internal feature
Change-Id: I9922d11607051ff22777e3df12b8f877a765dfa7
diff --git a/libs/tracing_perfetto/.clang-format b/libs/tracing_perfetto/.clang-format
new file mode 100644
index 0000000..f397454
--- /dev/null
+++ b/libs/tracing_perfetto/.clang-format
@@ -0,0 +1,12 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 80
+ContinuationIndentWidth: 4
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+UseTab: Never
+PenaltyExcessCharacter: 32
\ No newline at end of file
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
new file mode 100644
index 0000000..3a4c869
--- /dev/null
+++ b/libs/tracing_perfetto/Android.bp
@@ -0,0 +1,49 @@
+// Copyright 2024 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+    name: "libtracing_perfetto",
+    export_include_dirs: [
+        "include",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-enum-compare",
+        "-Wno-unused-function",
+    ],
+
+    srcs: [
+        "tracing_perfetto.cpp",
+        "tracing_perfetto_internal.cpp",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "libperfetto_c",
+        "android.os.flags-aconfig-cc-host",
+    ],
+
+    host_supported: true,
+}
diff --git a/libs/tracing_perfetto/OWNERS b/libs/tracing_perfetto/OWNERS
new file mode 100644
index 0000000..e2d4b46
--- /dev/null
+++ b/libs/tracing_perfetto/OWNERS
@@ -0,0 +1,2 @@
+zezeozue@google.com
+biswarupp@google.com
\ No newline at end of file
diff --git a/libs/tracing_perfetto/include/trace_categories.h b/libs/tracing_perfetto/include/trace_categories.h
new file mode 100644
index 0000000..6d4168b
--- /dev/null
+++ b/libs/tracing_perfetto/include/trace_categories.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 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 TRACE_CATEGORIES_H
+#define TRACE_CATEGORIES_H
+
+/**
+ * Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
+ */
+#define TRACE_CATEGORY_ALWAYS (1 << 0)
+#define TRACE_CATEGORY_GRAPHICS (1 << 1)
+#define TRACE_CATEGORY_INPUT (1 << 2)
+#define TRACE_CATEGORY_VIEW (1 << 3)
+#define TRACE_CATEGORY_WEBVIEW (1 << 4)
+#define TRACE_CATEGORY_WINDOW_MANAGER (1 << 5)
+#define TRACE_CATEGORY_ACTIVITY_MANAGER (1 << 6)
+#define TRACE_CATEGORY_SYNC_MANAGER (1 << 7)
+#define TRACE_CATEGORY_AUDIO (1 << 8)
+#define TRACE_CATEGORY_VIDEO (1 << 9)
+#define TRACE_CATEGORY_CAMERA (1 << 10)
+#define TRACE_CATEGORY_HAL (1 << 11)
+#define TRACE_CATEGORY_APP (1 << 12)
+#define TRACE_CATEGORY_RESOURCES (1 << 13)
+#define TRACE_CATEGORY_DALVIK (1 << 14)
+#define TRACE_CATEGORY_RS (1 << 15)
+#define TRACE_CATEGORY_BIONIC (1 << 16)
+#define TRACE_CATEGORY_POWER (1 << 17)
+#define TRACE_CATEGORY_PACKAGE_MANAGER (1 << 18)
+#define TRACE_CATEGORY_SYSTEM_SERVER (1 << 19)
+#define TRACE_CATEGORY_DATABASE (1 << 20)
+#define TRACE_CATEGORY_NETWORK (1 << 21)
+#define TRACE_CATEGORY_ADB (1 << 22)
+#define TRACE_CATEGORY_VIBRATOR (1 << 23)
+#define TRACE_CATEGORY_AIDL (1 << 24)
+#define TRACE_CATEGORY_NNAPI (1 << 25)
+#define TRACE_CATEGORY_RRO (1 << 26)
+#define TRACE_CATEGORY_THERMAL (1 << 27)
+
+// Allow all categories except TRACE_CATEGORY_APP
+#define TRACE_CATEGORIES                                                      \
+  TRACE_CATEGORY_ALWAYS | TRACE_CATEGORY_GRAPHICS | TRACE_CATEGORY_INPUT |    \
+      TRACE_CATEGORY_VIEW | TRACE_CATEGORY_WEBVIEW |                          \
+      TRACE_CATEGORY_WINDOW_MANAGER | TRACE_CATEGORY_ACTIVITY_MANAGER |       \
+      TRACE_CATEGORY_SYNC_MANAGER | TRACE_CATEGORY_AUDIO |                    \
+      TRACE_CATEGORY_VIDEO | TRACE_CATEGORY_CAMERA | TRACE_CATEGORY_HAL |     \
+      TRACE_CATEGORY_RESOURCES | TRACE_CATEGORY_DALVIK | TRACE_CATEGORY_RS |  \
+      TRACE_CATEGORY_BIONIC | TRACE_CATEGORY_POWER |                          \
+      TRACE_CATEGORY_PACKAGE_MANAGER | TRACE_CATEGORY_SYSTEM_SERVER |         \
+      TRACE_CATEGORY_DATABASE | TRACE_CATEGORY_NETWORK | TRACE_CATEGORY_ADB | \
+      TRACE_CATEGORY_VIBRATOR | TRACE_CATEGORY_AIDL | TRACE_CATEGORY_NNAPI |  \
+      TRACE_CATEGORY_RRO | TRACE_CATEGORY_THERMAL
+#endif  // TRACE_CATEGORIES_H
diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/tracing_perfetto/include/trace_result.h
new file mode 100644
index 0000000..f7581fc
--- /dev/null
+++ b/libs/tracing_perfetto/include/trace_result.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 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 TRACE_RESULT_H
+#define TRACE_RESULT_H
+
+namespace tracing_perfetto {
+
+enum class Result {
+  SUCCESS,
+  NOT_SUPPORTED,
+  INVALID_INPUT,
+};
+
+}
+
+#endif  // TRACE_RESULT_H
diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h
new file mode 100644
index 0000000..4e3c83f
--- /dev/null
+++ b/libs/tracing_perfetto/include/tracing_perfetto.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 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 TRACING_PERFETTO_H
+#define TRACING_PERFETTO_H
+
+#include <stdint.h>
+
+#include "trace_result.h"
+
+namespace tracing_perfetto {
+
+void registerWithPerfetto(bool test = false);
+
+Result traceBegin(uint64_t category, const char* name);
+
+Result traceEnd(uint64_t category);
+
+Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
+
+Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
+
+Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+                               const char* trackName, int32_t cookie);
+
+Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+                             int32_t cookie);
+
+Result traceInstant(uint64_t category, const char* name);
+
+Result traceInstantForTrack(uint64_t category, const char* trackName,
+                            const char* name);
+
+Result traceCounter(uint64_t category, const char* name, int64_t value);
+
+uint64_t getEnabledCategories();
+
+}  // namespace tracing_perfetto
+
+#endif  // TRACING_PERFETTO_H
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
new file mode 100644
index 0000000..a35b0e0
--- /dev/null
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright 2024 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+    name: "libtracing_perfetto_tests",
+    static_libs: [
+        "libflagtest",
+        "libgmock",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "android.os.flags-aconfig-cc-host",
+        "libbase",
+        "libperfetto_c",
+        "libtracing_perfetto",
+    ],
+    srcs: [
+        "tracing_perfetto_test.cpp",
+        "utils.cpp",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
new file mode 100644
index 0000000..7716b9a
--- /dev/null
+++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 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 "tracing_perfetto.h"
+
+#include <thread>
+
+#include <android_os.h>
+#include <flag_macros.h>
+
+#include "gtest/gtest.h"
+#include "perfetto/public/abi/data_source_abi.h"
+#include "perfetto/public/abi/heap_buffer.h"
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/abi/tracing_session_abi.h"
+#include "perfetto/public/abi/track_event_abi.h"
+#include "perfetto/public/data_source.h"
+#include "perfetto/public/pb_decoder.h"
+#include "perfetto/public/producer.h"
+#include "perfetto/public/protos/config/trace_config.pzc.h"
+#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h"
+#include "perfetto/public/protos/trace/test_event.pzc.h"
+#include "perfetto/public/protos/trace/trace.pzc.h"
+#include "perfetto/public/protos/trace/trace_packet.pzc.h"
+#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h"
+#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h"
+#include "perfetto/public/protos/trace/track_event/track_event.pzc.h"
+#include "perfetto/public/protos/trace/trigger.pzc.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "trace_categories.h"
+#include "utils.h"
+
+namespace tracing_perfetto {
+
+using ::perfetto::shlib::test_utils::AllFieldsWithId;
+using ::perfetto::shlib::test_utils::FieldView;
+using ::perfetto::shlib::test_utils::IdFieldView;
+using ::perfetto::shlib::test_utils::MsgField;
+using ::perfetto::shlib::test_utils::PbField;
+using ::perfetto::shlib::test_utils::StringField;
+using ::perfetto::shlib::test_utils::TracingSession;
+using ::perfetto::shlib::test_utils::VarIntField;
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::UnorderedElementsAre;
+
+const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing);
+
+class TracingPerfettoTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    tracing_perfetto::registerWithPerfetto(true /* test */);
+  }
+};
+
+// TODO(b/303199244): Add tests for all the library functions.
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant,
+                  REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+  TracingSession tracing_session =
+      TracingSession::Builder().set_data_source_name("track_event").Build();
+  tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, "");
+
+  tracing_session.StopBlocking();
+  std::vector<uint8_t> data = tracing_session.ReadBlocking();
+  bool found = false;
+  for (struct PerfettoPbDecoderField trace_field : FieldView(data)) {
+    ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number,
+                                     MsgField(_)));
+    IdFieldView track_event(
+        trace_field, perfetto_protos_TracePacket_track_event_field_number);
+    if (track_event.size() == 0) {
+      continue;
+    }
+    found = true;
+    IdFieldView cat_iid_fields(
+        track_event.front(),
+        perfetto_protos_TrackEvent_category_iids_field_number);
+    ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_)));
+    uint64_t cat_iid = cat_iid_fields.front().value.integer64;
+    EXPECT_THAT(
+        trace_field,
+        AllFieldsWithId(
+            perfetto_protos_TracePacket_interned_data_field_number,
+            ElementsAre(AllFieldsWithId(
+                perfetto_protos_InternedData_event_categories_field_number,
+                ElementsAre(MsgField(UnorderedElementsAre(
+                    PbField(perfetto_protos_EventCategory_iid_field_number,
+                            VarIntField(cat_iid)),
+                    PbField(perfetto_protos_EventCategory_name_field_number,
+                            StringField("input")))))))));
+  }
+  EXPECT_TRUE(found);
+}
+
+}  // namespace tracing_perfetto
\ No newline at end of file
diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp
new file mode 100644
index 0000000..9c42028
--- /dev/null
+++ b/libs/tracing_perfetto/tests/utils.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2024 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.
+ */
+
+// Copied from //external/perfetto/src/shared_lib/test/utils.cc
+
+#include "utils.h"
+
+#include "perfetto/public/abi/heap_buffer.h"
+#include "perfetto/public/pb_msg.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/protos/config/data_source_config.pzc.h"
+#include "perfetto/public/protos/config/trace_config.pzc.h"
+#include "perfetto/public/protos/config/track_event/track_event_config.pzc.h"
+#include "perfetto/public/tracing_session.h"
+
+namespace perfetto {
+namespace shlib {
+namespace test_utils {
+namespace {
+
+std::string ToHexChars(uint8_t val) {
+  std::string ret;
+  uint8_t high_nibble = (val & 0xF0) >> 4;
+  uint8_t low_nibble = (val & 0xF);
+  static const char hex_chars[] = "0123456789ABCDEF";
+  ret.push_back(hex_chars[high_nibble]);
+  ret.push_back(hex_chars[low_nibble]);
+  return ret;
+}
+
+}  // namespace
+
+TracingSession TracingSession::Builder::Build() {
+  struct PerfettoPbMsgWriter writer;
+  struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer);
+
+  struct perfetto_protos_TraceConfig cfg;
+  PerfettoPbMsgInit(&cfg.msg, &writer);
+
+  {
+    struct perfetto_protos_TraceConfig_BufferConfig buffers;
+    perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers);
+
+    perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024);
+
+    perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers);
+  }
+
+  {
+    struct perfetto_protos_TraceConfig_DataSource data_sources;
+    perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources);
+
+    {
+      struct perfetto_protos_DataSourceConfig ds_cfg;
+      perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources,
+                                                          &ds_cfg);
+
+      perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg,
+                                                     data_source_name_.c_str());
+      if (!enabled_categories_.empty() && !disabled_categories_.empty()) {
+        perfetto_protos_TrackEventConfig te_cfg;
+        perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg,
+                                                                  &te_cfg);
+        for (const std::string& cat : enabled_categories_) {
+          perfetto_protos_TrackEventConfig_set_enabled_categories(
+              &te_cfg, cat.data(), cat.size());
+        }
+        for (const std::string& cat : disabled_categories_) {
+          perfetto_protos_TrackEventConfig_set_disabled_categories(
+              &te_cfg, cat.data(), cat.size());
+        }
+        perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg,
+                                                                &te_cfg);
+      }
+
+      perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg);
+    }
+
+    perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources);
+  }
+  size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer);
+  std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]);
+  PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size);
+  PerfettoHeapBufferDestroy(hb, &writer.writer);
+
+  struct PerfettoTracingSessionImpl* ts =
+      PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS);
+
+  PerfettoTracingSessionSetup(ts, ser.get(), cfg_size);
+
+  PerfettoTracingSessionStartBlocking(ts);
+
+  return TracingSession::Adopt(ts);
+}
+
+TracingSession TracingSession::Adopt(struct PerfettoTracingSessionImpl* session) {
+  TracingSession ret;
+  ret.session_ = session;
+  ret.stopped_ = std::make_unique<WaitableEvent>();
+  PerfettoTracingSessionSetStopCb(
+      ret.session_,
+      [](struct PerfettoTracingSessionImpl*, void* arg) {
+        static_cast<WaitableEvent*>(arg)->Notify();
+      },
+      ret.stopped_.get());
+  return ret;
+}
+
+TracingSession::TracingSession(TracingSession&& other) noexcept {
+  session_ = other.session_;
+  other.session_ = nullptr;
+  stopped_ = std::move(other.stopped_);
+  other.stopped_ = nullptr;
+}
+
+TracingSession::~TracingSession() {
+  if (!session_) {
+    return;
+  }
+  if (!stopped_->IsNotified()) {
+    PerfettoTracingSessionStopBlocking(session_);
+    stopped_->WaitForNotification();
+  }
+  PerfettoTracingSessionDestroy(session_);
+}
+
+bool TracingSession::FlushBlocking(uint32_t timeout_ms) {
+  WaitableEvent notification;
+  bool result;
+  auto* cb = new std::function<void(bool)>([&](bool success) {
+    result = success;
+    notification.Notify();
+  });
+  PerfettoTracingSessionFlushAsync(
+      session_, timeout_ms,
+      [](PerfettoTracingSessionImpl*, bool success, void* user_arg) {
+        auto* f = reinterpret_cast<std::function<void(bool)>*>(user_arg);
+        (*f)(success);
+        delete f;
+      },
+      cb);
+  notification.WaitForNotification();
+  return result;
+}
+
+void TracingSession::WaitForStopped() {
+  stopped_->WaitForNotification();
+}
+
+void TracingSession::StopBlocking() {
+  PerfettoTracingSessionStopBlocking(session_);
+}
+
+std::vector<uint8_t> TracingSession::ReadBlocking() {
+  std::vector<uint8_t> data;
+  PerfettoTracingSessionReadTraceBlocking(
+      session_,
+      [](struct PerfettoTracingSessionImpl*, const void* trace_data,
+         size_t size, bool, void* user_arg) {
+        auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg);
+        auto* src = static_cast<const uint8_t*>(trace_data);
+        dst.insert(dst.end(), src, src + size);
+      },
+      &data);
+  return data;
+}
+
+}  // namespace test_utils
+}  // namespace shlib
+}  // namespace perfetto
+
+void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) {
+  std::ostream& os = *pos;
+  PerfettoPbDecoderStatus status =
+      static_cast<PerfettoPbDecoderStatus>(field.status);
+  switch (status) {
+    case PERFETTO_PB_DECODER_ERROR:
+      os << "MALFORMED PROTOBUF";
+      break;
+    case PERFETTO_PB_DECODER_DONE:
+      os << "DECODER DONE";
+      break;
+    case PERFETTO_PB_DECODER_OK:
+      switch (field.wire_type) {
+        case PERFETTO_PB_WIRE_TYPE_DELIMITED:
+          os << "\"";
+          for (size_t i = 0; i < field.value.delimited.len; i++) {
+            os << perfetto::shlib::test_utils::ToHexChars(
+                      field.value.delimited.start[i])
+               << " ";
+          }
+          os << "\"";
+          break;
+        case PERFETTO_PB_WIRE_TYPE_VARINT:
+          os << "varint: " << field.value.integer64;
+          break;
+        case PERFETTO_PB_WIRE_TYPE_FIXED32:
+          os << "fixed32: " << field.value.integer32;
+          break;
+        case PERFETTO_PB_WIRE_TYPE_FIXED64:
+          os << "fixed64: " << field.value.integer64;
+          break;
+      }
+      break;
+  }
+}
diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h
new file mode 100644
index 0000000..4353554
--- /dev/null
+++ b/libs/tracing_perfetto/tests/utils.h
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2024 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.
+ */
+
+// Copied from //external/perfetto/src/shared_lib/test/utils.h
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <cassert>
+#include <condition_variable>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-matchers.h"
+#include "gtest/gtest-matchers.h"
+#include "gtest/gtest.h"
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/tracing_session.h"
+
+// Pretty printer for gtest
+void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);
+
+namespace perfetto {
+namespace shlib {
+namespace test_utils {
+
+class WaitableEvent {
+ public:
+  WaitableEvent() = default;
+  void Notify() {
+    std::unique_lock<std::mutex> lock(m_);
+    notified_ = true;
+    cv_.notify_one();
+  }
+  bool WaitForNotification() {
+    std::unique_lock<std::mutex> lock(m_);
+    cv_.wait(lock, [this] { return notified_; });
+    return notified_;
+  }
+  bool IsNotified() {
+    std::unique_lock<std::mutex> lock(m_);
+    return notified_;
+  }
+
+ private:
+  std::mutex m_;
+  std::condition_variable cv_;
+  bool notified_ = false;
+};
+
+class TracingSession {
+ public:
+  class Builder {
+   public:
+    Builder() = default;
+    Builder& set_data_source_name(std::string data_source_name) {
+      data_source_name_ = std::move(data_source_name);
+      return *this;
+    }
+    Builder& add_enabled_category(std::string category) {
+      enabled_categories_.push_back(std::move(category));
+      return *this;
+    }
+    Builder& add_disabled_category(std::string category) {
+      disabled_categories_.push_back(std::move(category));
+      return *this;
+    }
+    TracingSession Build();
+
+   private:
+    std::string data_source_name_;
+    std::vector<std::string> enabled_categories_;
+    std::vector<std::string> disabled_categories_;
+  };
+
+  static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
+
+  TracingSession(TracingSession&&) noexcept;
+
+  ~TracingSession();
+
+  struct PerfettoTracingSessionImpl* session() const {
+    return session_;
+  }
+
+  bool FlushBlocking(uint32_t timeout_ms);
+  void WaitForStopped();
+  void StopBlocking();
+  std::vector<uint8_t> ReadBlocking();
+
+ private:
+  TracingSession() = default;
+  struct PerfettoTracingSessionImpl* session_;
+  std::unique_ptr<WaitableEvent> stopped_;
+};
+
+template <typename FieldSkipper>
+class FieldViewBase {
+ public:
+  class Iterator {
+   public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = const PerfettoPbDecoderField;
+    using pointer = value_type;
+    using reference = value_type;
+    reference operator*() const {
+      struct PerfettoPbDecoder decoder;
+      decoder.read_ptr = read_ptr_;
+      decoder.end_ptr = end_ptr_;
+      struct PerfettoPbDecoderField field;
+      do {
+        field = PerfettoPbDecoderParseField(&decoder);
+      } while (field.status == PERFETTO_PB_DECODER_OK &&
+               skipper_.ShouldSkip(field));
+      return field;
+    }
+    Iterator& operator++() {
+      struct PerfettoPbDecoder decoder;
+      decoder.read_ptr = read_ptr_;
+      decoder.end_ptr = end_ptr_;
+      PerfettoPbDecoderSkipField(&decoder);
+      read_ptr_ = decoder.read_ptr;
+      AdvanceToFirstInterestingField();
+      return *this;
+    }
+    Iterator operator++(int) {
+      Iterator tmp = *this;
+      ++(*this);
+      return tmp;
+    }
+
+    friend bool operator==(const Iterator& a, const Iterator& b) {
+      return a.read_ptr_ == b.read_ptr_;
+    }
+    friend bool operator!=(const Iterator& a, const Iterator& b) {
+      return a.read_ptr_ != b.read_ptr_;
+    }
+
+   private:
+    Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr,
+             const FieldSkipper& skipper)
+        : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) {
+      AdvanceToFirstInterestingField();
+    }
+    void AdvanceToFirstInterestingField() {
+      struct PerfettoPbDecoder decoder;
+      decoder.read_ptr = read_ptr_;
+      decoder.end_ptr = end_ptr_;
+      struct PerfettoPbDecoderField field;
+      const uint8_t* prev_read_ptr;
+      do {
+        prev_read_ptr = decoder.read_ptr;
+        field = PerfettoPbDecoderParseField(&decoder);
+      } while (field.status == PERFETTO_PB_DECODER_OK &&
+               skipper_.ShouldSkip(field));
+      if (field.status == PERFETTO_PB_DECODER_OK) {
+        read_ptr_ = prev_read_ptr;
+      } else {
+        read_ptr_ = decoder.read_ptr;
+      }
+    }
+    friend class FieldViewBase<FieldSkipper>;
+    const uint8_t* read_ptr_;
+    const uint8_t* end_ptr_;
+    const FieldSkipper& skipper_;
+  };
+  using value_type = const PerfettoPbDecoderField;
+  using const_iterator = Iterator;
+  template <typename... Args>
+  explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args)
+      : begin_(begin), end_(end), s_(args...) {
+  }
+  template <typename... Args>
+  explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args)
+      : FieldViewBase(data.data(), data.data() + data.size(), args...) {
+  }
+  template <typename... Args>
+  explicit FieldViewBase(const struct PerfettoPbDecoderField& field,
+                         Args... args)
+      : s_(args...) {
+    if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) {
+      abort();
+    }
+    begin_ = field.value.delimited.start;
+    end_ = begin_ + field.value.delimited.len;
+  }
+  Iterator begin() const {
+    return Iterator(begin_, end_, s_);
+  }
+  Iterator end() const {
+    return Iterator(end_, end_, s_);
+  }
+  PerfettoPbDecoderField front() const {
+    return *begin();
+  }
+
+  size_t size() const {
+    size_t count = 0;
+    for (auto field : *this) {
+      (void)field;
+      count++;
+    }
+    return count;
+  }
+
+  bool ok() const {
+    for (auto field : *this) {
+      if (field.status != PERFETTO_PB_DECODER_OK) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  const uint8_t* begin_;
+  const uint8_t* end_;
+  FieldSkipper s_;
+};
+
+// Pretty printer for gtest
+template <typename FieldSkipper>
+void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) {
+  std::ostream& os = *pos;
+  os << "{";
+  for (PerfettoPbDecoderField f : field_view) {
+    PrintTo(f, pos);
+    os << ", ";
+  }
+  os << "}";
+}
+
+class IdFieldSkipper {
+ public:
+  explicit IdFieldSkipper(uint32_t id) : id_(id) {
+  }
+  explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) {
+  }
+  bool ShouldSkip(const struct PerfettoPbDecoderField& field) const {
+    return field.id != id_;
+  }
+
+ private:
+  uint32_t id_;
+};
+
+class NoFieldSkipper {
+ public:
+  NoFieldSkipper() = default;
+  bool ShouldSkip(const struct PerfettoPbDecoderField&) const {
+    return false;
+  }
+};
+
+// View over all the fields of a contiguous serialized protobuf message.
+//
+// Examples:
+//
+// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) {
+//   //...
+// }
+// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field);
+// FieldView fields3(/*std::vector<uint8_t>*/ data);
+// size_t num = fields1.size(); // The number of fields.
+// bool ok = fields1.ok(); // Checks that the message is not malformed.
+using FieldView = FieldViewBase<NoFieldSkipper>;
+
+// Like `FieldView`, but only considers fields with a specific id.
+//
+// Examples:
+//
+// IdFieldView fields(msg_begin, msg_end, id)
+using IdFieldView = FieldViewBase<IdFieldSkipper>;
+
+// Matches a PerfettoPbDecoderField with the specified id. Accepts another
+// matcher to match the contents of the field.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, PbField(900, VarIntField(5)));
+template <typename M>
+auto PbField(int32_t id, M m) {
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::id, id), m);
+}
+
+// Matches a PerfettoPbDecoderField submessage field. Accepts a container
+// matcher for the subfields.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, MsgField(ElementsAre(...)));
+template <typename M>
+auto MsgField(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_DELIMITED),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField length delimited field. Accepts a string
+// matcher.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, StringField("string"));
+template <typename M>
+auto StringField(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) {
+    return std::string(
+        reinterpret_cast<const char*>(field.value.delimited.start),
+        field.value.delimited.len);
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_DELIMITED),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, VarIntField(1)));
+template <typename M>
+auto VarIntField(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) {
+    return field.value.integer64;
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_VARINT),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, Fixed64Field(1)));
+template <typename M>
+auto Fixed64Field(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) {
+    return field.value.integer64;
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_FIXED64),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, Fixed32Field(1)));
+template <typename M>
+auto Fixed32Field(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) {
+    return field.value.integer32;
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_FIXED32),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField double field. Accepts a double matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, DoubleField(1.0)));
+template <typename M>
+auto DoubleField(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) {
+    return field.value.double_val;
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_FIXED64),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField float field. Accepts a float matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, FloatField(1.0)));
+template <typename M>
+auto FloatField(M m) {
+  auto f = [](const PerfettoPbDecoderField& field) {
+    return field.value.float_val;
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_FIXED32),
+      testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField submessage field. Accepts a container
+// matcher for the subfields.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...)));
+template <typename M>
+auto AllFieldsWithId(int32_t id, M m) {
+  auto f = [id](const PerfettoPbDecoderField& field) {
+    return IdFieldView(field, id);
+  };
+  return testing::AllOf(
+      testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+      testing::Field(&PerfettoPbDecoderField::wire_type,
+                     PERFETTO_PB_WIRE_TYPE_DELIMITED),
+      testing::ResultOf(f, m));
+}
+
+}  // namespace test_utils
+}  // namespace shlib
+}  // namespace perfetto
+
+#endif  // UTILS_H
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
new file mode 100644
index 0000000..c7fb8bd
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2024 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 "tracing_perfetto.h"
+
+#include <cutils/trace.h>
+
+#include "perfetto/public/te_category_macros.h"
+#include "trace_categories.h"
+#include "tracing_perfetto_internal.h"
+
+namespace tracing_perfetto {
+
+void registerWithPerfetto(bool test) {
+  internal::registerWithPerfetto(test);
+}
+
+Result traceBegin(uint64_t category, const char* name) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceBegin(*perfettoTeCategory, name);
+  } else {
+    atrace_begin(category, name);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceEnd(uint64_t category) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceEnd(*perfettoTeCategory);
+  } else {
+    atrace_end(category);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie);
+  } else {
+    atrace_async_begin(category, name, cookie);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie);
+  } else {
+    atrace_async_end(category, name, cookie);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+                               const char* trackName, int32_t cookie) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
+  } else {
+    atrace_async_for_track_begin(category, trackName, name, cookie);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+                             int32_t cookie) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
+  } else {
+    atrace_async_for_track_end(category, trackName, cookie);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceInstant(uint64_t category, const char* name) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceInstant(*perfettoTeCategory, name);
+  } else {
+    atrace_instant(category, name);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceInstantForTrack(uint64_t category, const char* trackName,
+                            const char* name) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name);
+  } else {
+    atrace_instant_for_track(category, trackName, name);
+    return Result::SUCCESS;
+  }
+}
+
+Result traceCounter(uint64_t category, const char* name, int64_t value) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  if (perfettoTeCategory != nullptr) {
+    return internal::perfettoTraceCounter(*perfettoTeCategory, name, value);
+  } else {
+    atrace_int64(category, name, value);
+    return Result::SUCCESS;
+  }
+}
+
+uint64_t getEnabledCategories() {
+  if (internal::isPerfettoSdkTracingEnabled()) {
+    return internal::getDefaultCategories();
+  } else {
+    return atrace_get_enabled_tags();
+  }
+}
+
+}  // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
new file mode 100644
index 0000000..58ba428
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#define FRAMEWORK_CATEGORIES(C)                                  \
+  C(always, "always", "Always category")                         \
+  C(graphics, "graphics", "Graphics category")                   \
+  C(input, "input", "Input category")                            \
+  C(view, "view", "View category")                               \
+  C(webview, "webview", "WebView category")                      \
+  C(windowmanager, "wm", "WindowManager category")               \
+  C(activitymanager, "am", "ActivityManager category")           \
+  C(syncmanager, "syncmanager", "SyncManager category")          \
+  C(audio, "audio", "Audio category")                            \
+  C(video, "video", "Video category")                            \
+  C(camera, "camera", "Camera category")                         \
+  C(hal, "hal", "HAL category")                                  \
+  C(app, "app", "App category")                                  \
+  C(resources, "res", "Resources category")                      \
+  C(dalvik, "dalvik", "Dalvik category")                         \
+  C(rs, "rs", "RS category")                                     \
+  C(bionic, "bionic", "Bionic category")                         \
+  C(power, "power", "Power category")                            \
+  C(packagemanager, "packagemanager", "PackageManager category") \
+  C(systemserver, "ss", "System Server category")                \
+  C(database, "database", "Database category")                   \
+  C(network, "network", "Network category")                      \
+  C(adb, "adb", "ADB category")                                  \
+  C(vibrator, "vibrator", "Vibrator category")                   \
+  C(aidl, "aidl", "AIDL category")                               \
+  C(nnapi, "nnapi", "NNAPI category")                            \
+  C(rro, "rro", "RRO category")                                  \
+  C(thermal, "thermal", "Thermal category")
+
+#include "tracing_perfetto_internal.h"
+
+#include <inttypes.h>
+
+#include <mutex>
+
+#include <android_os.h>
+
+#include "perfetto/public/compiler.h"
+#include "perfetto/public/producer.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "trace_categories.h"
+#include "trace_result.h"
+
+namespace tracing_perfetto {
+
+namespace internal {
+
+namespace {
+
+PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES);
+
+PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES);
+
+struct PerfettoTeCategory* toCategory(uint64_t inCategory) {
+  switch (inCategory) {
+    case TRACE_CATEGORY_ALWAYS:
+      return &always;
+    case TRACE_CATEGORY_GRAPHICS:
+      return &graphics;
+    case TRACE_CATEGORY_INPUT:
+      return &input;
+    case TRACE_CATEGORY_VIEW:
+      return &view;
+    case TRACE_CATEGORY_WEBVIEW:
+      return &webview;
+    case TRACE_CATEGORY_WINDOW_MANAGER:
+      return &windowmanager;
+    case TRACE_CATEGORY_ACTIVITY_MANAGER:
+      return &activitymanager;
+    case TRACE_CATEGORY_SYNC_MANAGER:
+      return &syncmanager;
+    case TRACE_CATEGORY_AUDIO:
+      return &audio;
+    case TRACE_CATEGORY_VIDEO:
+      return &video;
+    case TRACE_CATEGORY_CAMERA:
+      return &camera;
+    case TRACE_CATEGORY_HAL:
+      return &hal;
+    case TRACE_CATEGORY_APP:
+      return &app;
+    case TRACE_CATEGORY_RESOURCES:
+      return &resources;
+    case TRACE_CATEGORY_DALVIK:
+      return &dalvik;
+    case TRACE_CATEGORY_RS:
+      return &rs;
+    case TRACE_CATEGORY_BIONIC:
+      return &bionic;
+    case TRACE_CATEGORY_POWER:
+      return &power;
+    case TRACE_CATEGORY_PACKAGE_MANAGER:
+      return &packagemanager;
+    case TRACE_CATEGORY_SYSTEM_SERVER:
+      return &systemserver;
+    case TRACE_CATEGORY_DATABASE:
+      return &database;
+    case TRACE_CATEGORY_NETWORK:
+      return &network;
+    case TRACE_CATEGORY_ADB:
+      return &adb;
+    case TRACE_CATEGORY_VIBRATOR:
+      return &vibrator;
+    case TRACE_CATEGORY_AIDL:
+      return &aidl;
+    case TRACE_CATEGORY_NNAPI:
+      return &nnapi;
+    case TRACE_CATEGORY_RRO:
+      return &rro;
+    case TRACE_CATEGORY_THERMAL:
+      return &thermal;
+    default:
+      return nullptr;
+  }
+}
+
+}  // namespace
+
+bool isPerfettoSdkTracingEnabled() {
+  return android::os::perfetto_sdk_tracing();
+}
+
+struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) {
+  if (!isPerfettoSdkTracingEnabled()) {
+    return nullptr;
+  }
+
+  struct PerfettoTeCategory* perfettoCategory = toCategory(category);
+  bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
+      (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+  return enabled ? perfettoCategory : nullptr;
+}
+
+void registerWithPerfetto(bool test) {
+  if (!isPerfettoSdkTracingEnabled()) {
+    return;
+  }
+  static std::once_flag registration;
+  std::call_once(registration, [test]() {
+    struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
+    args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM;
+    PerfettoProducerInit(args);
+    PerfettoTeInit();
+    PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
+  });
+}
+
+Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) {
+  PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name));
+  return Result::SUCCESS;
+}
+
+Result perfettoTraceEnd(const struct PerfettoTeCategory& category) {
+  PERFETTO_TE(category, PERFETTO_TE_SLICE_END());
+  return Result::SUCCESS;
+}
+
+Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+                                       const char* trackName, uint64_t cookie) {
+  PERFETTO_TE(
+      category, PERFETTO_TE_SLICE_BEGIN(name),
+      PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+  return Result::SUCCESS;
+}
+
+Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+                                     const char* trackName, uint64_t cookie) {
+  PERFETTO_TE(
+      category, PERFETTO_TE_SLICE_END(),
+      PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+  return Result::SUCCESS;
+}
+
+Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+                               uint64_t cookie) {
+  return perfettoTraceAsyncBeginForTrack(category, name, name, cookie);
+}
+
+Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+                             uint64_t cookie) {
+  return perfettoTraceAsyncEndForTrack(category, name, cookie);
+}
+
+Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) {
+  PERFETTO_TE(category, PERFETTO_TE_INSTANT(name));
+  return Result::SUCCESS;
+}
+
+Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+                                    const char* trackName, const char* name) {
+  PERFETTO_TE(
+      category, PERFETTO_TE_INSTANT(name),
+      PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid()));
+  return Result::SUCCESS;
+}
+
+Result perfettoTraceCounter(const struct PerfettoTeCategory& category,
+                            [[maybe_unused]] const char* name, int64_t value) {
+  PERFETTO_TE(category, PERFETTO_TE_COUNTER(),
+              PERFETTO_TE_INT_COUNTER(value));
+  return Result::SUCCESS;
+}
+
+uint64_t getDefaultCategories() {
+  return TRACE_CATEGORIES;
+}
+
+}  // namespace internal
+
+}  // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
new file mode 100644
index 0000000..9a579f1
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 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 TRACING_PERFETTO_INTERNAL_H
+#define TRACING_PERFETTO_INTERNAL_H
+
+#include <stdint.h>
+
+#include "include/trace_result.h"
+#include "perfetto/public/te_category_macros.h"
+
+namespace tracing_perfetto {
+
+namespace internal {
+
+bool isPerfettoSdkTracingEnabled();
+
+struct PerfettoTeCategory* toPerfettoCategory(uint64_t category);
+
+void registerWithPerfetto(bool test = false);
+
+Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name);
+
+Result perfettoTraceEnd(const struct PerfettoTeCategory& category);
+
+Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+                               uint64_t cookie);
+
+Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+                             uint64_t cookie);
+
+Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+                                       const char* trackName, uint64_t cookie);
+
+Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+                                     const char* trackName, uint64_t cookie);
+
+Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name);
+
+Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+                                    const char* trackName, const char* name);
+
+Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name,
+                            int64_t value);
+
+uint64_t getDefaultCategories();
+
+}  // namespace internal
+
+}  // namespace tracing_perfetto
+
+#endif  // TRACING_PERFETTO_INTERNAL_H