Add Trace Processor support for the AndroidInputEvent data source

Create and expose three SQL tables:
- android_key_events
- android_motion_events
- android_input_event_dispatch

Bug: 332714237
Test: tools/diff_test_trace_processor.py <trace_processor_shell> \
          --name-filter "AndroidInputEvent"
Change-Id: I35d987f7b0dc9483ea940fa335a3f901d4c43593
diff --git a/Android.bp b/Android.bp
index dbac421..dff63fb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12352,6 +12352,7 @@
     srcs: [
         "src/trace_processor/importers/proto/additional_modules.cc",
         "src/trace_processor/importers/proto/android_camera_event_module.cc",
+        "src/trace_processor/importers/proto/android_input_event_module.cc",
         "src/trace_processor/importers/proto/android_probes_module.cc",
         "src/trace_processor/importers/proto/android_probes_parser.cc",
         "src/trace_processor/importers/proto/android_probes_tracker.cc",
diff --git a/BUILD b/BUILD
index 3505021..17f6aef 100644
--- a/BUILD
+++ b/BUILD
@@ -1803,6 +1803,8 @@
         "src/trace_processor/importers/proto/additional_modules.h",
         "src/trace_processor/importers/proto/android_camera_event_module.cc",
         "src/trace_processor/importers/proto/android_camera_event_module.h",
+        "src/trace_processor/importers/proto/android_input_event_module.cc",
+        "src/trace_processor/importers/proto/android_input_event_module.h",
         "src/trace_processor/importers/proto/android_probes_module.cc",
         "src/trace_processor/importers/proto/android_probes_module.h",
         "src/trace_processor/importers/proto/android_probes_parser.cc",
diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
index 8b8d35c..e72b7c9 100644
--- a/src/trace_processor/importers/common/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -164,6 +164,21 @@
         id);
   }
 
