Merge changes Iedf67063,Ia5f9bcac,I0d319c65

* changes:
  TouchButtonAccumulator: Recognize mapped stylus buttons
  Extract HID usage accumulation logic from KeyboardInputMapper
  TouchButtonAccumulator: Miscelaneous cleanup
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index c5e1f0c..a53fcd7 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -54,6 +54,7 @@
         "mapper/VibratorInputMapper.cpp",
         "mapper/accumulator/CursorButtonAccumulator.cpp",
         "mapper/accumulator/CursorScrollAccumulator.cpp",
+        "mapper/accumulator/HidUsageAccumulator.cpp",
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
         "mapper/accumulator/TouchButtonAccumulator.cpp",
     ],
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index b9a2b4c..439123b 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -378,8 +378,11 @@
         mEventHub->getAbsoluteAxisInfo(mId, code, &info);
         return info.valid;
     }
-    inline bool isKeyPressed(int32_t code) const {
-        return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+    inline bool isKeyPressed(int32_t scanCode) const {
+        return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
+    }
+    inline bool isKeyCodePressed(int32_t keyCode) const {
+        return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN;
     }
     inline int32_t getAbsoluteAxisValue(int32_t code) const {
         int32_t value;
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 0404c9a..56fc5fa 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -24,7 +24,7 @@
 namespace android {
 
 ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
+      : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {}
 
 uint32_t ExternalStylusInputMapper::getSources() const {
     return AINPUT_SOURCE_STYLUS;
@@ -48,13 +48,13 @@
                                                            const InputReaderConfiguration* config,
                                                            uint32_t changes) {
     getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
-    mTouchButtonAccumulator.configure(getDeviceContext());
+    mTouchButtonAccumulator.configure();
     return {};
 }
 
 std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) {
     mSingleTouchMotionAccumulator.reset(getDeviceContext());
-    mTouchButtonAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset();
     return InputMapper::reset(when);
 }
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 33b8a1b..da9413e 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -180,7 +180,7 @@
 
 std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) {
     std::list<NotifyArgs> out = cancelAllDownKeys(when);
-    mCurrentHidUsage = 0;
+    mHidUsageAccumulator.reset();
 
     resetLedState();
 
@@ -190,29 +190,17 @@
 
 std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
     std::list<NotifyArgs> out;
+    mHidUsageAccumulator.process(*rawEvent);
     switch (rawEvent->type) {
         case EV_KEY: {
             int32_t scanCode = rawEvent->code;
-            int32_t usageCode = mCurrentHidUsage;
-            mCurrentHidUsage = 0;
 
             if (isSupportedScanCode(scanCode)) {
                 out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
-                                  scanCode, usageCode);
+                                  scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
             }
             break;
         }
-        case EV_MSC: {
-            if (rawEvent->code == MSC_SCAN) {
-                mCurrentHidUsage = rawEvent->value;
-            }
-            break;
-        }
-        case EV_SYN: {
-            if (rawEvent->code == SYN_REPORT) {
-                mCurrentHidUsage = 0;
-            }
-        }
     }
     return out;
 }
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 3dd570d..11d5ad2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "HidUsageAccumulator.h"
 #include "InputMapper.h"
 
 namespace android {
@@ -61,7 +62,7 @@
     std::vector<KeyDown> mKeyDowns{}; // keys that are down
     int32_t mMetaState{};
 
-    int32_t mCurrentHidUsage{}; // most recent HID usage seen this packet, or 0 if none
+    HidUsageAccumulator mHidUsageAccumulator;
 
     struct LedState {
         bool avail{}; // led is available
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 615889e..d17cdf5 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -170,6 +170,7 @@
 
 TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
       : InputMapper(deviceContext),
+        mTouchButtonAccumulator(deviceContext),
         mSource(0),
         mDeviceMode(DeviceMode::DISABLED),
         mDisplayWidth(-1),
@@ -360,7 +361,7 @@
 
         // Configure common accumulators.
         mCursorScrollAccumulator.configure(getDeviceContext());
-        mTouchButtonAccumulator.configure(getDeviceContext());
+        mTouchButtonAccumulator.configure();
 
         // Configure absolute axis information.
         configureRawPointerAxes();
@@ -1449,7 +1450,7 @@
 
     mCursorButtonAccumulator.reset(getDeviceContext());
     mCursorScrollAccumulator.reset(getDeviceContext());
-    mTouchButtonAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset();
 
     mPointerVelocityControl.reset();
     mWheelXVelocityControl.reset();
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
new file mode 100644
index 0000000..2da1d81
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 "HidUsageAccumulator.h"
+
+namespace android {
+
+void HidUsageAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_MSC && rawEvent.code == MSC_SCAN) {
+        mCurrentHidUsage = rawEvent.value;
+        return;
+    }
+
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+        reset();
+        return;
+    }
+}
+
+int32_t HidUsageAccumulator::consumeCurrentHidUsage() {
+    const int32_t currentHidUsage = mCurrentHidUsage;
+    reset();
+    return currentHidUsage;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
new file mode 100644
index 0000000..740a710
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include "EventHub.h"
+
+#include <cstdint>
+
+namespace android {
+
+/* Keeps track of the state of currently reported HID usage code. */
+class HidUsageAccumulator {
+public:
+    explicit HidUsageAccumulator() = default;
+    inline void reset() { *this = HidUsageAccumulator(); }
+
+    void process(const RawEvent& rawEvent);
+
+    /* This must be called when processing the `EV_KEY` event. Returns 0 if invalid. */
+    int32_t consumeCurrentHidUsage();
+
+private:
+    int32_t mCurrentHidUsage{};
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 86153d3..5d5bee7 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -21,55 +21,39 @@
 
 namespace android {
 
-TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) {
-    clearButtons();
+void TouchButtonAccumulator::configure() {
+    mHaveBtnTouch = mDeviceContext.hasScanCode(BTN_TOUCH);
+    mHaveStylus = mDeviceContext.hasScanCode(BTN_TOOL_PEN) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
 }
 
-void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) {
-    mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH);
-    mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) ||
-            deviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
-            deviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
-            deviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
-            deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) {
-    mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH);
-    mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS);
+void TouchButtonAccumulator::reset() {
+    mBtnTouch = mDeviceContext.isKeyPressed(BTN_TOUCH);
+    mBtnStylus = mDeviceContext.isKeyPressed(BTN_STYLUS) ||
+            mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_PRIMARY);
     // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
