diff --git a/base/android/scoped_input_event.cc b/base/android/scoped_input_event.cc
index 5ec87678ad13e..10d2fe4ee62a1 100644
--- a/base/android/scoped_input_event.cc
+++ b/base/android/scoped_input_event.cc
@@ -40,6 +40,38 @@ ScopedInputEvent& ScopedInputEvent::operator=(ScopedInputEvent&& other) {
   return *this;
 }
 
+#if BUILDFLAG(ENABLE_BASE_TRACING)
+void ScopedInputEvent::WriteIntoTrace(
+    perfetto::TracedProto<perfetto::protos::pbzero::EventForwarder> forwarder)
+    const {
+  if (!a_input_event_) {
+    return;
+  }
+
+  const int history_size =
+      static_cast<const int>(AMotionEvent_getHistorySize(a_input_event_));
+  forwarder->set_history_size(history_size);
+
+  forwarder->set_latest_time_ns(AMotionEvent_getEventTime(a_input_event_));
+  if (history_size > 0) {
+    forwarder->set_oldest_time_ns(AMotionEvent_getHistoricalEventTime(
+        a_input_event_, /* history_index= */ 0));
+  }
+  forwarder->set_down_time_ns(AMotionEvent_getDownTime(a_input_event_));
+
+  forwarder->set_x_pixel(
+      AMotionEvent_getX(a_input_event_, /* pointer_index= */ 0));
+  forwarder->set_y_pixel(
+      AMotionEvent_getY(a_input_event_, /* pointer_index= */ 0));
+
+  const int action =
+      AMotionEvent_getAction(a_input_event_) & AMOTION_EVENT_ACTION_MASK;
+  forwarder->set_action(
+      static_cast<perfetto::protos::pbzero::EventForwarder::AMotionEventAction>(
+          action));
+}
+#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
+
 void ScopedInputEvent::DestroyIfNeeded() {
   if (a_input_event_ == nullptr) {
     return;
diff --git a/base/android/scoped_input_event.h b/base/android/scoped_input_event.h
index 0efb4c0a1c17e..2a81cda82ec8c 100644
--- a/base/android/scoped_input_event.h
+++ b/base/android/scoped_input_event.h
@@ -9,6 +9,7 @@
 
 #include "base/base_export.h"
 #include "base/memory/raw_ptr.h"
+#include "base/trace_event/base_tracing.h"
 
 namespace base::android {
 
@@ -31,6 +32,12 @@ class BASE_EXPORT ScopedInputEvent {
 
   const AInputEvent* a_input_event() const { return a_input_event_.get(); }
 
+#if BUILDFLAG(ENABLE_BASE_TRACING)
+  void WriteIntoTrace(
+      perfetto::TracedProto<perfetto::protos::pbzero::EventForwarder> forwarder)
+      const;
+#endif  // BUILDFLAG(ENABLE_BASE_TRACING)
+
  private:
   void DestroyIfNeeded();
 
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index 2b1c79436cd79..ebf42b9e5acd6 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -1677,7 +1677,7 @@ message ScrollMetrics {
 //
 // All data comes from MotionEvent getters so read these for more context:
 // https://developer.android.com/reference/android/view/MotionEvent
-// Next id: 9
+// Next id: 10
 message EventForwarder {
   // The events getHistorySize().
   optional int32 history_size = 1;
@@ -1696,6 +1696,24 @@ message EventForwarder {
   optional bool has_x_movement = 6;
   // Determine if the previous forwarded event changed y coordinate.
   optional bool has_y_movement = 7;
+
+  // The enum comes from Android NDK header: `android/input.h`.
+  enum AMotionEventAction {
+    AMOTION_EVENT_ACTION_DOWN = 0;
+    AMOTION_EVENT_ACTION_UP = 1;
+    AMOTION_EVENT_ACTION_MOVE = 2;
+    AMOTION_EVENT_ACTION_CANCEL = 3;
+    AMOTION_EVENT_ACTION_OUTSIDE = 4;
+    AMOTION_EVENT_ACTION_POINTER_DOWN = 5;
+    AMOTION_EVENT_ACTION_POINTER_UP = 6;
+    AMOTION_EVENT_ACTION_HOVER_MOVE = 7;
+    AMOTION_EVENT_ACTION_SCROLL = 8;
+    AMOTION_EVENT_ACTION_HOVER_ENTER = 9;
+    AMOTION_EVENT_ACTION_HOVER_EXIT = 10;
+    AMOTION_EVENT_ACTION_BUTTON_PRESS = 11;
+    AMOTION_EVENT_ACTION_BUTTON_RELEASE = 12;
+  }
+  optional AMotionEventAction action = 9;
 }
 
 // TouchDispositionGestureFilter is a class on android that detects and forwards
diff --git a/components/viz/service/input/android_state_transfer_handler.cc b/components/viz/service/input/android_state_transfer_handler.cc
index b83bfaf0b7937..4d5b691af5435 100644
--- a/components/viz/service/input/android_state_transfer_handler.cc
+++ b/components/viz/service/input/android_state_transfer_handler.cc
@@ -96,7 +96,14 @@ void AndroidStateTransferHandler::StateOnTouchTransfer(
 bool AndroidStateTransferHandler::OnMotionEvent(
     base::android::ScopedInputEvent input_event,
     const FrameSinkId& root_frame_sink_id) {
-  TRACE_EVENT("input", "AndroidStateTransferHandler::OnMotionEvent");
+  TRACE_EVENT("input", "AndroidStateTransferHandler::OnMotionEvent",
+              [&](perfetto::EventContext& ctx) {
+                auto* chrome_track_event =
+                    ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
+                auto* forwarder = chrome_track_event->set_event_forwarder();
+
+                input_event.WriteIntoTrace(ctx.Wrap(forwarder));
+              });
 
   const int action = AMotionEvent_getAction(input_event.a_input_event()) &
                      AMOTION_EVENT_ACTION_MASK;
@@ -200,14 +207,17 @@ void AndroidStateTransferHandler::EmitPendingTransfersHistogram() {
 
 void AndroidStateTransferHandler::HandleTouchEvent(
     base::android::ScopedInputEvent input_event) {
+  // TODO(crbug.com/406986388) : Add flow events to track the events starting
+  // from when they were first were processed by Viz.
+  TRACE_EVENT("input", "AndroidStateTransferHandler::HandleTouchEvent");
   CHECK(state_for_curr_sequence_.has_value());
   const int action = AMotionEvent_getAction(input_event.a_input_event()) &
                      AMOTION_EVENT_ACTION_MASK;
-  // Due to an Android platform bug b/395610162, we see some motion events have
-  // different down time than the rest of the sequence.
-  CHECK(action == AMOTION_EVENT_ACTION_MOVE ||
-        GetEventDowntime(input_event) ==
-            state_for_curr_sequence_->transfer_state->down_time_ms);
+
+  if (GetEventDowntime(input_event) !=
+      state_for_curr_sequence_->transfer_state->down_time_ms) {
+    TRACE_EVENT_INSTANT("input", "DifferentDownTimeInSequence");
+  }
 
   if (!state_for_curr_sequence_->rir_support) {
     if (action == AMOTION_EVENT_ACTION_CANCEL ||