+  BoundInserter AddArgsTo(tables::AndroidKeyEventsTable::Id id) {
+    return AddArgsTo(context_->storage->mutable_android_key_events_table(),
+                     id);
+  }
+
+  BoundInserter AddArgsTo(tables::AndroidMotionEventsTable::Id id) {
+    return AddArgsTo(context_->storage->mutable_android_motion_events_table(),
+                     id);
+  }
+
+  BoundInserter AddArgsTo(tables::AndroidInputEventDispatchTable::Id id) {
+    return AddArgsTo(
+        context_->storage->mutable_android_input_event_dispatch_table(), id);
+  }
+
   BoundInserter AddArgsTo(MetadataId id) {
     auto* table = context_->storage->mutable_metadata_table();
     uint32_t row = *table->id().IndexOf(id);
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index b92bed6..effda82 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -115,6 +115,8 @@
     "additional_modules.h",
     "android_camera_event_module.cc",
     "android_camera_event_module.h",
+    "android_input_event_module.cc",
+    "android_input_event_module.h",
     "android_probes_module.cc",
     "android_probes_module.h",
     "android_probes_parser.cc",
diff --git a/src/trace_processor/importers/proto/additional_modules.cc b/src/trace_processor/importers/proto/additional_modules.cc
index 91645c3..cf2ee84 100644
--- a/src/trace_processor/importers/proto/additional_modules.cc
+++ b/src/trace_processor/importers/proto/additional_modules.cc
@@ -18,6 +18,7 @@
 #include "src/trace_processor/importers/etw/etw_module_impl.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
 #include "src/trace_processor/importers/proto/android_camera_event_module.h"
+#include "src/trace_processor/importers/proto/android_input_event_module.h"
 #include "src/trace_processor/importers/proto/android_probes_module.h"
 #include "src/trace_processor/importers/proto/graphics_event_module.h"
 #include "src/trace_processor/importers/proto/heap_graph_module.h"
@@ -45,6 +46,7 @@
   context->modules.emplace_back(new MetadataModule(context));
   context->modules.emplace_back(new V8Module(context));
   context->modules.emplace_back(new WinscopeModule(context));
+  context->modules.emplace_back(new AndroidInputEventModule(context));
 
   // Ftrace/Etw modules are special, because it has one extra method for parsing
   // ftrace/etw packets. So we need to store a pointer to it separately.
diff --git a/src/trace_processor/importers/proto/android_input_event_module.cc b/src/trace_processor/importers/proto/android_input_event_module.cc
new file mode 100644
index 0000000..26671a7
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_input_event_module.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 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 "src/trace_processor/importers/proto/android_input_event_module.h"
+
+#include "protos/perfetto/trace/android/android_input_event.pbzero.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/proto/args_parser.h"
+#include "src/trace_processor/importers/proto/trace.descriptor.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/android_tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto::trace_processor {
+
+using perfetto::protos::pbzero::AndroidInputEvent;
+using perfetto::protos::pbzero::AndroidKeyEvent;
+using perfetto::protos::pbzero::AndroidMotionEvent;
+using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent;
+using perfetto::protos::pbzero::TracePacket;
+
+AndroidInputEventModule::AndroidInputEventModule(TraceProcessorContext* context)
+    : context_(*context), args_parser_(pool_) {
+  pool_.AddFromFileDescriptorSet(kTraceDescriptor.data(),
+                                 kTraceDescriptor.size());
+  RegisterForField(TracePacket::kAndroidInputEventFieldNumber, context);
+}
+
+void AndroidInputEventModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t packet_ts,
+    const TracePacketData&,
+    uint32_t field_id) {
+  if (field_id != TracePacket::kAndroidInputEventFieldNumber)
+    return;
+
+  auto input_event = AndroidInputEvent::Decoder(decoder.android_input_event());
+
+  constexpr static auto supported_fields = std::array{
+      AndroidInputEvent::kDispatcherMotionEventFieldNumber,
+      AndroidInputEvent::kDispatcherMotionEventRedactedFieldNumber,
+      AndroidInputEvent::kDispatcherKeyEventFieldNumber,
+      AndroidInputEvent::kDispatcherKeyEventRedactedFieldNumber,
+      AndroidInputEvent::kDispatcherWindowDispatchEventFieldNumber,
+      AndroidInputEvent::kDispatcherWindowDispatchEventRedactedFieldNumber};
+
+  for (auto sub_field_id : supported_fields) {
+    auto sub_field = input_event.Get(static_cast<uint32_t>(sub_field_id));
+    if (!sub_field.valid())
+      continue;
+
+    switch (sub_field_id) {
+      case AndroidInputEvent::kDispatcherMotionEventFieldNumber:
+      case AndroidInputEvent::kDispatcherMotionEventRedactedFieldNumber:
+        ParseMotionEvent(packet_ts, sub_field.as_bytes());
+        return;
+      case AndroidInputEvent::kDispatcherKeyEventFieldNumber:
+      case AndroidInputEvent::kDispatcherKeyEventRedactedFieldNumber:
+        ParseKeyEvent(packet_ts, sub_field.as_bytes());
+        return;
+      case AndroidInputEvent::kDispatcherWindowDispatchEventFieldNumber:
+      case AndroidInputEvent::kDispatcherWindowDispatchEventRedactedFieldNumber:
+        ParseWindowDispatchEvent(packet_ts, sub_field.as_bytes());
+        return;
+    }
+  }
+}
+
+void AndroidInputEventModule::ParseMotionEvent(
+    int64_t packet_ts,
+    const protozero::ConstBytes& bytes) {
+  AndroidMotionEvent::Decoder event_proto(bytes);
+  tables::AndroidMotionEventsTable::Row event_row;
+  event_row.event_id = event_proto.event_id();
+  event_row.ts = event_proto.event_time_nanos();
+
+  auto event_row_id = context_.storage->mutable_android_motion_events_table()
+                          ->Insert(event_row)
+                          .id;
+  auto inserter = context_.args_tracker->AddArgsTo(event_row_id);
+  ArgsParser writer{packet_ts, inserter, *context_.storage};
+
+  base::Status status =
+      args_parser_.ParseMessage(bytes, ".perfetto.protos.AndroidMotionEvent",
+                                nullptr /*parse all fields*/, writer);
+  if (!status.ok())
+    context_.storage->IncrementStats(stats::android_input_event_parse_errors);
+}
+
+void AndroidInputEventModule::ParseKeyEvent(
+    int64_t packet_ts,
+    const protozero::ConstBytes& bytes) {
+  AndroidKeyEvent::Decoder event_proto(bytes);
+  tables::AndroidKeyEventsTable::Row event_row;
+  event_row.event_id = event_proto.event_id();
+  event_row.ts = event_proto.event_time_nanos();
+
+  auto event_row_id = context_.storage->mutable_android_key_events_table()
+                          ->Insert(event_row)
+                          .id;
+  auto inserter = context_.args_tracker->AddArgsTo(event_row_id);
+  ArgsParser writer{packet_ts, inserter, *context_.storage};
+
+  base::Status status =
+      args_parser_.ParseMessage(bytes, ".perfetto.protos.AndroidKeyEvent",
+                                nullptr /*parse all fields*/, writer);
+  if (!status.ok())
+    context_.storage->IncrementStats(stats::android_input_event_parse_errors);
+}
+
+void AndroidInputEventModule::ParseWindowDispatchEvent(
+    int64_t packet_ts,
+    const protozero::ConstBytes& bytes) {
+  AndroidWindowInputDispatchEvent::Decoder event_proto(bytes);
+  tables::AndroidInputEventDispatchTable::Row event_row;
+  event_row.event_id = event_proto.event_id();
+  event_row.vsync_id = event_proto.vsync_id();
+  event_row.window_id = event_proto.window_id();
+
+  auto event_row_id =
+      context_.storage->mutable_android_input_event_dispatch_table()
+          ->Insert(event_row)
+          .id;
+  auto inserter = context_.args_tracker->AddArgsTo(event_row_id);
+  ArgsParser writer{packet_ts, inserter, *context_.storage};
+
+  base::Status status = args_parser_.ParseMessage(
+      bytes, ".perfetto.protos.AndroidWindowInputDispatchEvent",
+      nullptr /*parse all fields*/, writer);
+  if (!status.ok())
+    context_.storage->IncrementStats(stats::android_input_event_parse_errors);
+}
+
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/importers/proto/android_input_event_module.h b/src/trace_processor/importers/proto/android_input_event_module.h
new file mode 100644
index 0000000..c310909
--- /dev/null
+++ b/src/trace_processor/importers/proto/android_input_event_module.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 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 SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_INPUT_EVENT_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_INPUT_EVENT_MODULE_H_
+
+#include <cstdint>
+#include "perfetto/base/build_config.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/proto_to_args_parser.h"
+
+namespace perfetto::trace_processor {
+
+class AndroidInputEventModule : public ProtoImporterModule {
+ public:
+  explicit AndroidInputEventModule(TraceProcessorContext* context);
+
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder&,
+                            int64_t packet_ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
+
+ private:
+  TraceProcessorContext& context_;
+  DescriptorPool pool_;
+  util::ProtoToArgsParser args_parser_;
+
+  void ParseMotionEvent(int64_t packet_ts, const protozero::ConstBytes& bytes);
+  void ParseKeyEvent(int64_t packet_ts, const protozero::ConstBytes& bytes);
+  void ParseWindowDispatchEvent(int64_t packet_ts, const protozero::ConstBytes& bytes);
+};
+
+}  // namespace perfetto::trace_processor
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_INPUT_EVENT_MODULE_H_
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/input.sql b/src/trace_processor/perfetto_sql/stdlib/android/input.sql
index a1d7b11..5ac6c74 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/input.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/input.sql
@@ -130,3 +130,62 @@
     AND dispatch.event_seq = finish.event_seq
 JOIN (SELECT * FROM _input_message_received WHERE event_type = '0x2') finish_ack
   ON finish_ack.event_channel = dispatch.event_channel AND dispatch.event_seq = finish_ack.event_seq;
