tp: parse process name, start seq id, and remap process start time

Adds support for process_name, start_seq_id, and start_time_ms to the
android_process_state plugin.

Specifically:
- Adds process_name and start_seq_id columns to
  __intrinsic_android_process_state.
- Overrides TokenizePacket on AndroidProcessStateModule to convert
  start_time_ms to boottime/monotonically-synchronized trace time and
  synthesize chronologically sorted single-record process state dump
  packets before normal packet ingestion.
- Updates main process table name and start_ts from AndroidProcessState
  snapshot records.
diff --git a/src/trace_processor/plugins/android_process_state/android_process_state_module.cc b/src/trace_processor/plugins/android_process_state/android_process_state_module.cc
index 434bff6..560648c 100644
--- a/src/trace_processor/plugins/android_process_state/android_process_state_module.cc
+++ b/src/trace_processor/plugins/android_process_state/android_process_state_module.cc
@@ -21,8 +21,12 @@
 #include "perfetto/base/compiler.h"
 #include "protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_trace_packet.pbzero.h"
 #include "protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_track_event.pbzero.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/blob_packet_writer.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
 #include "src/trace_processor/plugins/android_process_state/android_process_state_tracker.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto::trace_processor::android_process_state {
@@ -46,6 +50,68 @@
 
 AndroidProcessStateModule::~AndroidProcessStateModule() = default;
 