-    mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0);
-    mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER);
-    mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN);
-    mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER);
-    mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH);
-    mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL);
-    mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
-    mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE);
-    mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS);
-    mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
-    mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
-    mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
-    mBtnTouch = 0;
-    mBtnStylus = 0;
-    mBtnStylus2 = 0;
-    mBtnToolFinger = 0;
-    mBtnToolPen = 0;
-    mBtnToolRubber = 0;
-    mBtnToolBrush = 0;
-    mBtnToolPencil = 0;
-    mBtnToolAirbrush = 0;
-    mBtnToolMouse = 0;
-    mBtnToolLens = 0;
-    mBtnToolDoubleTap = 0;
-    mBtnToolTripleTap = 0;
-    mBtnToolQuadTap = 0;
+    mBtnStylus2 = mDeviceContext.isKeyPressed(BTN_STYLUS2) || mDeviceContext.isKeyPressed(BTN_0) ||
+            mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_SECONDARY);
+    mBtnToolFinger = mDeviceContext.isKeyPressed(BTN_TOOL_FINGER);
+    mBtnToolPen = mDeviceContext.isKeyPressed(BTN_TOOL_PEN);
+    mBtnToolRubber = mDeviceContext.isKeyPressed(BTN_TOOL_RUBBER);
+    mBtnToolBrush = mDeviceContext.isKeyPressed(BTN_TOOL_BRUSH);
+    mBtnToolPencil = mDeviceContext.isKeyPressed(BTN_TOOL_PENCIL);
+    mBtnToolAirbrush = mDeviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
+    mBtnToolMouse = mDeviceContext.isKeyPressed(BTN_TOOL_MOUSE);
+    mBtnToolLens = mDeviceContext.isKeyPressed(BTN_TOOL_LENS);
+    mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
+    mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
+    mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+    mHidUsageAccumulator.reset();
 }
 
 void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