+
+-- Key events processed by the Android framework (from android.input.inputevent data source).
+CREATE PERFETTO VIEW android_key_events(
+  -- ID of the trace entry
+  id INT,
+  -- The randomly-generated ID associated with each input event processed
+  -- by Android Framework, used to track the event through the input pipeline
+  event_id INT,
+  -- The timestamp associated with the input event
+  ts INT,
+  -- Details of the input event parsed from the proto message
+  arg_set_id INT
+) AS
+SELECT
+  id,
+  event_id,
+  ts,
+  arg_set_id
+FROM __intrinsic_android_key_events;
+
+-- Motion events processed by the Android framework (from android.input.inputevent data source).
+CREATE PERFETTO VIEW android_motion_events(
+  -- ID of the trace entry
+  id INT,
+  -- The randomly-generated ID associated with each input event processed
+  -- by Android Framework, used to track the event through the input pipeline
+  event_id INT,
+  -- The timestamp associated with the input event
+  ts INT,
+  -- Details of the input event parsed from the proto message
+  arg_set_id INT
+) AS
+SELECT
+  id,
+  event_id,
+  ts,
+  arg_set_id
+FROM __intrinsic_android_motion_events;
+
+-- Input event dispatching information in Android (from android.input.inputevent data source).
+CREATE PERFETTO VIEW android_input_event_dispatch(
+  -- ID of the trace entry
+  id INT,
+  -- Event ID of the input event that was dispatched
+  event_id INT,
+  -- Extra args parsed from the proto message
+  arg_set_id INT,
+  -- Vsync ID that identifies the state of the windows during which the dispatch decision was made
+  vsync_id INT,
+  -- Window ID of the window receiving the event
+  window_id INT
+) AS
+SELECT
+  id,
+  event_id,
+  arg_set_id,
+  vsync_id,
+  window_id
+FROM __intrinsic_android_input_event_dispatch;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 6238455..057c2fa 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -363,7 +363,11 @@
   F(ftrace_missing_event_id,              kSingle,  kInfo,    kAnalysis,       \
       "Indicates that the ftrace event was dropped because the event id was "  \
       "missing. This is an 'info' stat rather than an error stat because "     \
-      "this can be legitimately missing due to proto filtering.")
+      "this can be legitimately missing due to proto filtering."),             \
+  F(android_input_event_parse_errors,     kSingle,  kInfo,     kAnalysis,      \
+      "Android input event packet has unknown fields, which results "          \
+      "in some arguments missing. You may need a newer version of trace "      \
+      "processor to parse them.")
 // clang-format on
 
 enum Type {
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 734fd0d..61da6d9 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -514,6 +514,29 @@
     return &android_dumpstate_table_;
   }
 
