Validate indices when requesting PointerCoords

We are investigating a crash in PointerCoords. To further understand
what kind of MotionEvents we are dealing with, let's add some assertions
to the PointerCoords that we are producing.

It's possible that pointerCount is zero, for example. This would help us
further narrow down the issue.

In this CL, we are also adding a way to dump MotionEvent. This was
loosely following MotionEvent.java::toString() method. At some point, we
should remove the Java method implementation and replace it with a
single jni call to native. That work is out of scope for this CL.

In the jni layer, we can't remove the error checking/exception raising,
because someone might be catching an exception and moving on, and a
native crash would break this pattern. We can consider doing that in the
future under an experiment flag, though.

Bug: 233163975
Test: printed MotionEvent in log to see the formatting
Change-Id: I4f641c0cb89526a06146e4c0cf3a5fab2faa42f8
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index fe1754c..13ca9ec 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -22,7 +22,9 @@
 #include <inttypes.h>
 #include <string.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
 #include <gui/constants.h>
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
@@ -542,7 +544,14 @@
 }
 
 const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
-    return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
+    if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) {
+        LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this;
+    }
+    const size_t position = getHistorySize() * getPointerCount() + pointerIndex;
+    if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) {
+        LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this;
+    }
+    return &mSamplePointerCoords[position];
 }
 
 float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
@@ -555,7 +564,18 @@
 
 const PointerCoords* MotionEvent::getHistoricalRawPointerCoords(
         size_t pointerIndex, size_t historicalIndex) const {
-    return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
+    if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) {
+        LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this;
+    }
+    if (CC_UNLIKELY(historicalIndex < 0 || historicalIndex > getHistorySize())) {
+        LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex << " for "
+                   << *this;
+    }
+    const size_t position = historicalIndex * getPointerCount() + pointerIndex;
+    if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) {
+        LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this;
+    }
+    return &mSamplePointerCoords[position];
 }
 
 float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
@@ -903,6 +923,53 @@
     return out;
 }
 
+std::ostream& operator<<(std::ostream& out, const MotionEvent& event) {
+    out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction());
+    if (event.getActionButton() != 0) {
+        out << ", actionButton=" << std::to_string(event.getActionButton());
+    }
+    const size_t pointerCount = event.getPointerCount();
+    for (size_t i = 0; i < pointerCount; i++) {
+        out << ", id[" << i << "]=" << event.getPointerId(i);
+        float x = event.getX(i);
+        float y = event.getY(i);
+        if (x != 0 || y != 0) {
+            out << ", x[" << i << "]=" << x;
+            out << ", y[" << i << "]=" << y;
+        }
+        int toolType = event.getToolType(i);
+        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+            out << ", toolType[" << i << "]=" << toolType;
+        }
+    }
+    if (event.getButtonState() != 0) {
+        out << ", buttonState=" << event.getButtonState();
+    }
+    if (event.getClassification() != MotionClassification::NONE) {
+        out << ", classification=" << motionClassificationToString(event.getClassification());
+    }
+    if (event.getMetaState() != 0) {
+        out << ", metaState=" << event.getMetaState();
+    }
+    if (event.getEdgeFlags() != 0) {
+        out << ", edgeFlags=" << event.getEdgeFlags();
+    }
+    if (pointerCount != 1) {
+        out << ", pointerCount=" << pointerCount;
+    }
+    if (event.getHistorySize() != 0) {
+        out << ", historySize=" << event.getHistorySize();
+    }
+    out << ", eventTime=" << event.getEventTime();
+    out << ", downTime=" << event.getDownTime();
+    out << ", deviceId=" << event.getDeviceId();
+    out << ", source=" << inputEventSourceToString(event.getSource());
+    out << ", displayId=" << event.getDisplayId();
+    out << ", eventId=" << event.getId();
+    out << "}";
+    return out;
+}
+
 // --- FocusEvent ---
 
 void FocusEvent::initialize(int32_t id, bool hasFocus) {