+    mHidUsageAccumulator.process(*rawEvent);
+
     if (rawEvent->type == EV_KEY) {
         switch (rawEvent->code) {
             case BTN_TOUCH:
@@ -116,7 +100,29 @@
             case BTN_TOOL_QUADTAP:
                 mBtnToolQuadTap = rawEvent->value;
                 break;
+            default:
+                processMappedKey(rawEvent->code, rawEvent->value);
         }
+        return;
+    }
+}
+
+void TouchButtonAccumulator::processMappedKey(int32_t scanCode, bool down) {
+    int32_t outKeyCode, outMetaState;
+    uint32_t outFlags;
+    if (mDeviceContext.mapKey(scanCode, mHidUsageAccumulator.consumeCurrentHidUsage(),
+                              0 /*metaState*/, &outKeyCode, &outMetaState, &outFlags) != OK) {
+        return;
+    }
+    switch (outKeyCode) {
+        case AKEYCODE_STYLUS_BUTTON_PRIMARY:
+            mBtnStylus = down;
+            break;
+        case AKEYCODE_STYLUS_BUTTON_SECONDARY:
+            mBtnStylus2 = down;
+            break;
+        default:
+            break;
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 7b889be..65b0a62 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -16,7 +16,8 @@
 
 #pragma once
 
-#include <stdint.h>
+#include <cstdint>
+#include "HidUsageAccumulator.h"
 
 namespace android {
 
@@ -26,9 +27,11 @@
 /* Keeps track of the state of touch, stylus and tool buttons. */
 class TouchButtonAccumulator {
 public:
-    TouchButtonAccumulator();
-    void configure(InputDeviceContext& deviceContext);
-    void reset(InputDeviceContext& deviceContext);
+    explicit TouchButtonAccumulator(InputDeviceContext& deviceContext)
+          : mDeviceContext(deviceContext){};
+
+    void configure();
+    void reset();
 
     void process(const RawEvent* rawEvent);
 
@@ -39,25 +42,29 @@
     bool hasStylus() const;
 
 private:
-    bool mHaveBtnTouch;
-    bool mHaveStylus;
+    bool mHaveBtnTouch{};
+    bool mHaveStylus{};
 
-    bool mBtnTouch;
-    bool mBtnStylus;
-    bool mBtnStylus2;
-    bool mBtnToolFinger;
-    bool mBtnToolPen;
-    bool mBtnToolRubber;
-    bool mBtnToolBrush;
-    bool mBtnToolPencil;
-    bool mBtnToolAirbrush;
-    bool mBtnToolMouse;
-    bool mBtnToolLens;
-    bool mBtnToolDoubleTap;
-    bool mBtnToolTripleTap;
-    bool mBtnToolQuadTap;
+    bool mBtnTouch{};
+    bool mBtnStylus{};
+    bool mBtnStylus2{};
+    bool mBtnToolFinger{};
+    bool mBtnToolPen{};
+    bool mBtnToolRubber{};
+    bool mBtnToolBrush{};
+    bool mBtnToolPencil{};
+    bool mBtnToolAirbrush{};
+    bool mBtnToolMouse{};
+    bool mBtnToolLens{};
+    bool mBtnToolDoubleTap{};
+    bool mBtnToolTripleTap{};
+    bool mBtnToolQuadTap{};
 
-    void clearButtons();
+    HidUsageAccumulator mHidUsageAccumulator{};
+
+    InputDeviceContext& mDeviceContext;
+
+    void processMappedKey(int32_t scanCode, bool down);
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index e15d09f..2142070 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -7338,6 +7338,7 @@
     void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
     void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
     void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value);
     void processMTSync(MultiTouchInputMapper& mapper);
     void processSync(MultiTouchInputMapper& mapper);
 };
@@ -7442,6 +7443,12 @@
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
 }
 
+void MultiTouchInputMapperTest::processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode,
+                                                int32_t value) {
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, usageCode);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, value);
+}
+
 void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
@@ -8557,6 +8564,63 @@
     ASSERT_EQ(0, motionArgs.buttonState);
 }
 
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0);
+
+    // Touch down.
+    processId(mapper, 1);
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0))));
+
+    // Press and release button mapped to the primary stylus button.
+    processKey(mapper, BTN_A, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+    processKey(mapper, BTN_A, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+    // Press and release the HID usage mapped to the secondary stylus button.
+    processHidUsage(mapper, 0xabcd, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+
+    processHidUsage(mapper, 0xabcd, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+    // Release touch.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+}
+
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 97ba90b..5107af7 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -86,4 +86,9 @@
     return arg.flags == flags;
 }
 
+MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
+    *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
+    return arg.buttonState == buttons;
+}
+
 } // namespace android