+ModuleResult AndroidProcessStateModule::TokenizePacket(
+    const TokenizePacketArgs& args) {
+  if (args.field.id() !=
+      fb::FrameworksBaseTracePacket::kAndroidProcessStateFieldNumber) {
+    return ModuleResult::Ignored();
+  }
+
+  fb::AndroidProcessStateSnapshot::Decoder dump(
+      args.field.Cast<fb::FrameworksBaseTracePacket::kAndroidProcessState>());
+  for (auto it = dump.record(); it; ++it) {
+    fb::AndroidProcessStateSnapshot::Record::Decoder rec(*it);
+    int64_t real_ts = args.ts;
+    if (rec.has_start_time_ms()) {
+      std::optional<int64_t> start_ts = context_->clock_tracker->ToTraceTime(
+          ClockTracker::ClockId::Machine(
+              protos::pbzero::BUILTIN_CLOCK_BOOTTIME),
+          static_cast<int64_t>(rec.start_time_ms()) * 1000000LL);
+      if (start_ts.has_value()) {
+        real_ts = *start_ts;
+      }
+    }
+
+    TraceBlobView tbv = context_->blob_packet_writer->WritePacket(
+        [&](protos::pbzero::TracePacket* pkt) {
+          pkt->set_timestamp(static_cast<uint64_t>(real_ts));
+          auto* snap = pkt->BeginNestedMessage<fb::AndroidProcessStateSnapshot>(
+              fb::FrameworksBaseTracePacket::kAndroidProcessStateFieldNumber);
+          auto* r = snap->add_record();
+          r->set_pid(rec.pid());
+          if (rec.has_uid()) {
+            r->set_uid(rec.uid());
+          }
+          if (rec.has_proc_state()) {
+            r->set_proc_state(rec.proc_state());
+          }
+          if (rec.has_oom_score()) {
+            r->set_oom_score(rec.oom_score());
+          }
+          if (rec.has_capability_flags()) {
+            r->set_capability_flags(rec.capability_flags());
+          }
+          if (rec.has_process_name()) {
+            r->set_process_name(rec.process_name());
+          }
+          if (rec.has_start_seq_id()) {
+            r->set_start_seq_id(rec.start_seq_id());
+          }
+          if (rec.has_start_time_ms()) {
+            r->set_start_time_ms(rec.start_time_ms());
+          }
+        });
+
+    RefPtr<PacketSequenceStateGeneration> state =
+        args.state ? args.state
+                   : PacketSequenceStateGeneration::CreateFirst(context_);
+    module_context_->trace_packet_stream->Push(
+        real_ts, TracePacketData{std::move(tbv), std::move(state)});
+  }
+
+  return ModuleResult::Handled();
+}
+
 void AndroidProcessStateModule::ParseField(const ParseFieldArgs& args) {
   switch (args.field.id()) {
     case fb::FrameworksBaseTracePacket::kAndroidProcessStateFieldNumber:
diff --git a/src/trace_processor/plugins/android_process_state/android_process_state_module.h b/src/trace_processor/plugins/android_process_state/android_process_state_module.h
index 0c2eefe..de14da3 100644
--- a/src/trace_processor/plugins/android_process_state/android_process_state_module.h
+++ b/src/trace_processor/plugins/android_process_state/android_process_state_module.h
@@ -42,6 +42,7 @@
       tables::AndroidFreezerStateTable* freezer_state_table);
   ~AndroidProcessStateModule() override;
 
+  ModuleResult TokenizePacket(const TokenizePacketArgs& args) override;
   void ParseField(const ParseFieldArgs& args) override;
   void OnEventsFullyExtracted() override;
 
diff --git a/src/trace_processor/plugins/android_process_state/android_process_state_module_unittest.cc b/src/trace_processor/plugins/android_process_state/android_process_state_module_unittest.cc
index bc1ca6a..15730e1 100644
--- a/src/trace_processor/plugins/android_process_state/android_process_state_module_unittest.cc
+++ b/src/trace_processor/plugins/android_process_state/android_process_state_module_unittest.cc
@@ -56,6 +56,18 @@
 using ::com::android::internal::pbzero::FrameworksBaseTracePacket;
 using ::perfetto::protos::pbzero::TracePacket;
 
+class CaptureSink : public TraceSorter::Sink<TracePacketData, CaptureSink> {
+ public:
+  struct Packet {
+    int64_t ts;
+    TracePacketData data;
+  };
+  void Parse(int64_t ts, TracePacketData data) {
+    packets.push_back({ts, std::move(data)});
+  }
+  std::vector<Packet> packets;
+};
+
 class AndroidProcessStateModuleTest : public testing::Test {
  public:
   AndroidProcessStateModuleTest() {
@@ -74,6 +86,21 @@
     context_.process_tracker = std::make_unique<ProcessTracker>(&context_);
     context_.global_args_tracker =
         std::make_unique<GlobalArgsTracker>(context_.storage.get());
+    context_.blob_packet_writer = std::make_unique<BlobPacketWriter>();
+    context_.trace_time_state = std::make_unique<TraceTimeState>(
+        ClockTracker::ClockId::Machine(protos::pbzero::BUILTIN_CLOCK_BOOTTIME));
+    primary_sync_ = std::make_unique<ClockSynchronizer>(
+        context_.trace_time_state.get(),
+        std::make_unique<ClockSynchronizerListenerImpl>(&context_));
+    context_.clock_tracker = std::make_unique<ClockTracker>(
+        &context_, primary_sync_.get(), /*is_primary=*/true);
+
+    context_.sorter = std::make_unique<TraceSorter>(
+        &context_, TraceSorter::SortingMode::kFullSort);
+    auto sink = std::make_unique<CaptureSink>();
+    capture_sink_ = sink.get();
+    module_context_.trace_packet_stream =
+        context_.sorter->CreateStream(std::move(sink));
 
     process_state_table_ = std::make_unique<tables::AndroidProcessStateTable>(
         context_.storage->mutable_string_pool());
@@ -122,13 +149,75 @@
  protected:
   TraceProcessorContext context_;
   ProtoImporterModuleContext module_context_;
+  std::unique_ptr<ClockSynchronizer> primary_sync_;
+  CaptureSink* capture_sink_ = nullptr;
   std::unique_ptr<tables::AndroidProcessStateTable> process_state_table_;
   std::unique_ptr<tables::AndroidFreezerStateTable> freezer_state_table_;
   std::unique_ptr<AndroidProcessStateTracker> tracker_;
   std::unique_ptr<AndroidProcessStateModule> module_;
 };
 
+TEST_F(AndroidProcessStateModuleTest, TokenizePacketSplitsAndRemapsTimestamps) {
+  // Sync boottime clock with trace time
+  context_.clock_tracker->AddSnapshot({
+      {ClockTracker::ClockId::Machine(protos::pbzero::BUILTIN_CLOCK_BOOTTIME),
+       1000000000},
+      {ClockTracker::ClockId::Machine(protos::pbzero::BUILTIN_CLOCK_MONOTONIC),
+       1000000000},
+  });
+
+  protozero::HeapBuffered<AndroidProcessStateSnapshot> snapshot;
+  {
+    auto* rec = snapshot->add_record();
+    rec->set_pid(1001);
+    rec->set_uid(10001);
+    rec->set_process_name("com.test.app1");
+    rec->set_start_seq_id(42);
+    rec->set_start_time_ms(5000);  // 5000ms boottime
+  }
+  {
+    auto* rec = snapshot->add_record();
+    rec->set_pid(1002);
+    rec->set_uid(10002);
+    rec->set_process_name("com.test.app2");
+    rec->set_start_seq_id(43);
+    // no start_time_ms -> should use packet timestamp
+  }
+  std::vector<uint8_t> snap_bytes = snapshot.SerializeAsArray();
+
+  protozero::HeapBuffered<TracePacket> packet;
+  packet->AppendBytes(
+      FrameworksBaseTracePacket::kAndroidProcessStateFieldNumber,
+      snap_bytes.data(), snap_bytes.size());
+  std::vector<uint8_t> bytes = packet.SerializeAsArray();
+
+  TraceBlobView tbv(TraceBlob::CopyFrom(bytes.data(), bytes.size()));
+  SelectiveTracePacketDecoder decoder(tbv.data(), tbv.length());
+  TracePacketField field = decoder.FindUnknownField(
+      FrameworksBaseTracePacket::kAndroidProcessStateFieldNumber);
+  ASSERT_TRUE(field.valid());
+
+  ModuleResult res = module_->TokenizePacket(
+      {decoder, &tbv, 99999999,
+       PacketSequenceStateGeneration::CreateFirst(&context_), field});
+  EXPECT_FALSE(res.ignored());
+
+  context_.sorter->ExtractEventsForced();
+  ASSERT_EQ(capture_sink_->packets.size(), 2u);
+
+  EXPECT_EQ(capture_sink_->packets[0].ts, 99999999LL);
+  EXPECT_EQ(capture_sink_->packets[1].ts, 5000000000LL);
+}
+
 TEST_F(AndroidProcessStateModuleTest, ParseDumpPopulatesProcessTable) {
+  // Sync boottime clock
+  context_.clock_tracker->AddSnapshot({
+      {ClockTracker::ClockId::Machine(protos::pbzero::BUILTIN_CLOCK_BOOTTIME),
+       1000000000},
+      {ClockTracker::ClockId::Machine(protos::pbzero::BUILTIN_CLOCK_MONOTONIC),
+       1000000000},
+  });
+
   protozero::HeapBuffered<AndroidProcessStateSnapshot> snapshot;
   {
     auto* rec = snapshot->add_record();
@@ -137,6 +226,9 @@
     rec->set_proc_state(2);  // PROCESS_STATE_PERSISTENT
     rec->set_oom_score(-900);
     rec->set_capability_flags(1);
+    rec->set_process_name("com.android.systemui");
+    rec->set_start_seq_id(10);
+    rec->set_start_time_ms(5000);
   }
   {
     auto* rec = snapshot->add_record();
@@ -145,6 +237,9 @@
     rec->set_proc_state(19);  // PROCESS_STATE_CACHED_EMPTY
     rec->set_oom_score(905);
     rec->set_capability_flags(0);
+    rec->set_process_name("com.example.cached");
+    rec->set_start_seq_id(11);
+    rec->set_start_time_ms(8000);
   }
   std::vector<uint8_t> snap_bytes = snapshot.SerializeAsArray();
 
@@ -153,6 +248,11 @@
 
   // Verify process_state table
   EXPECT_EQ(process_state_table_->row_count(), 2u);
+  auto rr = (*process_state_table_)[0];
+  ASSERT_TRUE(rr.process_name().has_value());
+  EXPECT_STREQ(context_.storage->GetString(*rr.process_name()).c_str(),
+               "com.android.systemui");
+  EXPECT_EQ(*rr.start_seq_id(), 10LL);
 
   // Verify main process table (row 0 is reserved swapper/idle process)
   const auto& pt = context_.storage->process_table();
@@ -161,10 +261,20 @@
   auto prr0 = pt[1];
   EXPECT_EQ(prr0.pid(), 2001);
   EXPECT_EQ(prr0.uid(), 10002u);
+  ASSERT_TRUE(prr0.name().has_value());
+  EXPECT_STREQ(context_.storage->GetString(*prr0.name()).c_str(),
+               "com.android.systemui");
+  ASSERT_TRUE(prr0.start_ts().has_value());
+  EXPECT_EQ(*prr0.start_ts(), 5000000000LL);
 
   auto prr1 = pt[2];
   EXPECT_EQ(prr1.pid(), 2002);
   EXPECT_EQ(prr1.uid(), 10003u);
+  ASSERT_TRUE(prr1.name().has_value());
+  EXPECT_STREQ(context_.storage->GetString(*prr1.name()).c_str(),
+               "com.example.cached");
+  ASSERT_TRUE(prr1.start_ts().has_value());
+  EXPECT_EQ(*prr1.start_ts(), 8000000000LL);
 }
 
 TEST_F(AndroidProcessStateModuleTest, ParseChangeAndDumpRewind) {
diff --git a/src/trace_processor/plugins/android_process_state/android_process_state_tracker.cc b/src/trace_processor/plugins/android_process_state/android_process_state_tracker.cc
index c3bf5d2..f9f64eb 100644
--- a/src/trace_processor/plugins/android_process_state/android_process_state_tracker.cc
+++ b/src/trace_processor/plugins/android_process_state/android_process_state_tracker.cc
@@ -19,8 +19,10 @@
 #include <string>
 
 #include "perfetto/ext/base/string_view.h"
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_trace_packet.pbzero.h"
 #include "protos/third_party/android/frameworks/base/proto/tracing/frameworks_base_track_event.pbzero.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
@@ -124,6 +126,25 @@
           v.upid, static_cast<uint32_t>(rec.uid()));
     }
 
+    if (rec.has_process_name()) {
+      v.process_name = context_->storage->InternString(rec.process_name());
+      context_->process_tracker->UpdateProcessName(
+          v.upid, *v.process_name, ProcessNamePriority::kSystem);
+    }
+    if (rec.has_start_seq_id()) {
+      v.start_seq_id = rec.start_seq_id();
+    }
+    if (rec.has_start_time_ms()) {
+      v.start_time_ms = rec.start_time_ms();
+      std::optional<int64_t> start_ts = context_->clock_tracker->ToTraceTime(
+          ClockTracker::ClockId::Machine(
+              protos::pbzero::BUILTIN_CLOCK_BOOTTIME),
+          static_cast<int64_t>(rec.start_time_ms()) * 1000000LL);
+      if (start_ts.has_value()) {
+        context_->process_tracker->SetStartTsIfUnset(v.upid, *start_ts);
+      }
+    }
+
     // Note: android.util.proto.ProtoOutputStream ignores/omits 0 data points
     // during serialization on Android, so unset fields in the dump snapshot
     // represent 0.
@@ -158,6 +179,15 @@
     if (earliest.values.capability_flags.has_value()) {
       v.capability_flags = earliest.values.capability_flags;
     }
+    if (earliest.values.process_name.has_value()) {
+      v.process_name = earliest.values.process_name;
+    }
+    if (earliest.values.start_seq_id.has_value()) {
+      v.start_seq_id = earliest.values.start_seq_id;
+    }
+    if (earliest.values.start_time_ms.has_value()) {
+      v.start_time_ms = earliest.values.start_time_ms;
+    }
   }
 
   return initial;
