Add data source parsing packages.list.

Bug: 123186697

Change-Id: Ie446b8e96fddc77d8087605c27b4134a1ced59f5
diff --git a/Android.bp b/Android.bp
index a6492fb..85af40d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -270,6 +270,7 @@
     "src/traced/probes/ftrace/ftrace_stats.cc",
     "src/traced/probes/ftrace/page_pool.cc",
     "src/traced/probes/ftrace/proto_translation_table.cc",
+    "src/traced/probes/packages_list/packages_list_data_source.cc",
     "src/traced/probes/power/android_power_data_source.cc",
     "src/traced/probes/probes.cc",
     "src/traced/probes/probes_data_source.cc",
@@ -636,6 +637,7 @@
     "src/traced/probes/ftrace/page_pool.cc",
     "src/traced/probes/ftrace/proto_translation_table.cc",
     "src/traced/probes/ftrace/test/cpu_reader_support.cc",
+    "src/traced/probes/packages_list/packages_list_data_source.cc",
     "src/traced/probes/power/android_power_data_source.cc",
     "src/traced/probes/probes_data_source.cc",
     "src/traced/probes/probes_producer.cc",
@@ -1038,6 +1040,7 @@
   name: "perfetto_protos_perfetto_trace_android_lite_gen",
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
+    "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
     "aprotoc",
@@ -1045,6 +1048,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pb.cc",
+    "external/perfetto/protos/perfetto/trace/android/packages_list.pb.cc",
   ],
 }
 
@@ -1053,6 +1057,7 @@
   name: "perfetto_protos_perfetto_trace_android_lite_gen_headers",
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
+    "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
     "aprotoc",
@@ -1060,6 +1065,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pb.h",
+    "external/perfetto/protos/perfetto/trace/android/packages_list.pb.h",
   ],
   export_include_dirs: [
     "protos",
@@ -1071,6 +1077,7 @@
   name: "perfetto_protos_perfetto_trace_android_zero_gen",
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
+    "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
     "aprotoc",
@@ -1079,6 +1086,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pbzero.cc",
+    "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.cc",
   ],
 }
 
