tp: add inputmethod parsing

- Extend WinscopeModule to parse inputmethod messages:
    - InputMethodClients
    - InputMethodManagerService
    - InputMethodService
- Populate tables:
    __intrinsic_inputmethod_clients
    __intrinsic_inputmethod_manager_service
    __intrinsic_inputmethod_service
- Add winscope.sql to stdlib to access instrinsic tables

Bug: 276433199
Test: ./tools/ninja -C out/linux_clang_release/ \
    && ./tools/diff_test_trace_processor.py \
          --name-filter="InputMethod" \
          ./out/linux_clang_release/trace_processor_shell
Change-Id: I51376d8b4747efc6e03744b6ce44f37a9eb8cb1c
diff --git a/Android.bp b/Android.bp
index ae8225d..61dea9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12997,6 +12997,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/statsd.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/suspend.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/thread.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql",
         "src/trace_processor/perfetto_sql/stdlib/chrome/**/*.sql",
         "src/trace_processor/perfetto_sql/stdlib/common/args.sql",
         "src/trace_processor/perfetto_sql/stdlib/common/counters.sql",
diff --git a/BUILD b/BUILD
index 5fac4da..1ec9ddd 100644
--- a/BUILD
+++ b/BUILD
@@ -2443,6 +2443,14 @@
     ],
 )
 
+# GN target: //src/trace_processor/perfetto_sql/stdlib/android/winscope:winscope
+perfetto_filegroup(
+    name = "src_trace_processor_perfetto_sql_stdlib_android_winscope_winscope",
+    srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql",
+    ],
+)
+
 # GN target: //src/trace_processor/perfetto_sql/stdlib/android:android
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_android_android",
@@ -2673,6 +2681,7 @@
         ":src_trace_processor_perfetto_sql_stdlib_android_auto_auto",
         ":src_trace_processor_perfetto_sql_stdlib_android_frames_frames",
         ":src_trace_processor_perfetto_sql_stdlib_android_startup_startup",
+        ":src_trace_processor_perfetto_sql_stdlib_android_winscope_winscope",
         ":src_trace_processor_perfetto_sql_stdlib_chrome_chrome_sql",
         ":src_trace_processor_perfetto_sql_stdlib_common_common",
         ":src_trace_processor_perfetto_sql_stdlib_counters_counters",
diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
index a83e5ef..964a6ad 100644
--- a/src/trace_processor/importers/common/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -119,6 +119,21 @@
     return AddArgsTo(context_->storage->mutable_flow_table(), id);
   }
 
+  BoundInserter AddArgsTo(tables::InputMethodClientsTable::Id id) {
+    return AddArgsTo(context_->storage->mutable_inputmethod_clients_table(),
+                     id);
+  }
+
+  BoundInserter AddArgsTo(tables::InputMethodServiceTable::Id id) {
+    return AddArgsTo(context_->storage->mutable_inputmethod_service_table(),
+                     id);
+  }
+
+  BoundInserter AddArgsTo(tables::InputMethodManagerServiceTable::Id id) {
+    return AddArgsTo(
+        context_->storage->mutable_inputmethod_manager_service_table(), id);
+  }
+
   BoundInserter AddArgsTo(tables::MemorySnapshotNodeTable::Id id) {
     return AddArgsTo(context_->storage->mutable_memory_snapshot_node_table(),
                      id);
diff --git a/src/trace_processor/importers/proto/winscope/BUILD.gn b/src/trace_processor/importers/proto/winscope/BUILD.gn
index f557b84..09b7b77 100644
--- a/src/trace_processor/importers/proto/winscope/BUILD.gn
+++ b/src/trace_processor/importers/proto/winscope/BUILD.gn
@@ -39,8 +39,9 @@
     "../../../../../gn:default_deps",
     "../../../../../protos/perfetto/trace:zero",
     "../../../../../protos/perfetto/trace/android:zero",
-    "../../../../../protos/perfetto/trace/android:winscope_regular_zero",
+    "../../../../../protos/perfetto/trace/android:winscope_common_zero",
     "../../../../../protos/perfetto/trace/android:winscope_extensions_zero",
+    "../../../../../protos/perfetto/trace/android:winscope_regular_zero",
     "../../../../../protos/perfetto/trace/interned_data:zero",
     "../../../../../protos/perfetto/trace/profiling:zero",
     "../../../../protozero",
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.cc b/src/trace_processor/importers/proto/winscope/winscope_module.cc
index a59e7eb..8d7e037 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.cc
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.cc
@@ -15,14 +15,21 @@
  */
 
 #include "src/trace_processor/importers/proto/winscope/winscope_module.h"
+#include "protos/perfetto/trace/android/winscope_extensions.pbzero.h"
+#include "protos/perfetto/trace/android/winscope_extensions_impl.pbzero.h"
+#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
+#include "src/trace_processor/importers/proto/winscope/winscope_args_parser.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 using perfetto::protos::pbzero::TracePacket;
+using perfetto::protos::pbzero::WinscopeExtensionsImpl;
 
 WinscopeModule::WinscopeModule(TraceProcessorContext* context)
-    : surfaceflinger_layers_parser_(context),
+    : context_{context},
+      args_parser_{pool_},
+      surfaceflinger_layers_parser_(context),
       surfaceflinger_transactions_parser_(context),
       shell_transitions_parser_(context),
       protolog_parser_(context) {
@@ -34,6 +41,10 @@
   RegisterForField(TracePacket::kShellHandlerMappingsFieldNumber, context);
   RegisterForField(TracePacket::kProtologMessageFieldNumber, context);
   RegisterForField(TracePacket::kProtologViewerConfigFieldNumber, context);
+  RegisterForField(TracePacket::kWinscopeExtensionsFieldNumber, context);
+
+  pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
+                                 kWinscopeDescriptor.size());
 }
 
 void WinscopeModule::ParseTracePacketData(const TracePacket::Decoder& decoder,
@@ -64,6 +75,87 @@
       protolog_parser_.ParseProtoLogViewerConfig(
           decoder.protolog_viewer_config());
       return;
+    case TracePacket::kWinscopeExtensionsFieldNumber:
+      ParseWinscopeExtensionsData(decoder.winscope_extensions(), timestamp);
+      return;
+  }
+}
+
+void WinscopeModule::ParseWinscopeExtensionsData(protozero::ConstBytes blob,
+                                                 int64_t timestamp) {
+  WinscopeExtensionsImpl::Decoder decoder(blob.data, blob.size);
+
+  if (auto field =
+          decoder.Get(WinscopeExtensionsImpl::kInputmethodClientsFieldNumber);
+      field.valid()) {
+    ParseInputMethodClientsData(timestamp, field.as_bytes());
+  } else if (field = decoder.Get(
+                 WinscopeExtensionsImpl::kInputmethodManagerServiceFieldNumber);
+             field.valid()) {
+    ParseInputMethodManagerServiceData(timestamp, field.as_bytes());
+  } else if (field = decoder.Get(
+                 WinscopeExtensionsImpl::kInputmethodServiceFieldNumber);
+             field.valid()) {
+    ParseInputMethodServiceData(timestamp, field.as_bytes());
+  }
+}
+
+void WinscopeModule::ParseInputMethodClientsData(int64_t timestamp,
+                                                 protozero::ConstBytes blob) {
+  tables::InputMethodClientsTable::Row row;
+  row.ts = timestamp;
+  auto rowId =
+      context_->storage->mutable_inputmethod_clients_table()->Insert(row).id;
+
+  ArgsTracker tracker(context_);
+  auto inserter = tracker.AddArgsTo(rowId);
+  WinscopeArgsParser writer(inserter, *context_->storage.get());
+  base::Status status =
+      args_parser_.ParseMessage(blob, kInputMethodClientsProtoName,
+                                nullptr /* parse all fields */, writer);
+  if (!status.ok()) {
+    context_->storage->IncrementStats(
+        stats::winscope_inputmethod_clients_parse_errors);
+  }
+}
+
+void WinscopeModule::ParseInputMethodManagerServiceData(
+    int64_t timestamp,
+    protozero::ConstBytes blob) {
+  tables::InputMethodManagerServiceTable::Row row;
+  row.ts = timestamp;
+  auto rowId = context_->storage->mutable_inputmethod_manager_service_table()
+                   ->Insert(row)
+                   .id;
+
+  ArgsTracker tracker(context_);
+  auto inserter = tracker.AddArgsTo(rowId);
+  WinscopeArgsParser writer(inserter, *context_->storage.get());
+  base::Status status =
+      args_parser_.ParseMessage(blob, kInputMethodManagerServiceProtoName,
+                                nullptr /* parse all fields */, writer);
+  if (!status.ok()) {
+    context_->storage->IncrementStats(
+        stats::winscope_inputmethod_manager_service_parse_errors);
+  }
+}
+
+void WinscopeModule::ParseInputMethodServiceData(int64_t timestamp,
+                                                 protozero::ConstBytes blob) {
+  tables::InputMethodServiceTable::Row row;
+  row.ts = timestamp;
+  auto rowId =
+      context_->storage->mutable_inputmethod_service_table()->Insert(row).id;
+
+  ArgsTracker tracker(context_);
+  auto inserter = tracker.AddArgsTo(rowId);
+  WinscopeArgsParser writer(inserter, *context_->storage.get());
+  base::Status status =
+      args_parser_.ParseMessage(blob, kInputMethodServiceProtoName,
+                                nullptr /* parse all fields */, writer);
+  if (!status.ok()) {
+    context_->storage->IncrementStats(
+        stats::winscope_inputmethod_service_parse_errors);
   }
 }
 
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.h b/src/trace_processor/importers/proto/winscope/winscope_module.h
index 6334411..c77fcb4 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.h
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.h
@@ -41,6 +41,27 @@
                             uint32_t field_id) override;
 
  private:
+  void ParseWinscopeExtensionsData(protozero::ConstBytes blob,
+                                   int64_t timestamp);
+  void ParseInputMethodClientsData(int64_t timestamp,
+                                   protozero::ConstBytes blob);
+  void ParseInputMethodManagerServiceData(int64_t timestamp,
+                                          protozero::ConstBytes blob);
+  void ParseInputMethodServiceData(int64_t timestamp,
+                                   protozero::ConstBytes blob);
+
+  static constexpr auto* kInputMethodClientsProtoName =
+      ".perfetto.protos.InputMethodClientsTraceProto";
+  static constexpr auto* kInputMethodManagerServiceProtoName =
+      ".perfetto.protos.InputMethodManagerServiceTraceProto";
+
+  static constexpr auto* kInputMethodServiceProtoName =
+      ".perfetto.protos.InputMethodServiceTraceProto";
+
+  TraceProcessorContext* const context_;
+  DescriptorPool pool_;
+  util::ProtoToArgsParser args_parser_;
+
   SurfaceFlingerLayersParser surfaceflinger_layers_parser_;
   SurfaceFlingerTransactionsParser surfaceflinger_transactions_parser_;
   ShellTransitionsParser shell_transitions_parser_;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index bd2f18d..feba87c 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -19,6 +19,7 @@
     "auto",
     "frames",
     "startup",