@@ -185,6 +215,12 @@
   if (v.capability_flags.has_value()) {
     row.capability_flags = *v.capability_flags;
   }
+  if (v.process_name.has_value()) {
+    row.process_name = *v.process_name;
+  }
+  if (v.start_seq_id.has_value()) {
+    row.start_seq_id = *v.start_seq_id;
+  }
   process_state_table_->Insert(row);
 }
 
diff --git a/src/trace_processor/plugins/android_process_state/android_process_state_tracker.h b/src/trace_processor/plugins/android_process_state/android_process_state_tracker.h
index 25524c6..07be436 100644
--- a/src/trace_processor/plugins/android_process_state/android_process_state_tracker.h
+++ b/src/trace_processor/plugins/android_process_state/android_process_state_tracker.h
@@ -66,6 +66,9 @@
     std::optional<int32_t> oom_score;
     std::optional<int32_t> proc_state;
     std::optional<int32_t> capability_flags;
+    std::optional<StringId> process_name;
+    std::optional<int64_t> start_seq_id;
+    std::optional<int64_t> start_time_ms;
   };
 
   // Tracks the state before the first observed change (prev_*) for a process,
diff --git a/src/trace_processor/plugins/android_process_state/tables.py b/src/trace_processor/plugins/android_process_state/tables.py
index 315618c..a9f1826 100644
--- a/src/trace_processor/plugins/android_process_state/tables.py
+++ b/src/trace_processor/plugins/android_process_state/tables.py
@@ -35,11 +35,15 @@
           cpp_access=CppAccess.READ,
           cpp_access_duration=CppAccessDuration.POST_FINALIZATION),
         C('upid', CppTableId(PROCESS_TABLE), cpp_access=CppAccess.READ),