@@ -1087,6 +1095,7 @@
   name: "perfetto_protos_perfetto_trace_android_zero_gen_headers",
   srcs: [
     "protos/perfetto/trace/android/android_log.proto",
+    "protos/perfetto/trace/android/packages_list.proto",
   ],
   tools: [
     "aprotoc",
@@ -1095,6 +1104,7 @@
   cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
   out: [
     "external/perfetto/protos/perfetto/trace/android/android_log.pbzero.h",
+    "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.h",
   ],
   export_include_dirs: [
     "protos",
@@ -2954,6 +2964,8 @@
     "src/traced/probes/ftrace/proto_translation_table.cc",
     "src/traced/probes/ftrace/proto_translation_table_unittest.cc",
     "src/traced/probes/ftrace/test/cpu_reader_support.cc",
+    "src/traced/probes/packages_list/packages_list_data_source.cc",
+    "src/traced/probes/packages_list/packages_list_data_source_unittest.cc",
     "src/traced/probes/power/android_power_data_source.cc",
     "src/traced/probes/probes_data_source.cc",
     "src/traced/probes/probes_producer.cc",
diff --git a/protos/BUILD b/protos/BUILD
index faccfe6..9a780bd 100644
--- a/protos/BUILD
+++ b/protos/BUILD
@@ -28,6 +28,7 @@
     name = "android",
     srcs = [
         "perfetto/trace/android/android_log.proto",
+        "perfetto/trace/android/packages_list.proto",
     ],
     has_services = 1,
     cc_api_version = 2,
@@ -56,6 +57,7 @@
     name = "android_zero",
     srcs = [
         "perfetto/trace/android/android_log.proto",
+        "perfetto/trace/android/packages_list.proto",
     ],
     deps = [
         "//third_party/perfetto/protos:common_zero",
diff --git a/protos/perfetto/trace/android/BUILD.gn b/protos/perfetto/trace/android/BUILD.gn
index 020f3d2..72a3e3c 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -16,7 +16,10 @@
 import("../../../../gn/proto_library.gni")
 import("../../../../gn/protozero_library.gni")
 
-android_proto_names = [ "android_log.proto" ]
+android_proto_names = [
+  "android_log.proto",
+  "packages_list.proto",
+]
 
 proto_library("lite") {
   deps = [
diff --git a/protos/perfetto/trace/android/packages_list.proto b/protos/perfetto/trace/android/packages_list.proto
new file mode 100644
index 0000000..6412ea5
--- /dev/null
+++ b/protos/perfetto/trace/android/packages_list.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package perfetto.protos;
+
+message PackagesList {
+  message Package {
+    optional string name = 1;
+    optional uint64 uid = 2;
+    optional bool debuggable = 3;
+    optional bool profileable_from_shell = 4;
+    optional int64 version_code = 5;
+  }
+
+  repeated Package packages = 1;
+
+  // At least one error occured parsing the packages.list.
+  optional bool error = 2;
+}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 5362bbb..16f48e2 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -240,6 +240,25 @@
 
 // End of protos/perfetto/trace/android/android_log.proto
 
+// Begin of protos/perfetto/trace/android/packages_list.proto
+
+message PackagesList {
+  message Package {
+    optional string name = 1;
+    optional uint64 uid = 2;
+    optional bool debuggable = 3;
+    optional bool profileable_from_shell = 4;
+    optional int64 version_code = 5;
+  }
+
+  repeated Package packages = 1;
+
+  // At least one error occured parsing the packages.list.
+  optional bool error = 2;
+}
+
+// End of protos/perfetto/trace/android/packages_list.proto
+
 // Begin of protos/perfetto/trace/clock_snapshot.proto
 
 // A snapshot of clock readings to allow for trace alignment.
@@ -2680,6 +2699,7 @@
     AndroidLogPacket android_log = 39;
     SystemInfo system_info = 45;
     Trigger trigger = 46;
+    PackagesList packages_list = 47;
 
     // Only used by TrackEvent.
     ProcessDescriptor process_descriptor = 43;
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 246cacb..0a4a497 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -20,6 +20,7 @@
 import "perfetto/common/trace_stats.proto";
 import "perfetto/config/trace_config.proto";
 import "perfetto/trace/android/android_log.proto";
+import "perfetto/trace/android/packages_list.proto";
 import "perfetto/trace/chrome/chrome_trace_event.proto";
 import "perfetto/trace/clock_snapshot.proto";
 import "perfetto/trace/filesystem/inode_file_map.proto";
@@ -73,6 +74,7 @@
     AndroidLogPacket android_log = 39;
     SystemInfo system_info = 45;
     Trigger trigger = 46;
+    PackagesList packages_list = 47;
 
     // Only used by TrackEvent.
     ProcessDescriptor process_descriptor = 43;
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 827c570..adc0124 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -40,6 +40,7 @@
     "../../tracing:tracing",
     "android_log",
     "filesystem",
+    "packages_list",
     "power",
     "ps",
     "sys_stats",
@@ -72,6 +73,7 @@
     "../../tracing:test_support",
     "android_log:unittests",
     "filesystem:unittests",
+    "packages_list:unittests",
     "ps:unittests",
     "sys_stats:unittests",
   ]
diff --git a/src/traced/probes/packages_list/BUILD.gn b/src/traced/probes/packages_list/BUILD.gn
new file mode 100644
index 0000000..d896aef
--- /dev/null
+++ b/src/traced/probes/packages_list/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright (C) 2019 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.
+
+source_set("packages_list") {
+  public_deps = [
+    "../../../tracing",
+  ]
+  deps = [
+    "..:data_source",
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/traced",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../base",
+  ]
+  sources = [
+    "packages_list_data_source.cc",
+    "packages_list_data_source.h",
+  ]
+}
+
+source_set("unittests") {
+  testonly = true
+  deps = [
+    ":packages_list",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_deps",
+    "../../../../protos/perfetto/trace:lite",
+    "../../../../src/base:test_support",
+    "../../../../src/tracing:test_support",
+  ]
+  sources = [
+    "packages_list_data_source_unittest.cc",
+  ]
+}
diff --git a/src/traced/probes/packages_list/packages_list_data_source.cc b/src/traced/probes/packages_list/packages_list_data_source.cc
new file mode 100644
index 0000000..bbff20a
--- /dev/null
+++ b/src/traced/probes/packages_list/packages_list_data_source.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 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 "src/traced/probes/packages_list/packages_list_data_source.h"
+
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/string_splitter.h"
+
+#include "perfetto/trace/trace_packet.pbzero.h"
+#include "perfetto/tracing/core/trace_writer.h"
+
+namespace perfetto {
+
+bool ReadPackagesListLine(char* line, Package* package) {
+  size_t idx = 0;
+  for (base::StringSplitter ss(line, ' '); ss.Next();) {
+    switch (idx) {
+      case 0:
+        package->name = std::string(ss.cur_token(), ss.cur_token_size());
+        break;
+      case 1: {
+        char* end;
+        long long uid = strtoll(ss.cur_token(), &end, 10);
+        if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+          PERFETTO_ELOG("Failed to parse packages.list uid.");
+          return false;
+        }
+        package->uid = static_cast<uint64_t>(uid);
+        break;
+      }
+      case 2: {
+        char* end;
+        long long debuggable = strtoll(ss.cur_token(), &end, 10);
+        if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+          PERFETTO_ELOG("Failed to parse packages.list debuggable.");
+          return false;
+        }
+        package->debuggable = debuggable != 0;
+        break;
+      }
+      case 6: {
+        char* end;
+        long long profilable_from_shell = strtoll(ss.cur_token(), &end, 10);
+        if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+          PERFETTO_ELOG("Failed to parse packages.list profilable_from_shell.");
+          return false;
+        }
+        package->profileable_from_shell = profilable_from_shell != 0;
+        break;
+      }
+      case 7: {
+        char* end;
+        long long version_code = strtoll(ss.cur_token(), &end, 10);
+        if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
+          PERFETTO_ELOG("Failed to parse packages.list version_code: %s.",
+                        ss.cur_token());
+          return false;
+        }
+        package->version_code = version_code;
+        break;
+      }
+    }
+    ++idx;
+  }
+  return true;
+}
+
+PackagesListDataSource::PackagesListDataSource(
+    TracingSessionID session_id,
+    std::unique_ptr<TraceWriter> writer)
+    : ProbesDataSource(session_id, kTypeId), writer_(std::move(writer)) {}
+
+void PackagesListDataSource::Start() {
+  base::ScopedFstream fs(fopen("/data/system/packages.list", "r"));
+  auto trace_packet = writer_->NewTracePacket();
+  auto* packages_list_packet = trace_packet->set_packages_list();
+  if (!fs) {
+    PERFETTO_ELOG("Failed to open packages.list");
+    packages_list_packet->set_error(true);
+    trace_packet->Finalize();
+    writer_->Flush();
+    return;
+  }
+  char line[2048];
+  while (fgets(line, sizeof(line), *fs) != nullptr) {
+    Package pkg_struct;
+    if (ReadPackagesListLine(line, &pkg_struct)) {
+      auto* package = packages_list_packet->add_packages();
+      package->set_name(pkg_struct.name.c_str(), pkg_struct.name.size());
+      package->set_uid(pkg_struct.uid);
+      package->set_debuggable(pkg_struct.debuggable);
+      package->set_profileable_from_shell(pkg_struct.profileable_from_shell);
+      package->set_version_code(pkg_struct.version_code);
+    } else {
+      packages_list_packet->set_error(true);
+    }
+  }
+  trace_packet->Finalize();
+  writer_->Flush();
+}
+
+void PackagesListDataSource::Flush(FlushRequestID,
+                                   std::function<void()> callback) {
+  // Flush is no-op. We flush after the first write.
+  callback();
+}
+
+PackagesListDataSource::~PackagesListDataSource() = default;
+
+}  // namespace perfetto
diff --git a/src/traced/probes/packages_list/packages_list_data_source.h b/src/traced/probes/packages_list/packages_list_data_source.h
new file mode 100644
index 0000000..e6026ae
--- /dev/null
+++ b/src/traced/probes/packages_list/packages_list_data_source.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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_TRACED_PROBES_PACKAGES_LIST_PACKAGES_LIST_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_PACKAGES_LIST_PACKAGES_LIST_DATA_SOURCE_H_
+
+#include <functional>
+#include <memory>
+
+#include "perfetto/base/task_runner.h"
+
+#include "perfetto/trace/android/packages_list.pbzero.h"
+#include "perfetto/tracing/core/basic_types.h"
+#include "perfetto/tracing/core/data_source_config.h"
+
+#include "src/traced/probes/probes_data_source.h"
+
+namespace perfetto {
+
+class TraceWriter;
+
+struct Package {
+  std::string name;
+  uint64_t uid = 0;
+  bool debuggable = false;
+  bool profileable_from_shell = false;
+  int64_t version_code = 0;
+};
+
+bool ReadPackagesListLine(char* line, Package* package);
+
+class PackagesListDataSource : public ProbesDataSource {
+ public:
+  static constexpr int kTypeId = 7;
+  PackagesListDataSource(TracingSessionID session_id,
+                         std::unique_ptr<TraceWriter> writer);
+  // ProbesDataSource implementation.
+  void Start() override;
+  void Flush(FlushRequestID, std::function<void()> callback) override;
+
+  ~PackagesListDataSource() override;
+
+ private:
+  std::unique_ptr<TraceWriter> writer_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_PACKAGES_LIST_PACKAGES_LIST_DATA_SOURCE_H_
diff --git a/src/traced/probes/packages_list/packages_list_data_source_unittest.cc b/src/traced/probes/packages_list/packages_list_data_source_unittest.cc
new file mode 100644
index 0000000..2c89000
--- /dev/null
+++ b/src/traced/probes/packages_list/packages_list_data_source_unittest.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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 "src/traced/probes/packages_list/packages_list_data_source.h"
+
+#include <gtest/gtest.h>
+
+namespace perfetto {
+namespace {
+
+TEST(PackagesListDataSourceTest, ParseLineNonProfileNonDebug) {
+  char kLine[] =
+      "com.test.app 1234 0 /data/user/0/com.test.app "
+      "default:targetSdkVersion=12452 1234,5678 0 1111\n";
+  Package pkg;
+  ASSERT_TRUE(ReadPackagesListLine(kLine, &pkg));
+  EXPECT_EQ(pkg.name, "com.test.app");
+  EXPECT_EQ(pkg.debuggable, false);
+  EXPECT_EQ(pkg.profileable_from_shell, false);
+  EXPECT_EQ(pkg.version_code, 1111);
+}
+
+TEST(PackagesListDataSourceTest, ParseLineProfileNonDebug) {
+  char kLine[] =
+      "com.test.app 1234 0 /data/user/0/com.test.app "
+      "default:targetSdkVersion=12452 1234,5678 1 1111\n";
+  Package pkg;
+  ASSERT_TRUE(ReadPackagesListLine(kLine, &pkg));
+  EXPECT_EQ(pkg.name, "com.test.app");
+  EXPECT_EQ(pkg.debuggable, false);
+  EXPECT_EQ(pkg.profileable_from_shell, true);
+  EXPECT_EQ(pkg.version_code, 1111);
+}
+
+TEST(PackagesListDataSourceTest, ParseLineNonProfileDebug) {
+  char kLine[] =
+      "com.test.app 1234 1 /data/user/0/com.test.app "
+      "default:targetSdkVersion=12452 1234,5678 0 1111\n";
+  Package pkg;
+  ASSERT_TRUE(ReadPackagesListLine(kLine, &pkg));
+  EXPECT_EQ(pkg.name, "com.test.app");
+  EXPECT_EQ(pkg.debuggable, true);
+  EXPECT_EQ(pkg.profileable_from_shell, false);
+  EXPECT_EQ(pkg.version_code, 1111);
+}
+
+TEST(PackagesListDataSourceTest, ParseLineProfileDebug) {
+  char kLine[] =
+      "com.test.app 1234 1 /data/user/0/com.test.app "
+      "default:targetSdkVersion=12452 1234,5678 1 1111\n";
+  Package pkg;
+  ASSERT_TRUE(ReadPackagesListLine(kLine, &pkg));
+  EXPECT_EQ(pkg.name, "com.test.app");
+  EXPECT_EQ(pkg.debuggable, true);
+  EXPECT_EQ(pkg.profileable_from_shell, true);
+  EXPECT_EQ(pkg.version_code, 1111);
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 1832fd5..9128ba7 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -36,6 +36,7 @@
 #include "src/traced/probes/android_log/android_log_data_source.h"
 #include "src/traced/probes/filesystem/inode_file_data_source.h"
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
+#include "src/traced/probes/packages_list/packages_list_data_source.h"
 #include "src/traced/probes/power/android_power_data_source.h"
 #include "src/traced/probes/probes_data_source.h"
 #include "src/traced/probes/ps/process_stats_data_source.h"
@@ -61,6 +62,7 @@
 constexpr char kSysStatsSourceName[] = "linux.sys_stats";
 constexpr char kAndroidPowerSourceName[] = "android.power";
 constexpr char kAndroidLogSourceName[] = "android.log";
+constexpr char kPackagesListSourceName[] = "android.packages_list";
 
 }  // namespace.
 
@@ -120,6 +122,12 @@
     desc.set_name(kAndroidLogSourceName);
     endpoint_->RegisterDataSource(desc);
   }
+
+  {
+    DataSourceDescriptor desc;
+    desc.set_name(kPackagesListSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
 }
 
 void ProbesProducer::OnDisconnect() {
@@ -172,6 +180,8 @@
     data_source = CreateAndroidPowerDataSource(session_id, config);
   } else if (config.name() == kAndroidLogSourceName) {
     data_source = CreateAndroidLogDataSource(session_id, config);
+  } else if (config.name() == kPackagesListSourceName) {
+    data_source = CreatePackagesListDataSource(session_id, config);
   }
 
   if (!data_source) {
@@ -282,6 +292,14 @@
                                endpoint_->CreateTraceWriter(buffer_id)));
 }
 
+std::unique_ptr<ProbesDataSource> ProbesProducer::CreatePackagesListDataSource(
+    TracingSessionID session_id,
+    const DataSourceConfig& config) {
+  auto buffer_id = static_cast<BufferID>(config.target_buffer());
+  return std::unique_ptr<ProbesDataSource>(new PackagesListDataSource(
+      session_id, endpoint_->CreateTraceWriter(buffer_id)));
+}
+
 std::unique_ptr<ProbesDataSource> ProbesProducer::CreateSysStatsDataSource(
     TracingSessionID session_id,
     const DataSourceConfig& config) {
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index bbe6b41..e8b3a6c 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -79,6 +79,9 @@
   std::unique_ptr<ProbesDataSource> CreateAndroidLogDataSource(
       TracingSessionID session_id,
       const DataSourceConfig& config);
+  std::unique_ptr<ProbesDataSource> CreatePackagesListDataSource(
+      TracingSessionID session_id,
+      const DataSourceConfig& config);
 
  private:
   enum State {
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index 1696501..f61ae67 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -43,6 +43,7 @@
 
 TRACE_PROTOS = (
   'protos/perfetto/trace/android/android_log.proto',
+  'protos/perfetto/trace/android/packages_list.proto',
   'protos/perfetto/trace/clock_snapshot.proto',
   'protos/perfetto/trace/filesystem/inode_file_map.proto',
   'protos/perfetto/trace/ftrace/binder.proto',
@@ -109,7 +110,7 @@
       content = f.read()
 
     # Remove header
-    header = re.match(r'\/(\*|\/)(?:.|\s)*?package.*;\n', content)
+    header = re.match(r'\/(\*|\/)(?:.|\s)*?package .*;\n', content)
     header = header.group(0)
     content = content[len(header):]
     if merged_content == '':