Merge "SurfaceFlinger: Correct logic error in early release" into tm-dev
diff --git a/include/input/Input.h b/include/input/Input.h
index b23a951..e7d68fc 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -837,6 +837,8 @@
     std::vector<PointerCoords> mSamplePointerCoords;
 };
 
+std::ostream& operator<<(std::ostream& out, const MotionEvent& event);
+
 /*
  * Focus events.
  */
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 41b3460..76e3e66 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -39,9 +39,16 @@
 
 cc_library_headers {
     name: "libarect_headers",
+    vendor_available: true,
+    min_sdk_version: "29",
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
     export_include_dirs: ["include"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
 }
 
 cc_library_static {
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) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4d25c59..5e9427a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2507,7 +2507,7 @@
 }
 
 void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
-    if (!mDragState) {
+    if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId) {
         return;
     }
 
@@ -4765,9 +4765,11 @@
 
         // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
         // could just clear the state here.
-        if (mDragState &&
+        if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId &&
             std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
                     windowHandles.end()) {
+            ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str());
+            sendDropWindowCommandLocked(nullptr, 0, 0);
             mDragState.reset();
         }
     }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 91a666c..df43071 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6407,6 +6407,58 @@
     mSecondWindow->consumeMotionMove();
 }
 
+TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
+    performDrag();
+
+    // Update window of second display.
+    sp<FakeWindowHandle> windowInSecondary =
+            new FakeWindowHandle(mApp, mDispatcher, "D_2", SECOND_DISPLAY_ID);
+    mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+
+    // Let second display has a touch state.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher,
+                                MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+                                                   AINPUT_SOURCE_TOUCHSCREEN)
+                                        .displayId(SECOND_DISPLAY_ID)
+                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                                         .x(100)
+                                                         .y(100))
+                                        .build()));
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
+                                    SECOND_DISPLAY_ID, 0 /* expectedFlag */);
+    // Update window again.
+    mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+
+    // Move on window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->consumeDragEvent(false, 50, 50);
+
+    // drop to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
 
 TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 9c6e56d..3c8dc64 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -118,16 +118,17 @@
         auto triggerTime = mClock->now() + mInterval;
         state = TimerState::WAITING;
         while (true) {
-            mWaiting = true;
-            constexpr auto zero = std::chrono::steady_clock::duration::zero();
-            // Wait for mInterval time to check if we need to reset or drop into the idle state.
-            struct timespec ts;
-            calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
-            int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
-            if (result && errno != ETIMEDOUT && errno != EINTR) {
-                std::stringstream ss;
-                ss << "sem_clockwait failed (" << errno << ")";
-                LOG_ALWAYS_FATAL("%s", ss.str().c_str());
+            // Wait until triggerTime time to check if we need to reset or drop into the idle state.
+            if (const auto triggerInterval = triggerTime - mClock->now(); triggerInterval > 0ns) {
+                mWaiting = true;
+                struct timespec ts;
+                calculateTimeoutTime(triggerInterval, &ts);
+                int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
+                if (result && errno != ETIMEDOUT && errno != EINTR) {
+                    std::stringstream ss;
+                    ss << "sem_clockwait failed (" << errno << ")";
+                    LOG_ALWAYS_FATAL("%s", ss.str().c_str());
+                }
             }
 
             mWaiting = false;
@@ -136,7 +137,7 @@
                 break;
             }
 
-            if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= zero) {
+            if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= 0ns) {
                 triggerTimeout = true;
                 state = TimerState::IDLE;
                 break;
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 597e5e7..aafc323 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -130,6 +130,40 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
+// TODO(b/186417847) This test is new and passes locally, but may be flaky
+TEST_F(OneShotTimerTest, DISABLED_resetBackToBackSlowAdvanceTest) {
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
+                                                           mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::FakeClock>(clock));
+    mIdleTimer->start();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+    mIdleTimer->reset();
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+
+    clock->advanceTime(200us);
+    mIdleTimer->reset();
+
+    // Normally we would check that the timer callbacks weren't invoked here
+    // after resetting the timer, but we need to precisely control the timing of
+    // this test, and checking that callbacks weren't invoked requires non-zero
+    // time.
+
+    clock->advanceTime(1500us);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1100us).has_value());
+    mIdleTimer->reset();
+    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+
+    mIdleTimer->stop();
+    clock->advanceTime(2ms);
+    // Final quick check that no more callback were observed.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+}
+
 TEST_F(OneShotTimerTest, startNotCalledTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,