+    "winscope",
   ]
   sources = [
     "anrs.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/winscope/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/winscope/BUILD.gn
new file mode 100644
index 0000000..93abe35
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/winscope/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+import("../../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("winscope") {
+  sources = [
+    "inputmethod.sql",
+  ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql b/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql
new file mode 100644
index 0000000..15a4a8e
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql
@@ -0,0 +1,59 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     https://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.
+
+-- Android inputmethod clients state dumps (from android.inputmethod data source).
+CREATE PERFETTO VIEW android_inputmethod_clients(
+  -- Dump id
+  id INT,
+  -- Timestamp when the dump was triggered
+  ts INT,
+  -- Extra args parsed from the proto message
+  arg_set_id INT
+) AS
+SELECT
+  id,
+  ts,
+  arg_set_id
+FROM __intrinsic_inputmethod_clients;
+
+-- Android inputmethod manager service state dumps (from android.inputmethod data source).
+CREATE PERFETTO VIEW android_inputmethod_manager_service(
+  -- Dump id
+  id INT,
+  -- Timestamp when the dump was triggered
+  ts INT,
+  -- Extra args parsed from the proto message
+  arg_set_id INT
+) AS
+SELECT
+  id,
+  ts,
+  arg_set_id
+FROM __intrinsic_inputmethod_manager_service;
+
+-- Android inputmethod service state dumps (from android.inputmethod data source).
+CREATE PERFETTO VIEW android_inputmethod_service(
+  -- Dump id
+  id INT,
+  -- Timestamp when the dump was triggered
+  ts INT,
+  -- Extra args parsed from the proto message
+  arg_set_id INT
+) AS
+SELECT
+  id,
+  ts,
+  arg_set_id
+FROM __intrinsic_inputmethod_service;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 7495cfc..0584d84 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -298,6 +298,21 @@
   F(v8_unknown_code_type,                 kSingle,  kError,    kAnalysis, ""), \
   F(v8_code_load_missing_code_range,      kSingle,  kError,    kAnalysis,      \
       "V8 load had no code range or an empty one. Event ignored."),            \
+  F(winscope_inputmethod_clients_parse_errors,                                 \
+                                          kSingle,  kInfo,     kAnalysis,      \
+      "InputMethod clients packet has unknown fields, which results in "       \
+      "some arguments missing. You may need a newer version of trace "         \
+      "processor to parse them."),                                             \
+  F(winscope_inputmethod_manager_service_parse_errors,                         \
+                                          kSingle,  kInfo,     kAnalysis,      \
+      "InputMethod manager service packet has unknown fields, which results "  \
+      "in some arguments missing. You may need a newer version of trace "      \
+      "processor to parse them."),                                             \
+  F(winscope_inputmethod_service_parse_errors,                                 \
+                                          kSingle,  kInfo,     kAnalysis,      \
+      "InputMethod service packet has unknown fields, which results in "       \
+      "some arguments missing. You may need a newer version of trace "         \
+      "processor to parse them."),                                             \
   F(winscope_sf_layers_parse_errors,      kSingle,  kInfo,     kAnalysis,      \
       "SurfaceFlinger layers snapshot has unknown fields, which results in "   \
       "some arguments missing. You may need a newer version of trace "         \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 792ad4f..ff280e1 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -797,6 +797,29 @@
   }
   tables::JitFrameTable* mutable_jit_frame_table() { return &jit_frame_table_; }
 
+  const tables::InputMethodClientsTable& inputmethod_clients_table() const {
+    return inputmethod_clients_table_;
+  }
+  tables::InputMethodClientsTable* mutable_inputmethod_clients_table() {
+    return &inputmethod_clients_table_;
+  }
+
+  const tables::InputMethodManagerServiceTable&
+  inputmethod_manager_service_table() const {
+    return inputmethod_manager_service_table_;
+  }
+  tables::InputMethodManagerServiceTable*
+  mutable_inputmethod_manager_service_table() {
+    return &inputmethod_manager_service_table_;
+  }
+
+  const tables::InputMethodServiceTable& inputmethod_service_table() const {
+    return inputmethod_service_table_;
+  }
+  tables::InputMethodServiceTable* mutable_inputmethod_service_table() {
+    return &inputmethod_service_table_;
+  }
+
   const tables::SurfaceFlingerLayersSnapshotTable&
   surfaceflinger_layers_snapshot_table() const {
     return surfaceflinger_layers_snapshot_table_;
@@ -1115,6 +1138,10 @@
   tables::JitFrameTable jit_frame_table_{&string_pool_};
 
   // Winscope tables
+  tables::InputMethodClientsTable inputmethod_clients_table_{&string_pool_};
+  tables::InputMethodManagerServiceTable inputmethod_manager_service_table_{
+      &string_pool_};
+  tables::InputMethodServiceTable inputmethod_service_table_{&string_pool_};
   tables::SurfaceFlingerLayersSnapshotTable
       surfaceflinger_layers_snapshot_table_{&string_pool_};
   tables::SurfaceFlingerLayerTable surfaceflinger_layer_table_{&string_pool_};
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index eade18c..6093c83 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -136,6 +136,9 @@
 V8RegexpCodeTable::~V8RegexpCodeTable() = default;
 
 // winscope_tables_py.h
+InputMethodClientsTable::~InputMethodClientsTable() = default;
+InputMethodManagerServiceTable::~InputMethodManagerServiceTable() = default;
+InputMethodServiceTable::~InputMethodServiceTable() = default;
 SurfaceFlingerLayersSnapshotTable::~SurfaceFlingerLayersSnapshotTable() =
     default;
 SurfaceFlingerLayerTable::~SurfaceFlingerLayerTable() = default;
diff --git a/src/trace_processor/tables/winscope_tables.py b/src/trace_processor/tables/winscope_tables.py
index 8a60e43..8c058aa 100644
--- a/src/trace_processor/tables/winscope_tables.py
+++ b/src/trace_processor/tables/winscope_tables.py
@@ -20,6 +20,54 @@
 from python.generators.trace_processor_table.public import CppUint32
 from python.generators.trace_processor_table.public import CppString
 
+INPUTMETHOD_CLIENTS_TABLE = Table(
+    python_module=__file__,
+    class_name='InputMethodClientsTable',
+    sql_name='__intrinsic_inputmethod_clients',
+    columns=[
+        C('ts', CppInt64()),
+        C('arg_set_id', CppUint32()),
+    ],
+    tabledoc=TableDoc(
+        doc='InputMethod clients',
+        group='Winscope',
+        columns={
+            'ts': 'The timestamp the dump was triggered',
+            'arg_set_id': 'Extra args parsed from the proto message',
+        }))
+
+INPUTMETHOD_MANAGER_SERVICE_TABLE = Table(
+    python_module=__file__,
+    class_name='InputMethodManagerServiceTable',
+    sql_name='__intrinsic_inputmethod_manager_service',
+    columns=[
+        C('ts', CppInt64()),
+        C('arg_set_id', CppUint32()),
+    ],
+    tabledoc=TableDoc(
+        doc='InputMethod manager service',
+        group='Winscope',
+        columns={
+            'ts': 'The timestamp the dump was triggered',
+            'arg_set_id': 'Extra args parsed from the proto message',
+        }))
+
+INPUTMETHOD_SERVICE_TABLE = Table(
+    python_module=__file__,
+    class_name='InputMethodServiceTable',
+    sql_name='__intrinsic_inputmethod_service',
+    columns=[
+        C('ts', CppInt64()),
+        C('arg_set_id', CppUint32()),
+    ],
+    tabledoc=TableDoc(
+        doc='InputMethod service',
+        group='Winscope',
+        columns={
+            'ts': 'The timestamp the dump was triggered',
+            'arg_set_id': 'Extra args parsed from the proto message',
+        }))
+
 SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE = Table(
     python_module=__file__,
     class_name='SurfaceFlingerLayersSnapshotTable',
@@ -128,6 +176,9 @@
 # Keep this list sorted.
 ALL_TABLES = [
     PROTOLOG_TABLE,
+    INPUTMETHOD_CLIENTS_TABLE,
+    INPUTMETHOD_MANAGER_SERVICE_TABLE,
+    INPUTMETHOD_SERVICE_TABLE,
     SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE,
     SURFACE_FLINGER_LAYER_TABLE,
     SURFACE_FLINGER_TRANSACTIONS_TABLE,
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 991079a..bbbecac 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -873,6 +873,10 @@
   RegisterStaticTable(storage->jit_code_table());
   RegisterStaticTable(storage->jit_frame_table());
 
+  RegisterStaticTable(storage->inputmethod_clients_table());
+  RegisterStaticTable(storage->inputmethod_manager_service_table());
+  RegisterStaticTable(storage->inputmethod_service_table());
+
   RegisterStaticTable(storage->surfaceflinger_layers_snapshot_table());
   RegisterStaticTable(storage->surfaceflinger_layer_table());
   RegisterStaticTable(storage->surfaceflinger_transactions_table());
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index cb82c55..dd8fade 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -50,6 +50,9 @@
 from diff_tests.parser.android.tests import AndroidParser
 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
+from diff_tests.parser.android.tests_inputmethod_manager_service import InputMethodManagerService
+from diff_tests.parser.android.tests_inputmethod_service import InputMethodService
 from diff_tests.parser.android.tests_protolog import ProtoLog
 from diff_tests.parser.android.tests_shell_transitions import ShellTransitions
 from diff_tests.parser.android.tests_surfaceflinger_layers import SurfaceFlingerLayers
@@ -183,6 +186,12 @@
                            'SmokeComputeMetrics').fetch(),
       *SmokeJson(index_path, 'parser/smoke', 'SmokeJson').fetch(),
       *SmokeSchedEvents(index_path, 'parser/smoke', 'SmokeSchedEvents').fetch(),
+      *InputMethodClients(index_path, 'parser/android',
+                            'InputMethodClients').fetch(),
+      *InputMethodManagerService(index_path, 'parser/android',
+                            'InputMethodManagerService').fetch(),
+      *InputMethodService(index_path, 'parser/android',
+                            'InputMethodService').fetch(),
       *SurfaceFlingerLayers(index_path, 'parser/android',
                             'SurfaceFlingerLayers').fetch(),
       *SurfaceFlingerTransactions(index_path, 'parser/android',
diff --git a/test/trace_processor/diff_tests/parser/android/inputmethod_clients.textproto b/test/trace_processor/diff_tests/parser/android/inputmethod_clients.textproto
new file mode 100644
index 0000000..0d33da4
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/inputmethod_clients.textproto
@@ -0,0 +1,247 @@
+packet {
+  trusted_uid: 9999
+  clock_snapshot {
+    clocks {
+      clock_id: 6
+      timestamp: 118567353207
+    }
+    clocks {
+      clock_id: 2
+      timestamp: 1710943499588000057
+    }
+    clocks {
+      clock_id: 4
+      timestamp: 118562961198
+    }
+    clocks {
+      clock_id: 1
+      timestamp: 1710943499592392595
+    }
+    clocks {
+      clock_id: 3
+      timestamp: 118567353939
+    }
+    clocks {
+      clock_id: 5
+      timestamp: 118567354265
+    }
+    primary_trace_clock: BUILTIN_CLOCK_BOOTTIME
+  }
+  trusted_packet_sequence_id: 1
+}
+
+packet {
+  trusted_uid: 10254
+  timestamp: 119232512509
+  trusted_packet_sequence_id: 2
+  previous_packet_dropped: true
+  trusted_pid: 2798
+  first_packet_on_sequence: true
+  winscope_extensions {
+    [perfetto.protos.WinscopeExtensionsImpl.inputmethod_clients] {
+      where: "InputMethodManager#showSoftInput"
+      client {
+        input_method_manager {
+          cur_id: "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+          active: true
+          served_view: "com.google.android.apps.nexuslauncher.search.SearchEditText{ba34590 VFED..CL. .F....ID 0,0-688,151 #7f0a01e6 app:id/input aid=1073741824}"
+          next_served_view: "com.google.android.apps.nexuslauncher.search.SearchEditText{ba34590 VFED..CL. .F....ID 0,0-688,151 #7f0a01e6 app:id/input aid=1073741824}"
+        }
+        view_root_impl {
+          view: "com.android.internal.policy.DecorView{1cd90e0 V.E...... R.....ID 0,0-1080,2400 aid=0}[NexusLauncherActivity]"
+          app_visible: true
+          width: 1080
+          height: 2400
+          added: true
+          win_frame {
+            right: 1080
+            bottom: 2400
+          }
+          last_window_insets: "WindowInsets{\n    statusBars=Insets{left=0, top=128, right=0, bottom=0} max=Insets{left=0, top=128, right=0, bottom=0} vis=true boundingRects=[Rect(0, 0 - 1080, 128)] maxBoundingRects=[Rect(0, 0 - 1080, 128)]\n    navigationBars=Insets{left=0, top=0, right=0, bottom=63} max=Insets{left=0, top=0, right=0, bottom=63} vis=true boundingRects=[Rect(0, 2337 - 1080, 2400)] maxBoundingRects=[Rect(0, 2337 - 1080, 2400)]\n    captionBar=null max=null vis=false boundingRects=null maxBoundingRects=null\n    ime=null max=null vis=false boundingRects=null maxBoundingRects=null\n    systemGestures=Insets{left=78, top=160, right=78, bottom=84} max=Insets{left=78, top=160, right=78, bottom=84} vis=true boundingRects=[Rect(0, 0 - 1080, 160), Rect(1002, 0 - 1080, 2400), Rect(0, 2316 - 1080, 2400), Rect(0, 0 - 78, 2400)] maxBoundingRects=[Rect(0, 0 - 1080, 160), Rect(1002, 0 - 1080, 2400), Rect(0, 2316 - 1080, 2400), Rect(0, 0 - 78, 2400)]\n    mandatorySystemGestures=Insets{left=0, top=160, right=0, bottom=84} max=Insets{left=0, top=160, right=0, bottom=84} vis=true boundingRects=[Rect(0, 0 - 1080, 160), Rect(0, 2316 - 1080, 2400)] maxBoundingRects=[Rect(0, 0 - 1080, 160), Rect(0, 2316 - 1080, 2400)]\n    tappableElement=Insets{left=0, top=128, right=0, bottom=0} max=Insets{left=0, top=128, right=0, bottom=0} vis=true boundingRects=[Rect(0, 0 - 1080, 128)] maxBoundingRects=[Rect(0, 0 - 1080, 128)]\n    displayCutout=Insets{left=0, top=128, right=0, bottom=0} max=Insets{left=0, top=128, right=0, bottom=0} vis=true boundingRects=[Rect(0, 0 - 1080, 128)] maxBoundingRects=[Rect(0, 0 - 1080, 128)]\n    windowDecor=null max=null vis=false boundingRects=null maxBoundingRects=null\n    systemOverlays=null max=null vis=false boundingRects=null maxBoundingRects=null\n    cutout=DisplayCutout{insets=Rect(0, 128 - 0, 0) waterfall=Insets{left=0, top=0, right=0, bottom=0} boundingRect={Bounds=[Rect(0, 0 - 0, 0), Rect(492, 0 - 610, 128), Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0)]} cutoutPathParserInfo={CutoutPathParserInfo{displayWidth=1080 displayHeight=2400 physicalDisplayWidth=1080 physicalDisplayHeight=2400 density={2.625} cutoutSpec={M 507,64 a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0 Z @left} rotation={0} scale={1.0} physicalPixelDisplaySizeRatio={1.0}}} sideOverrides={}}\n    roundedCorners=RoundedCorners{[RoundedCorner{position=TopLeft, radius=28, center=Point(28, 28)}, RoundedCorner{position=TopRight, radius=28, center=Point(1052, 28)}, RoundedCorner{position=BottomRight, radius=28, center=Point(1052, 2372)}, RoundedCorner{position=BottomLeft, radius=28, center=Point(28, 2372)}]}\n    privacyIndicatorBounds=PrivacyIndicatorBounds {static bounds=Rect(827, 0 - 1043, 128) rotation=0}\n    displayShape=DisplayShape{ spec=-2047108559 displayWidth=1080 displayHeight=2400 physicalPixelDisplaySizeRatio=1.0 rotation=0 offsetX=0 offsetY=0 scale=1.0}\n    forceConsumingTypes=\n    suppressScrimTypes=navigationBars\n    compatInsetsTypes=statusBars navigationBars captionBar displayCutout systemOverlays\n    compatIgnoreVisibility=true\n    systemWindowInsetsConsumed=false\n    stableInsetsConsumed=false\n    displayCutoutConsumed=false\n    \n    frameWidth=1080\n    frameHeight=2400}"
+          soft_input_mode: "STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+          window_attributes {
+            type: 1
+            width: -1
+            height: -1
+            soft_input_mode: 48
+            format: TRANSPARENT
+            window_animations: 16974588
+            alpha: 1
+            screen_brightness: -1
+            button_brightness: -1
+            user_activity_timeout: -1
+            flags: 2173763840
+            private_flags: 268468800
+            subtree_system_ui_visibility_flags: 1792
+            behavior: 1
+          }
+        }
+        insets_controller {
+          state {
+            sources {
+              visible_frame {
+                top: 2274
+                right: 1080
+                bottom: 2400
+              }
+              type_number: 8
+            }
+            display_frame {
+              right: 1080
+              bottom: 2400
+            }
+            display_cutout {
+              insets {
+                top: 128
+              }
+              bound_top {
+                left: 492
+                right: 610
+                bottom: 128
+              }
+              side_overrides: -1
+              side_overrides: -1
+              side_overrides: -1
+              side_overrides: -1
+            }
+          }
+        }
+        ime_insets_source_consumer {
+          insets_source_consumer {
+            has_window_focus: true
+            is_requested_visible: true
+            source_control {
+              position {
+                y: 128
+              }
+              leash {
+                hash_code: 135479902
+                name: "Surface(name=cff3eb3 InputMethod)/@0xf85eda5 - animation-leash of insets_animation"
+                layerId: 105
+              }
+              type_number: 8
+            }
+            type_number: 8
+          }
+        }
+        editor_info {
+          input_type: 1
+          ime_options: 33554435
+          private_ime_options: "com.google.android.inputmethod.latin.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.canary.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.dev.appSupportsSmartComposeAndDel"
+          package_name: "com.google.android.apps.nexuslauncher"
+          field_id: 2131362278
+        }
+        ime_focus_controller {
+          has_ime_focus: true
+        }
+      }
+    }
+  }
+}
+
+packet {
+  trusted_uid: 10254
+  timestamp: 119237883196
+  trusted_packet_sequence_id: 2
+  trusted_pid: 2798
+
+  winscope_extensions {
+    [perfetto.protos.WinscopeExtensionsImpl.inputmethod_clients] {
+      where: "InputMethodManager#showSoftInput"
+      client {
+        input_method_manager {
+          cur_id: "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+          active: true
+          served_view: "com.google.android.apps.nexuslauncher.search.SearchEditText{ba34590 VFED..CL. .F....ID 0,0-688,151 #7f0a01e6 app:id/input aid=1073741824}"
+          next_served_view: "com.google.android.apps.nexuslauncher.search.SearchEditText{ba34590 VFED..CL. .F....ID 0,0-688,151 #7f0a01e6 app:id/input aid=1073741824}"
+        }
+        view_root_impl {
+          view: "com.android.internal.policy.DecorView{1cd90e0 V.E...... R.....ID 0,0-1080,2400 aid=0}[NexusLauncherActivity]"
+          app_visible: true
+          width: 1080
+          height: 2400
+          added: true
+          win_frame {
+            right: 1080
+            bottom: 2400
+          }
+          last_window_insets: "WindowInsets{\n    statusBars=Insets{left=0, top=128, right=0, bottom=0} max=Insets{left=0, top=128, right=0, bottom=0} vis=true boundingRects=[Rect(0, 0 - 1080, 128)] maxBoundingRects=[Rect(0, 0 - 1080, 128)]\n    navigationBars=Insets{left=0, top=0, right=0, bottom=63} max=Insets{left=0, top=0, right=0, bottom=63} vis=true boundingRects=[Rect(0, 2337 - 1080, 2400)] maxBoundingRects=[Rect(0, 2337 - 1080, 2400)]\n    captionBar=null max=null vis=false boundingRects=null maxBoundingRects=null\n    ime=null max=null vis=false boundingRects=null maxBoundingRects=null\n    systemGestures=Insets{left=78, top=160, right=78, bottom=84} max=Insets{left=78, top=160, right=78, bottom=84} vis=true boundingRects=[Rect(0, 0 - 1080, 160), Rect(1002, 0 - 1080, 2400), Rect(0, 2316 - 1080, 2400), Rect(0, 0 - 78, 2400)] maxBoundingRects=[Rect(0, 0 - 1080, 160), Rect(1002, 0 - 1080, 2400), Rect(0, 2316 - 1080, 2400), Rect(0, 0 - 78, 2400)]\n    mandatorySystemGestures=Insets{left=0, top=160, right=0, bottom=84} max=Insets{left=0, top=160, right=0, bottom=84} vis=true boundingRects=[Rect(0, 0 - 1080, 160), Rect(0, 2316 - 1080, 2400)] maxBoundingRects=[Rect(0, 0 - 1080, 160), Rect(0, 2316 - 1080, 2400)]\n    tappableElement=Insets{left=0, top=128, right=0, bottom=0} max=Insets{left=0, top=128, right=0, bottom=0} vis=true boundingRects=[Rect(0, 0 - 1080, 128)] maxBoundingRects=[Rect(0, 0 - 1080, 128)]\n    displayCutout=Insets{left=0, top=128, right=0, bottom=0} max=Insets{left=0, top=128, right=0, bottom=0} vis=true boundingRects=[Rect(0, 0 - 1080, 128)] maxBoundingRects=[Rect(0, 0 - 1080, 128)]\n    windowDecor=null max=null vis=false boundingRects=null maxBoundingRects=null\n    systemOverlays=null max=null vis=false boundingRects=null maxBoundingRects=null\n    cutout=DisplayCutout{insets=Rect(0, 128 - 0, 0) waterfall=Insets{left=0, top=0, right=0, bottom=0} boundingRect={Bounds=[Rect(0, 0 - 0, 0), Rect(492, 0 - 610, 128), Rect(0, 0 - 0, 0), Rect(0, 0 - 0, 0)]} cutoutPathParserInfo={CutoutPathParserInfo{displayWidth=1080 displayHeight=2400 physicalDisplayWidth=1080 physicalDisplayHeight=2400 density={2.625} cutoutSpec={M 507,64 a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0 Z @left} rotation={0} scale={1.0} physicalPixelDisplaySizeRatio={1.0}}} sideOverrides={}}\n    roundedCorners=RoundedCorners{[RoundedCorner{position=TopLeft, radius=28, center=Point(28, 28)}, RoundedCorner{position=TopRight, radius=28, center=Point(1052, 28)}, RoundedCorner{position=BottomRight, radius=28, center=Point(1052, 2372)}, RoundedCorner{position=BottomLeft, radius=28, center=Point(28, 2372)}]}\n    privacyIndicatorBounds=PrivacyIndicatorBounds {static bounds=Rect(827, 0 - 1043, 128) rotation=0}\n    displayShape=DisplayShape{ spec=-2047108559 displayWidth=1080 displayHeight=2400 physicalPixelDisplaySizeRatio=1.0 rotation=0 offsetX=0 offsetY=0 scale=1.0}\n    forceConsumingTypes=\n    suppressScrimTypes=navigationBars\n    compatInsetsTypes=statusBars navigationBars captionBar displayCutout systemOverlays\n    compatIgnoreVisibility=true\n    systemWindowInsetsConsumed=false\n    stableInsetsConsumed=false\n    displayCutoutConsumed=false\n    \n    frameWidth=1080\n    frameHeight=2400}"
+          soft_input_mode: "STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+          window_attributes {
+            type: 1
+            width: -1
+            height: -1
+            soft_input_mode: 48
+            format: TRANSPARENT
+            window_animations: 16974588
+            alpha: 1
+            screen_brightness: -1
+            button_brightness: -1
+            user_activity_timeout: -1
+            flags: 2173763840
+            private_flags: 268468800
+            subtree_system_ui_visibility_flags: 1792
+            behavior: 1
+          }
+        }
+        insets_controller {
+          state {
+            sources {
+              visible_frame {
+                top: 2274
+                right: 1080
+                bottom: 2400
+              }
+              type_number: 8
+            }
+            display_frame {
+              right: 1080
+              bottom: 2400
+            }
+            display_cutout {
+              insets {
+                top: 128
+              }
+              bound_top {
+                left: 492
+                right: 610
+                bottom: 128
+              }
+              side_overrides: -1
+              side_overrides: -1
+              side_overrides: -1
+              side_overrides: -1
+            }
+          }
+        }
+        ime_insets_source_consumer {
+          insets_source_consumer {
+            has_window_focus: true
+            source_control {
+              position {
+                y: 128
+              }
+              leash {
+                hash_code: 135479902
+                name: "Surface(name=cff3eb3 InputMethod)/@0xf85eda5 - animation-leash of insets_animation"
+                layerId: 105
+              }
+              type_number: 8
+            }
+            type_number: 8
+          }
+        }
+        editor_info {
+          input_type: 1
+          ime_options: 33554435
+          private_ime_options: "com.google.android.inputmethod.latin.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.canary.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.dev.appSupportsSmartComposeAndDel"
+          package_name: "com.google.android.apps.nexuslauncher"
+          field_id: 2131362278
+        }
+        ime_focus_controller {
+          has_ime_focus: true
+        }
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/inputmethod_manager_service.textproto b/test/trace_processor/diff_tests/parser/android/inputmethod_manager_service.textproto
new file mode 100644
index 0000000..9626b31
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/inputmethod_manager_service.textproto
@@ -0,0 +1,97 @@
+packet {
+    clock_snapshot {
+        primary_trace_clock: BUILTIN_CLOCK_BOOTTIME
+        clocks {
+            clock_id: 6
+            timestamp: 39116317767
+        }
+        clocks {
+            clock_id: 2
+            timestamp: 1711013588124000018
+        }
+        clocks {
+            clock_id: 4
+            timestamp: 39115297096
+        }
+        clocks {
+            clock_id: 1
+            timestamp: 1711013588125020892
+        }
+        clocks {
+            clock_id: 3
+            timestamp: 39116318011
+        }
+        clocks {
+            clock_id: 5
+            timestamp: 39116318093
+        }
+    }
+    trusted_uid: 9999
+    trusted_packet_sequence_id: 1
+}
+
+packet {
+    first_packet_on_sequence: true
+    timestamp: 39998329771
+    winscope_extensions {
+        [perfetto.protos.WinscopeExtensionsImpl.inputmethod_manager_service] {
+            where: "InputMethodManagerService#startInputOrWindowGainedFocus"
+            input_method_manager_service {
+                cur_method_id: "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+                cur_seq: 1
+                cur_client: "ClientState{6c16bdd mUid=10254 mPid=2790 mSelfReportedDisplayId=0}"
+                cur_focused_window_name: "a74954b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+                cur_focused_window_soft_input_mode: "STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+                last_ime_target_window_name: "a74954b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+                cur_focused_window_soft_input_mode: "STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+                cur_attribute {
+                    package_name: "com.google.android.apps.nexuslauncher"
+                }
+                cur_id: "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+                cur_token: "android.os.Binder@3abdbc4"
+                system_ready: true
+                have_connection: true
+                bound_to_method: true
+                is_interactive: true
+            }
+        }
+    }
+    trusted_uid: 1000
+    trusted_packet_sequence_id: 4
+    trusted_pid: 1678
+    previous_packet_dropped: true
+}
+
+packet {
+    timestamp: 40003054136
+    winscope_extensions {
+        [perfetto.protos.WinscopeExtensionsImpl.inputmethod_manager_service] {
+            where: "InputMethodManagerService#showSoftInput"
+            input_method_manager_service {
+                cur_method_id: "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+                cur_seq: 2
+                cur_client: "ClientState{6c16bdd mUid=10254 mPid=2790 mSelfReportedDisplayId=0}"
+                cur_focused_window_name: "a74954b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+                cur_focused_window_soft_input_mode: "STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+                last_ime_target_window_name: "a74954b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+                cur_focused_window_soft_input_mode: "STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+                cur_attribute {
+                    input_type: 1
+                    ime_options: 33554435
+                    private_ime_options: "com.google.android.inputmethod.latin.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.canary.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.dev.appSupportsSmartComposeAndDel"
+                    package_name: "com.google.android.apps.nexuslauncher"
+                    field_id: 2131362278
+                }
+                cur_id: "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+                cur_token: "android.os.Binder@3abdbc4"
+                system_ready: true
+                have_connection: true
+                bound_to_method: true
+                is_interactive: true
+            }
+        }
+    }
+    trusted_uid: 1000
+    trusted_packet_sequence_id: 4
+    trusted_pid: 1678
+}
diff --git a/test/trace_processor/diff_tests/parser/android/inputmethod_service.textproto b/test/trace_processor/diff_tests/parser/android/inputmethod_service.textproto
new file mode 100644
index 0000000..463913a
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/inputmethod_service.textproto
@@ -0,0 +1,94 @@
+packet {
+  trusted_uid: 9999
+  clock_snapshot {
+    clocks {
+      clock_id: 6
+      timestamp: 61175548939
+    }
+    clocks {
+      clock_id: 2
+      timestamp: 1710950518184000029
+    }
+    clocks {
+      clock_id: 4
+      timestamp: 61168655343
+    }
+    clocks {
+      clock_id: 1
+      timestamp: 1710950518190893869
+    }
+    clocks {
+      clock_id: 3
+      timestamp: 61175549223
+    }
+    clocks {
+      clock_id: 5
+      timestamp: 61175549468
+    }
+    primary_trace_clock: BUILTIN_CLOCK_BOOTTIME
+  }
+  trusted_packet_sequence_id: 1
+}
+
+packet {
+  trusted_uid: 10215
+  timestamp: 61829562285
+  trusted_packet_sequence_id: 4
+  previous_packet_dropped: true
+  trusted_pid: 4233
+  first_packet_on_sequence: true
+  winscope_extensions {
+    [perfetto.protos.WinscopeExtensionsImpl.inputmethod_service] {
+      where: "InputMethodService#doFinishInput"
+      input_method_service {
+        soft_input_window {
+          window_state: 2
+        }
+        configuration: "{1.0 ?mcc0mnc [en_US] ldltr sw411dp w411dp h842dp 420dpi nrml long hdr widecg port night finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 128 - 1080, 2337) mMaxBounds=Rect(0, 0 - 1080, 2400) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.11 fontWeightAdjustment=0}"
+        token: "android.os.BinderProxy@50043d1"
+        input_binding: "InputBinding{android.os.BinderProxy@cbb5415 / uid 10254 / pid 2812}"
+        input_started: true
+        input_editor_info {
+          package_name: "com.google.android.apps.nexuslauncher"
+        }
+        candidates_visibility: 4
+        last_computed_insets {
+          content_top_insets: 126
+          touchable_insets: 2
+          touchable_region: "SkRegion()"
+        }
+        settings_observer: "SettingsObserver{mShowImeWithHardKeyboard=1}"
+      }
+    }
+  }
+}
+
+packet {
+  trusted_uid: 10215
+  timestamp: 61831101307
+  trusted_packet_sequence_id: 4
+  trusted_pid: 4233
+  winscope_extensions {
+    [perfetto.protos.WinscopeExtensionsImpl.inputmethod_service] {
+      where: "InputMethodService#doStartInput"
+      input_method_service {
+        soft_input_window {
+          window_state: 2
+        }
+        configuration: "{1.0 ?mcc0mnc [en_US] ldltr sw411dp w411dp h842dp 420dpi nrml long hdr widecg port night finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 128 - 1080, 2337) mMaxBounds=Rect(0, 0 - 1080, 2400) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.11 fontWeightAdjustment=0}"
+        token: "android.os.BinderProxy@50043d1"
+        input_binding: "InputBinding{android.os.BinderProxy@cbb5415 / uid 10254 / pid 2812}"
+        input_editor_info {
+          package_name: "com.google.android.apps.nexuslauncher"
+        }
+        candidates_visibility: 4
+        last_computed_insets {
+          content_top_insets: 126
+          touchable_insets: 2
+          touchable_region: "SkRegion()"
+        }
+        settings_observer: "SettingsObserver{mShowImeWithHardKeyboard=1}"
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py
new file mode 100644
index 0000000..74b2760
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py
@@ -0,0 +1,65 @@
+#!/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 InputMethodClients(TestSuite):
+
+  def test_has_expected_rows(self):
+    return DiffTestBlueprint(
+        trace=Path('inputmethod_clients.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+        SELECT
+          id, ts
+        FROM
+          android_inputmethod_clients;
+        """,
+        out=Csv("""
+        "id","ts"
+        0,119232512509
+        1,119237883196
+        """))
+
+  def test_has_expected_args(self):
+    return DiffTestBlueprint(
+        trace=Path('inputmethod_clients.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+        SELECT
+          args.key, args.display_value
+        FROM
+          android_inputmethod_clients AS imc JOIN args ON imc.arg_set_id = args.arg_set_id
+        WHERE imc.id = 0
+        ORDER BY args.key
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "key","display_value"
+        "client.editor_info.field_id","2131362278"
+        "client.editor_info.ime_options","33554435"
+        "client.editor_info.input_type","1"
+        "client.editor_info.package_name","com.google.android.apps.nexuslauncher"
+        "client.editor_info.private_ime_options","com.google.android.inputmethod.latin.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.canary.appSupportsSmartComposeAndDel,com.google.android.inputmethod.latin.dev.appSupportsSmartComposeAndDel"
+        "client.ime_focus_controller.has_ime_focus","true"
+        "client.ime_insets_source_consumer.insets_source_consumer.has_window_focus","true"
+        "client.ime_insets_source_consumer.insets_source_consumer.is_requested_visible","true"
+        "client.ime_insets_source_consumer.insets_source_consumer.source_control.leash.hash_code","135479902"
+        "client.ime_insets_source_consumer.insets_source_consumer.source_control.leash.layerId","105"
+        """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py
new file mode 100644
index 0000000..612f5b8
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py
@@ -0,0 +1,68 @@
+#!/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 InputMethodManagerService(TestSuite):
+
+  def test_has_expected_rows(self):
+    return DiffTestBlueprint(
+        trace=Path('inputmethod_manager_service.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+        SELECT
+          id, ts
+        FROM
+          android_inputmethod_manager_service;
+        """,
+        out=Csv("""
+        "id","ts"
+        0,39998329771
+        1,40003054136
+        """))
+
+  def test_has_expected_args(self):
+    return DiffTestBlueprint(
+        trace=Path('inputmethod_manager_service.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+        SELECT
+          args.key, args.display_value
+        FROM
+          android_inputmethod_manager_service AS imms JOIN args ON imms.arg_set_id = args.arg_set_id
+        WHERE imms.id = 0
+        ORDER BY args.key;
+        """,
+        out=Csv("""
+        "key","display_value"
+        "input_method_manager_service.bound_to_method","true"
+        "input_method_manager_service.cur_attribute.package_name","com.google.android.apps.nexuslauncher"
+        "input_method_manager_service.cur_client","ClientState{6c16bdd mUid=10254 mPid=2790 mSelfReportedDisplayId=0}"
+        "input_method_manager_service.cur_focused_window_name","a74954b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+        "input_method_manager_service.cur_focused_window_soft_input_mode","STATE_UNSPECIFIED|ADJUST_NOTHING|IS_FORWARD_NAVIGATION"
+        "input_method_manager_service.cur_id","com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+        "input_method_manager_service.cur_method_id","com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME"
+        "input_method_manager_service.cur_seq","1"
+        "input_method_manager_service.cur_token","android.os.Binder@3abdbc4"
+        "input_method_manager_service.have_connection","true"
+        "input_method_manager_service.is_interactive","true"
+        "input_method_manager_service.last_ime_target_window_name","a74954b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+        "input_method_manager_service.system_ready","true"
+        "where","InputMethodManagerService#startInputOrWindowGainedFocus"
+        """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py
new file mode 100644
index 0000000..4ec909c
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py
@@ -0,0 +1,66 @@
+#!/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 InputMethodService(TestSuite):
+
+  def test_has_expected_rows(self):
+    return DiffTestBlueprint(
+        trace=Path('inputmethod_service.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+        SELECT
+          id, ts
+        FROM
+          android_inputmethod_service;
+        """,
+        out=Csv("""
+        "id","ts"
+        0,61829562285
+        1,61831101307
+        """))
+
+  def test_has_expected_args(self):
+    return DiffTestBlueprint(
+        trace=Path('inputmethod_service.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+        SELECT
+          args.key, args.display_value
+        FROM
+          android_inputmethod_service AS ims JOIN args ON ims.arg_set_id = args.arg_set_id
+        WHERE ims.id = 0
+        ORDER BY args.key;
+        """,
+        out=Csv("""
+        "key","display_value"
+        "input_method_service.candidates_visibility","4"
+        "input_method_service.configuration","{1.0 ?mcc0mnc [en_US] ldltr sw411dp w411dp h842dp 420dpi nrml long hdr widecg port night finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2400) mAppBounds=Rect(0, 128 - 1080, 2337) mMaxBounds=Rect(0, 0 - 1080, 2400) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.11 fontWeightAdjustment=0}"
+        "input_method_service.input_binding","InputBinding{android.os.BinderProxy@cbb5415 / uid 10254 / pid 2812}"
+        "input_method_service.input_editor_info.package_name","com.google.android.apps.nexuslauncher"
+        "input_method_service.input_started","true"
+        "input_method_service.last_computed_insets.content_top_insets","126"
+        "input_method_service.last_computed_insets.touchable_insets","2"
+        "input_method_service.last_computed_insets.touchable_region","SkRegion()"
+        "input_method_service.settings_observer","SettingsObserver{mShowImeWithHardKeyboard=1}"
+        "input_method_service.soft_input_window.window_state","2"
+        "input_method_service.token","android.os.BinderProxy@50043d1"
+        "where","InputMethodService#doFinishInput"
+        """))