-        C('proc_state', CppOptional(CppString())),
-        C('oom_score', CppOptional(CppInt32())),
-        C('capability_flags', CppOptional(CppInt32())),
-        C('reason', CppOptional(CppString())),
-        C('seq_id', CppOptional(CppInt64())),
+        C('proc_state', CppOptional(CppString()), cpp_access=CppAccess.READ),
+        C('oom_score', CppOptional(CppInt32()), cpp_access=CppAccess.READ),
+        C('capability_flags',
+          CppOptional(CppInt32()),
+          cpp_access=CppAccess.READ),
+        C('reason', CppOptional(CppString()), cpp_access=CppAccess.READ),
+        C('seq_id', CppOptional(CppInt64()), cpp_access=CppAccess.READ),
+        C('process_name', CppOptional(CppString()), cpp_access=CppAccess.READ),
+        C('start_seq_id', CppOptional(CppInt64()), cpp_access=CppAccess.READ),
         C('is_initial', CppUint32(), cpp_access=CppAccess.READ),
     ],
     tabledoc=TableDoc(
@@ -60,6 +64,10 @@
                 'Reason for state change (if from track event).',
             'seq_id':
                 'Sequence ID for OOM adjustment iterations.',
+            'process_name':
+                'Process name from dump snapshot.',
+            'start_seq_id':
+                'Monotonically increasing process launch sequence ID.',
             'is_initial':
                 '1 for synthesized initial state row, 0 for change event.',
         },