+  const tables::AndroidKeyEventsTable& android_key_events_table() const {
+    return android_key_events_table_;
+  }
+  tables::AndroidKeyEventsTable* mutable_android_key_events_table() {
+    return &android_key_events_table_;
+  }
+
+  const tables::AndroidMotionEventsTable& android_motion_events_table() const {
+    return android_motion_events_table_;
+  }
+  tables::AndroidMotionEventsTable* mutable_android_motion_events_table() {
+    return &android_motion_events_table_;
+  }
+
+  const tables::AndroidInputEventDispatchTable&
+  android_input_event_dispatch_table() const {
+    return android_input_event_dispatch_table_;
+  }
+  tables::AndroidInputEventDispatchTable*
+  mutable_android_input_event_dispatch_table() {
+    return &android_input_event_dispatch_table_;
+  }
+
   const StatsMap& stats() const { return stats_; }
 
   const tables::MetadataTable& metadata_table() const {
@@ -1097,6 +1120,11 @@
 
   tables::AndroidDumpstateTable android_dumpstate_table_{&string_pool_};
 
+  tables::AndroidKeyEventsTable android_key_events_table_{&string_pool_};
+  tables::AndroidMotionEventsTable android_motion_events_table_{&string_pool_};
+  tables::AndroidInputEventDispatchTable
+      android_input_event_dispatch_table_{&string_pool_};
+
   tables::StackProfileMappingTable stack_profile_mapping_table_{&string_pool_};
   tables::StackProfileFrameTable stack_profile_frame_table_{&string_pool_};
   tables::StackProfileCallsiteTable stack_profile_callsite_table_{
diff --git a/src/trace_processor/tables/android_tables.py b/src/trace_processor/tables/android_tables.py
index 88f2592..5954d53 100644
--- a/src/trace_processor/tables/android_tables.py
+++ b/src/trace_processor/tables/android_tables.py
@@ -14,11 +14,11 @@
 """Contains tables for relevant for Android."""
 
 from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnDoc
 from python.generators.trace_processor_table.public import CppDouble
 from python.generators.trace_processor_table.public import CppInt32
 from python.generators.trace_processor_table.public import CppInt64
 from python.generators.trace_processor_table.public import CppOptional
-from python.generators.trace_processor_table.public import CppSelfTableId
 from python.generators.trace_processor_table.public import CppString
 from python.generators.trace_processor_table.public import Table
 from python.generators.trace_processor_table.public import TableDoc
@@ -156,9 +156,100 @@
                 '''
         }))
 
+ANDROID_MOTION_EVENTS_TABLE = Table(
+    python_module=__file__,
+    class_name='AndroidMotionEventsTable',
+    sql_name='__intrinsic_android_motion_events',
+    columns=[
+        C('event_id', CppUint32()),
+        C('ts', CppInt64()),
+        C('arg_set_id', CppUint32()),
+    ],
+    tabledoc=TableDoc(
+        doc='Contains Android MotionEvents processed by the system',
+        group='Android',
+        columns={
+            'event_id':
+                '''
+                    The randomly-generated ID associated with each input event processed
+                    by Android Framework, used to track the event through the input pipeline.
+                ''',
+            'ts':
+                '''The timestamp associated with the input event.''',
+            'arg_set_id':
+                ColumnDoc(
+                    doc='Details of the motion event parsed from the proto message.',
+                    joinable='args.arg_set_id'),
+        }))
+
+ANDROID_KEY_EVENTS_TABLE = Table(
+    python_module=__file__,
+    class_name='AndroidKeyEventsTable',
+    sql_name='__intrinsic_android_key_events',
+    columns=[
+        C('event_id', CppUint32()),
+        C('ts', CppInt64()),
+        C('arg_set_id', CppUint32()),
+    ],
+    tabledoc=TableDoc(
+        doc='Contains Android KeyEvents processed by the system',
+        group='Android',
+        columns={
+            'event_id':
+                '''
+                    The randomly-generated ID associated with each input event processed
+                    by Android Framework, used to track the event through the input pipeline.
+                ''',
+            'ts':
+                '''The timestamp associated with the input event.''',
+            'arg_set_id':
+                ColumnDoc(
+                    doc='Details of the key event parsed from the proto message.',
+                    joinable='args.arg_set_id'),
+        }))
+
+ANDROID_INPUT_EVENT_DISPATCH_TABLE = Table(
+    python_module=__file__,
+    class_name='AndroidInputEventDispatchTable',
+    sql_name='__intrinsic_android_input_event_dispatch',
+    columns=[
+        C('event_id', CppUint32()),
+        C('arg_set_id', CppUint32()),
+        C('vsync_id', CppInt64()),
+        C('window_id', CppInt32()),
+    ],
+    tabledoc=TableDoc(
+        doc=
+            '''
+                Contains records of Android input events being dispatched to input windows
+                by the Android Framework.
+            ''',
+        group='Android',
+        columns={
+            'event_id':
+                ColumnDoc(
+                    doc='The id of the input event that was dispatched.',
+                    joinable='android_input_event.event_id'),
+            'arg_set_id':
+                ColumnDoc(
+                    doc='Details of the dispatched event parsed from the proto message.',
+                    joinable='args.arg_set_id'),
+            'vsync_id':
+                '''
+                    The id of the vsync during which the Framework made the decision to
+                    dispatch this input event, used to identify the state of the input windows
+                    when the dispatching decision was made.
+                ''',
+            'window_id':
+                'The id of the window to which the event was dispatched.',
+        }))
+
 # Keep this list sorted.
 ALL_TABLES = [
     ANDROID_LOG_TABLE,
     ANDROID_DUMPSTATE_TABLE,
     ANDROID_GAME_INTERVENTION_LIST_TABLE,
+    ANDROID_KEY_EVENTS_TABLE,
+    ANDROID_MOTION_EVENTS_TABLE,
+    ANDROID_INPUT_EVENT_DISPATCH_TABLE,
 ]
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 8339b48..067cefa 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -40,6 +40,9 @@
 AndroidDumpstateTable::~AndroidDumpstateTable() = default;
 AndroidGameInterventionListTable::~AndroidGameInterventionListTable() = default;
 AndroidLogTable::~AndroidLogTable() = default;
+AndroidKeyEventsTable::~AndroidKeyEventsTable() = default;
+AndroidMotionEventsTable::~AndroidMotionEventsTable() = default;
+AndroidInputEventDispatchTable::~AndroidInputEventDispatchTable() = default;
 
 // counter_tables_py.h
 CounterTable::~CounterTable() = default;
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 7eac7b7..00f87bf 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -865,6 +865,9 @@
   RegisterStaticTable(storage->android_log_table());
   RegisterStaticTable(storage->android_dumpstate_table());
   RegisterStaticTable(storage->android_game_intervention_list_table());
+  RegisterStaticTable(storage->android_key_events_table());
+  RegisterStaticTable(storage->android_motion_events_table());
+  RegisterStaticTable(storage->android_input_event_dispatch_table());
 
   RegisterStaticTable(storage->vulkan_memory_allocations_table());
 
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index a531d3a..d3dba5c 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -48,6 +48,7 @@
 from diff_tests.metrics.webview.tests import WebView
 from diff_tests.parser.android_fs.tests import AndroidFs
 from diff_tests.parser.android.tests import AndroidParser
+from diff_tests.parser.android.tests_android_input_event import AndroidInputEvent
 from diff_tests.parser.android.tests_bugreport import AndroidBugreport
 from diff_tests.parser.android.tests_games import AndroidGames
 from diff_tests.parser.android.tests_inputmethod_clients import InputMethodClients
@@ -225,6 +226,7 @@
       *ParsingTracedStats(index_path, 'parser/parsing',
                           'ParsingTracedStats').fetch(),
       *Zip(index_path, 'parser/zip', 'Zip').fetch(),
+      *AndroidInputEvent(index_path, 'parser/android', 'AndroidInputEvent').fetch(),
   ]
 
   metrics_tests = [
diff --git a/test/trace_processor/diff_tests/parser/android/input_event_trace.textproto b/test/trace_processor/diff_tests/parser/android/input_event_trace.textproto
new file mode 100644
index 0000000..02ecce4
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/input_event_trace.textproto
@@ -0,0 +1,1008 @@
+packet {
+  clock_snapshot {
+    primary_trace_clock: BUILTIN_CLOCK_BOOTTIME
+    clocks {
+      clock_id: 6
+      timestamp: 178674061789798
+    }
+    clocks {
+      clock_id: 2
+      timestamp: 1715375168889637820
+    }
+    clocks {
+      clock_id: 4
+      timestamp: 64176147060835
+    }
+    clocks {
+      clock_id: 1
+      timestamp: 1715375168892622806
+    }
+    clocks {
+      clock_id: 3
+      timestamp: 64176150045943
+    }
+    clocks {
+      clock_id: 5
+      timestamp: 64176150046147
+    }
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+}
+packet {
+  clock_snapshot {
+    primary_trace_clock: BUILTIN_CLOCK_BOOTTIME
+    clocks {
+      clock_id: 6
+      timestamp: 178674061806929
+    }
+    clocks {
+      clock_id: 2
+      timestamp: 1715375168889637820
+    }
+    clocks {
+      clock_id: 4
+      timestamp: 64176147060835
+    }
+    clocks {
+      clock_id: 1
+      timestamp: 1715375168892639855
+    }
+    clocks {
+      clock_id: 3
+      timestamp: 64176150062992
+    }
+    clocks {
+      clock_id: 5
+      timestamp: 64176150063155
+    }
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+}
+packet {
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  synchronization_marker: "\202Gzv\262\215B\272\201\33432mW\240y"
+}
+packet {
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  trace_config {
+    buffers {
+      size_kb: 1000000
+      fill_policy: RING_BUFFER
+    }
+    data_sources {
+      config {
+        name: "android.input.inputevent"
+        android_input_event_config {
+          mode: TRACE_MODE_TRACE_ALL
+        }
+      }
+    }
+    duration_ms: 10000
+    enable_extra_guardrails: false
+    statsd_metadata {
+    }
+    statsd_logging: STATSD_LOGGING_DISABLED
+    trace_uuid_msb: 6148766808500972866
+    trace_uuid_lsb: -1402094119056663186
+  }
+}
+packet {
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  trace_uuid {
+    lsb: -1402094119056663186
+    msb: 6148766808500972866
+  }
+}
+packet {
+  system_info {
+    tracing_service_version: "Perfetto v44.0 (N/A)"
+    timezone_off_mins: 0
+    utsname {
+      sysname: "Linux"
+      version: "#1 SMP PREEMPT Thu Mar 28 12:45:51 UTC 2024"
+      machine: "aarch64"
+      release: "5.10.209-android13-4-02808-g9f408f561c85-ab11640454"
+    }
+    page_size: 4096
+    num_cpus: 8
+    android_build_fingerprint: "google/tangorpro/tangorpro:VanillaIceCream/MAIN/eng.prabir.20240508.183909:eng/dev-keys"
+    android_sdk_version: 35
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+}
+packet {
+  timestamp: 178674061798587
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  service_event {
+    tracing_started: true
+  }
+}
+packet {
+  timestamp: 178674065835493
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  service_event {
+    all_data_sources_started: true
+  }
+}
+packet {
+  timestamp: 178683982315600
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  service_event {
+    all_data_sources_flushed: true
+  }
+}
+packet {
+  timestamp: 178683984185189
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  service_event {
+    tracing_disabled: true
+  }
+}
+packet {
+  first_packet_on_sequence: true
+  android_input_event {
+    dispatcher_motion_event {
+      event_id: 104114844
+      event_time_nanos: 64179212500000
+      down_time_nanos: 64179212500000
+      source: 4098
+      action: 0
+      device_id: 4
+      display_id: 0
+      classification: 0
+      flags: 0
+      policy_flags: 1644167168
+      cursor_position_x: nan
+      cursor_position_y: nan
+      meta_state: 0
+      pointer {
+        pointer_id: 0
+        tool_type: 1
+        axis_value {
+          axis: 0
+          value: 580.000000
+        }
+        axis_value {
+          axis: 1
+          value: 798.000000
+        }
+        axis_value {
+          axis: 2
+          value: 1.003906
+        }
+        axis_value {
+          axis: 3
+          value: 0.033998
+        }
+        axis_value {
+          axis: 4
+          value: 92.000000
+        }
+        axis_value {
+          axis: 5
+          value: 82.000000
+        }
+        axis_value {
+          axis: 6
+          value: 92.000000
+        }
+        axis_value {
+          axis: 7
+          value: 82.000000
+        }
+        axis_value {
+          axis: 8
+          value: 0.983282
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+  previous_packet_dropped: true
+}
+packet {
+  android_input_event {
+    dispatcher_motion_event {
+      event_id: 1141228253
+      event_time_nanos: 64179212500000
+      down_time_nanos: 64179212500000
+      source: 4098
+      action: 4
+      device_id: 4
+      display_id: 0
+      classification: 0
+      flags: 0
+      policy_flags: 1644167168
+      cursor_position_x: nan
+      cursor_position_y: nan
+      meta_state: 0
+      pointer {
+        pointer_id: 0
+        tool_type: 1
+        axis_value {
+          axis: 0
+          value: 580.000000
+        }
+        axis_value {
+          axis: 1
+          value: 798.000000
+        }
+        axis_value {
+          axis: 2
+          value: 1.003906
+        }
+        axis_value {
+          axis: 3
+          value: 0.033998
+        }
+        axis_value {
+          axis: 4
+          value: 92.000000
+        }
+        axis_value {
+          axis: 5
+          value: 82.000000
+        }
+        axis_value {
+          axis: 6
+          value: 92.000000
+        }
+        axis_value {
+          axis: 7
+          value: 82.000000
+        }
+        axis_value {
+          axis: 8
+          value: 0.983282
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 1141228253
+      vsync_id: 182239
+      window_id: 105
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: -681.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.554132
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 104114844
+      vsync_id: 182239
+      window_id: 181
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.554157
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 104114844
+      vsync_id: 182239
+      window_id: 58
+      resolved_flags: 3
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1718.181641
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 600.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.554074
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 104114844
+      vsync_id: 182239
+      window_id: 76
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.554157
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 104114844
+      vsync_id: 182239
+      window_id: 68
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.554157
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 104114844
+      vsync_id: 0
+      window_id: 0
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.554157
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_motion_event {
+      event_id: 843076721
+      event_time_nanos: 64179262122000
+      down_time_nanos: 64179212500000
+      source: 4098
+      action: 2
+      device_id: 4
+      display_id: 0
+      classification: 1
+      flags: 0
+      policy_flags: 1644167168
+      cursor_position_x: nan
+      cursor_position_y: nan
+      meta_state: 0
+      pointer {
+        pointer_id: 0
+        tool_type: 1
+        axis_value {
+          axis: 0
+          value: 580.000000
+        }
+        axis_value {
+          axis: 1
+          value: 798.000000
+        }
+        axis_value {
+          axis: 2
+          value: 0.113281
+        }
+        axis_value {
+          axis: 3
+          value: 0.011333
+        }
+        axis_value {
+          axis: 4
+          value: 29.000000
+        }
+        axis_value {
+          axis: 5
+          value: 29.000000
+        }
+        axis_value {
+          axis: 6
+          value: 29.000000
+        }
+        axis_value {
+          axis: 7
+          value: 29.000000
+        }
+        axis_value {
+          axis: 8
+          value: 0.912335
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 843076721
+      vsync_id: 182239
+      window_id: 181
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 843076721
+      vsync_id: 182239
+      window_id: 58
+      resolved_flags: 3
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1718.181641
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 600.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483237
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 843076721
+      vsync_id: 182239
+      window_id: 76
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 843076721
+      vsync_id: 182239
+      window_id: 68
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 843076721
+      vsync_id: 0
+      window_id: 0
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_motion_event {
+      event_id: 744146837
+      event_time_nanos: 64179268985000
+      down_time_nanos: 64179212500000
+      source: 4098
+      action: 1
+      device_id: 4
+      display_id: 0
+      classification: 1
+      flags: 0
+      policy_flags: 1644167168
+      cursor_position_x: nan
+      cursor_position_y: nan
+      meta_state: 0
+      pointer {
+        pointer_id: 0
+        tool_type: 1
+        axis_value {
+          axis: 0
+          value: 580.000000
+        }
+        axis_value {
+          axis: 1
+          value: 798.000000
+        }
+        axis_value {
+          axis: 2
+          value: 0.113281
+        }
+        axis_value {
+          axis: 3
+          value: 0.011333
+        }
+        axis_value {
+          axis: 4
+          value: 29.000000
+        }
+        axis_value {
+          axis: 5
+          value: 29.000000
+        }
+        axis_value {
+          axis: 6
+          value: 29.000000
+        }
+        axis_value {
+          axis: 7
+          value: 29.000000
+        }
+        axis_value {
+          axis: 8
+          value: 0.912335
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 744146837
+      vsync_id: 182239
+      window_id: 181
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 744146837
+      vsync_id: 182239
+      window_id: 58
+      resolved_flags: 3
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1718.181641
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 600.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483237
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 744146837
+      vsync_id: 182239
+      window_id: 76
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 744146837
+      vsync_id: 182239
+      window_id: 68
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 744146837
+      vsync_id: 0
+      window_id: 0
+      resolved_flags: 0
+      dispatched_pointer {
+        pointer_id: 0
+        x_in_display: 1762.000000
+        y_in_display: 580.000000
+        axis_value_in_window {
+          axis: 0
+          value: 1762.000000
+        }
+        axis_value_in_window {
+          axis: 1
+          value: 580.000000
+        }
+        axis_value_in_window {
+          axis: 8
+          value: 2.483198
+        }
+      }
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_key_event {
+      event_id: 324105269
+      event_time_nanos: 64182660299000
+      down_time_nanos: 64182660299000
+      source: 257
+      action: 0
+      device_id: 2
+      display_id: -1
+      repeat_count: 0
+      flags: 8
+      policy_flags: 1644167168
+      key_code: 25
+      scan_code: 114
+      meta_state: 0
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 324105269
+      vsync_id: 182239
+      window_id: 181
+      resolved_flags: 8
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 324105269
+      vsync_id: 0
+      window_id: 0
+      resolved_flags: 8
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_key_event {
+      event_id: 60594531
+      event_time_nanos: 64182816340000
+      down_time_nanos: 64182660299000
+      source: 257
+      action: 1
+      device_id: 2
+      display_id: -1
+      repeat_count: 0
+      flags: 8
+      policy_flags: 1644167168
+      key_code: 25
+      scan_code: 114
+      meta_state: 0
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 60594531
+      vsync_id: 182239
+      window_id: 181
+      resolved_flags: 8
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  android_input_event {
+    dispatcher_window_dispatch_event {
+      event_id: 60594531
+      vsync_id: 0
+      window_id: 0
+      resolved_flags: 8
+    }
+  }
+  trusted_uid: 1000
+  trusted_packet_sequence_id: 2
+  trusted_pid: 1722
+}
+packet {
+  timestamp: 178683985660693
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  service_event {
+    read_tracing_buffers_completed: true
+  }
+}
+packet {
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 1
+  trace_stats {
+    buffer_stats {
+      buffer_size: 1024000000
+      bytes_written: 4096
+      chunks_written: 1
+    }
+    producers_connected: 13
+    producers_seen: 16
+    data_sources_registered: 40
+    data_sources_seen: 13
+    tracing_sessions: 2
+    total_buffers: 4
+    chunks_discarded: 7
+    patches_discarded: 0
+    invalid_packets: 0
+    flushes_requested: 1
+    flushes_succeeded: 1
+    flushes_failed: 0
+    final_flush_outcome: FINAL_FLUSH_UNSPECIFIED
+  }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py b/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py
new file mode 100644
index 0000000..30ac616
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+# Copyright (C) 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 a
+#
+#      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.
+
+from python.generators.diff_tests.testing import Path
+from python.generators.diff_tests.testing import Csv
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+class AndroidInputEvent(TestSuite):
+
+  def test_key_events_table(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          event_id, ts
+        FROM
+          android_key_events;
+        """,
+      out=Csv("""
+        "event_id","ts"
+        324105269,64182660299000
+        60594531,64182816340000
+        """))
+
+  def test_key_events_args(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          args.key, args.display_value
+        FROM
+          android_key_events AS e JOIN args ON e.arg_set_id = args.arg_set_id
+        WHERE e.event_id = 60594531
+        ORDER BY args.key;
+        """,
+      out=Csv("""
+        "key","display_value"
+        "action","1"
+        "device_id","2"
+        "display_id","-1"
+        "down_time_nanos","64182660299000"
+        "event_id","60594531"
+        "event_time_nanos","64182816340000"
+        "flags","8"
+        "key_code","25"
+        "meta_state","0"
+        "policy_flags","1644167168"
+        "repeat_count","0"
+        "scan_code","114"
+        "source","257"
+        """))
+
+  def test_motion_events_table(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          event_id, ts
+        FROM
+          android_motion_events;
+        """,
+      out=Csv("""
+        "event_id","ts"
+        104114844,64179212500000
+        1141228253,64179212500000
+        843076721,64179262122000
+        744146837,64179268985000
+        """))
+
+  def test_motion_events_args(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          args.key, args.display_value
+        FROM
+          android_motion_events AS e JOIN args ON e.arg_set_id = args.arg_set_id
+        WHERE e.event_id = 1141228253
+        ORDER BY args.key;
+        """,
+      out=Csv("""
+        "key","display_value"
+        "action","4"
+        "classification","0"
+        "cursor_position_x","[NULL]"
+        "cursor_position_y","[NULL]"
+        "device_id","4"
+        "display_id","0"
+        "down_time_nanos","64179212500000"
+        "event_id","1141228253"
+        "event_time_nanos","64179212500000"
+        "flags","0"
+        "meta_state","0"
+        "pointer[0].axis_value[0].axis","0"
+        "pointer[0].axis_value[0].value","580.0"
+        "pointer[0].axis_value[1].axis","1"
+        "pointer[0].axis_value[1].value","798.0"
+        "pointer[0].axis_value[2].axis","2"
+        "pointer[0].axis_value[2].value","1.00390601158142"
+        "pointer[0].axis_value[3].axis","3"
+        "pointer[0].axis_value[3].value","0.0339980013668537"
+        "pointer[0].axis_value[4].axis","4"
+        "pointer[0].axis_value[4].value","92.0"
+        "pointer[0].axis_value[5].axis","5"
+        "pointer[0].axis_value[5].value","82.0"
+        "pointer[0].axis_value[6].axis","6"
+        "pointer[0].axis_value[6].value","92.0"
+        "pointer[0].axis_value[7].axis","7"
+        "pointer[0].axis_value[7].value","82.0"
+        "pointer[0].axis_value[8].axis","8"
+        "pointer[0].axis_value[8].value","0.983282029628754"
+        "pointer[0].pointer_id","0"
+        "pointer[0].tool_type","1"
+        "policy_flags","1644167168"
+        "source","4098"
+        """))
+
+  def test_dispatch_table(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          id, event_id, vsync_id, window_id
+        FROM
+          android_input_event_dispatch;
+        """,
+      out=Csv("""
+        "id","event_id","vsync_id","window_id"
+        0,1141228253,182239,105
+        1,104114844,182239,181
+        2,104114844,182239,58
+        3,104114844,182239,76
+        4,104114844,182239,68
+        5,104114844,0,0
+        6,843076721,182239,181
+        7,843076721,182239,58
+        8,843076721,182239,76
+        9,843076721,182239,68
+        10,843076721,0,0
+        11,744146837,182239,181
+        12,744146837,182239,58
+        13,744146837,182239,76
+        14,744146837,182239,68
+        15,744146837,0,0
+        16,324105269,182239,181
+        17,324105269,0,0
+        18,60594531,182239,181
+        19,60594531,0,0
+        """))
+
+  def test_motion_dispatch_args(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          d.id, args.key, args.display_value
+        FROM
+          android_input_event_dispatch AS d JOIN args ON d.arg_set_id = args.arg_set_id
+        WHERE d.event_id = 104114844
+        ORDER BY d.id, args.key;
+        """,
+      out=Csv("""
+        "id","key","display_value"
+        1,"dispatched_pointer[0].axis_value_in_window[0].axis","0"
+        1,"dispatched_pointer[0].axis_value_in_window[0].value","1762.0"
+        1,"dispatched_pointer[0].axis_value_in_window[1].axis","1"
+        1,"dispatched_pointer[0].axis_value_in_window[1].value","580.0"
+        1,"dispatched_pointer[0].axis_value_in_window[2].axis","8"
+        1,"dispatched_pointer[0].axis_value_in_window[2].value","2.5541570186615"
+        1,"dispatched_pointer[0].pointer_id","0"
+        1,"dispatched_pointer[0].x_in_display","1762.0"
+        1,"dispatched_pointer[0].y_in_display","580.0"
+        1,"event_id","104114844"
+        1,"resolved_flags","0"
+        1,"vsync_id","182239"
+        1,"window_id","181"
+        2,"dispatched_pointer[0].axis_value_in_window[0].axis","0"
+        2,"dispatched_pointer[0].axis_value_in_window[0].value","1718.181640625"
+        2,"dispatched_pointer[0].axis_value_in_window[1].axis","1"
+        2,"dispatched_pointer[0].axis_value_in_window[1].value","600.0"
+        2,"dispatched_pointer[0].axis_value_in_window[2].axis","8"
+        2,"dispatched_pointer[0].axis_value_in_window[2].value","2.55407404899597"
+        2,"dispatched_pointer[0].pointer_id","0"
+        2,"dispatched_pointer[0].x_in_display","1762.0"
+        2,"dispatched_pointer[0].y_in_display","580.0"
+        2,"event_id","104114844"
+        2,"resolved_flags","3"
+        2,"vsync_id","182239"
+        2,"window_id","58"
+        3,"dispatched_pointer[0].axis_value_in_window[0].axis","0"
+        3,"dispatched_pointer[0].axis_value_in_window[0].value","1762.0"
+        3,"dispatched_pointer[0].axis_value_in_window[1].axis","1"
+        3,"dispatched_pointer[0].axis_value_in_window[1].value","580.0"
+        3,"dispatched_pointer[0].axis_value_in_window[2].axis","8"
+        3,"dispatched_pointer[0].axis_value_in_window[2].value","2.5541570186615"
+        3,"dispatched_pointer[0].pointer_id","0"
+        3,"dispatched_pointer[0].x_in_display","1762.0"
+        3,"dispatched_pointer[0].y_in_display","580.0"
+        3,"event_id","104114844"
+        3,"resolved_flags","0"
+        3,"vsync_id","182239"
+        3,"window_id","76"
+        4,"dispatched_pointer[0].axis_value_in_window[0].axis","0"
+        4,"dispatched_pointer[0].axis_value_in_window[0].value","1762.0"
+        4,"dispatched_pointer[0].axis_value_in_window[1].axis","1"
+        4,"dispatched_pointer[0].axis_value_in_window[1].value","580.0"
+        4,"dispatched_pointer[0].axis_value_in_window[2].axis","8"
+        4,"dispatched_pointer[0].axis_value_in_window[2].value","2.5541570186615"
+        4,"dispatched_pointer[0].pointer_id","0"
+        4,"dispatched_pointer[0].x_in_display","1762.0"
+        4,"dispatched_pointer[0].y_in_display","580.0"
+        4,"event_id","104114844"
+        4,"resolved_flags","0"
+        4,"vsync_id","182239"
+        4,"window_id","68"
+        5,"dispatched_pointer[0].axis_value_in_window[0].axis","0"
+        5,"dispatched_pointer[0].axis_value_in_window[0].value","1762.0"
+        5,"dispatched_pointer[0].axis_value_in_window[1].axis","1"
+        5,"dispatched_pointer[0].axis_value_in_window[1].value","580.0"
+        5,"dispatched_pointer[0].axis_value_in_window[2].axis","8"
+        5,"dispatched_pointer[0].axis_value_in_window[2].value","2.5541570186615"
+        5,"dispatched_pointer[0].pointer_id","0"
+        5,"dispatched_pointer[0].x_in_display","1762.0"
+        5,"dispatched_pointer[0].y_in_display","580.0"
+        5,"event_id","104114844"
+        5,"resolved_flags","0"
+        5,"vsync_id","0"
+        5,"window_id","0"
+        """))
+
+  def test_key_dispatch_args(self):
+    return DiffTestBlueprint(
+      trace=Path('input_event_trace.textproto'),
+      query="""
+        INCLUDE PERFETTO MODULE android.input;
+        SELECT
+          d.id, args.key, args.display_value
+        FROM
+          android_input_event_dispatch AS d JOIN args ON d.arg_set_id = args.arg_set_id
+        WHERE d.event_id = 324105269
+        ORDER BY d.id, args.key;
+        """,
+      out=Csv("""
+        "id","key","display_value"
+        16,"event_id","324105269"
+        16,"resolved_flags","8"
+        16,"vsync_id","182239"
+        16,"window_id","181"
+        17,"event_id","324105269"
+        17,"resolved_flags","8"
+        17,"vsync_id","0"
+        17,"window_id","0"
+        """))