diff --git a/test/trace_processor/diff_tests/parser/android/android_process_state.textproto b/test/trace_processor/diff_tests/parser/android/android_process_state.textproto
index b014163..c773b1d 100644
--- a/test/trace_processor/diff_tests/parser/android/android_process_state.textproto
+++ b/test/trace_processor/diff_tests/parser/android/android_process_state.textproto
@@ -131,6 +131,8 @@
       proc_state: 1002
       oom_score: 0
       capability_flags: 1
+      process_name: "com.android.systemui"
+      start_seq_id: 10
     }
     record {
       pid: 200
@@ -138,6 +140,8 @@
       proc_state: 1015
       oom_score: 900
       capability_flags: 0
+      process_name: "com.example.app"
+      start_seq_id: 11
     }
     # PID 300: Dump-only process with non-zero values.
     record {
diff --git a/test/trace_processor/diff_tests/parser/android/tests.py b/test/trace_processor/diff_tests/parser/android/tests.py
index c06ff31..f3a27b4 100644
--- a/test/trace_processor/diff_tests/parser/android/tests.py
+++ b/test/trace_processor/diff_tests/parser/android/tests.py
@@ -374,7 +374,10 @@
         SELECT
           t.ts,
           p.pid,
+          p.name AS p_name,
           p.uid,
+          t.process_name,
+          t.start_seq_id,
           t.proc_state,
           t.oom_score,
           t.capability_flags,
@@ -386,16 +389,16 @@
         ORDER BY p.pid, t.ts, t.seq_id;
         """,
         out=Csv("""
-          "ts","pid","uid","proc_state","oom_score","capability_flags","reason","seq_id","is_initial"
-          "[NULL]",100,10001,"PROCESS_STATE_TOP",0,1,"[NULL]","[NULL]",1
-          "[NULL]",200,10002,"PROCESS_STATE_TOP",0,1,"[NULL]","[NULL]",1
-          2000,200,10002,"PROCESS_STATE_IMPORTANT_FOREGROUND",200,0,"OOM_ADJ_REASON_START_RECEIVER",10,0
-          4000,200,10002,"PROCESS_STATE_CACHED_ACTIVITY",900,0,"OOM_ADJ_REASON_BIND_SERVICE",11,0
-          "[NULL]",300,10003,"PROCESS_STATE_PERSISTENT",-1000,1,"[NULL]","[NULL]",1
-          "[NULL]",400,10004,"PROCESS_STATE_FOREGROUND_SERVICE",0,0,"[NULL]","[NULL]",1
-          "[NULL]",500,"[NULL]","PROCESS_STATE_TOP",0,1,"[NULL]","[NULL]",1
-          2000,500,"[NULL]","PROCESS_STATE_BOUND_FOREGROUND_SERVICE",250,0,"OOM_ADJ_REASON_START_RECEIVER",20,0
-          2000,500,"[NULL]","PROCESS_STATE_IMPORTANT_FOREGROUND",300,0,"OOM_ADJ_REASON_BIND_SERVICE",21,0
+          "ts","pid","p_name","uid","process_name","start_seq_id","proc_state","oom_score","capability_flags","reason","seq_id","is_initial"
+          "[NULL]",100,"com.android.systemui",10001,"com.android.systemui",10,"PROCESS_STATE_TOP",0,1,"[NULL]","[NULL]",1
+          "[NULL]",200,"com.example.app",10002,"com.example.app",11,"PROCESS_STATE_TOP",0,1,"[NULL]","[NULL]",1
+          2000,200,"com.example.app",10002,"[NULL]","[NULL]","PROCESS_STATE_IMPORTANT_FOREGROUND",200,0,"OOM_ADJ_REASON_START_RECEIVER",10,0
+          4000,200,"com.example.app",10002,"[NULL]","[NULL]","PROCESS_STATE_CACHED_ACTIVITY",900,0,"OOM_ADJ_REASON_BIND_SERVICE",11,0
+          "[NULL]",300,"[NULL]",10003,"[NULL]","[NULL]","PROCESS_STATE_PERSISTENT",-1000,1,"[NULL]","[NULL]",1
+          "[NULL]",400,"[NULL]",10004,"[NULL]","[NULL]","PROCESS_STATE_FOREGROUND_SERVICE",0,0,"[NULL]","[NULL]",1
+          "[NULL]",500,"[NULL]","[NULL]","[NULL]","[NULL]","PROCESS_STATE_TOP",0,1,"[NULL]","[NULL]",1
+          2000,500,"[NULL]","[NULL]","[NULL]","[NULL]","PROCESS_STATE_BOUND_FOREGROUND_SERVICE",250,0,"OOM_ADJ_REASON_START_RECEIVER",20,0
+          2000,500,"[NULL]","[NULL]","[NULL]","[NULL]","PROCESS_STATE_IMPORTANT_FOREGROUND",300,0,"OOM_ADJ_REASON_BIND_SERVICE",21,0
         """))
 
   def test_android_freezer_state(self):