CTS test for Android Security b/179839665
Bug: 179839665
Bug: 193153606
Test: Ran the new testcase on android-11.0.0_r34 to test with/without patch
Change-Id: I10fda7b53b027091dbfe2338b24e90862ba88b76
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/Android.bp
new file mode 100644
index 0000000..2c9502b
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2021-0684",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ header_libs: [
+ "libbatteryservice_headers",
+ ],
+ srcs: [
+ "poc.cpp",
+ "TestInputListener.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DCHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE=4096",
+ "-Wno-unused-parameter",
+ ],
+ static_libs: [
+ "libinputdispatcher",
+ ],
+ shared_libs: [
+ "libinputflinger_base",
+ "libinputreader",
+ "libinputflinger",
+ "libinputreader",
+ "libbase",
+ "libinput",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.cpp
new file mode 100644
index 0000000..875a38a
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/* 'frameworks/native/services/inputflinger/tests/TestInputListener.cpp'
+ * is used as reference to come up with file
+ * Only code pertaining to gtest 'Process_DeactivateViewport_AbortTouches' is
+ * retained
+ */
+
+#include "TestInputListener.h"
+
+namespace android {
+
+// --- TestInputListener ---
+
+TestInputListener::TestInputListener(std::chrono::milliseconds eventHappenedTimeout,
+ std::chrono::milliseconds eventDidNotHappenTimeout)
+ : mEventHappenedTimeout(eventHappenedTimeout),
+ mEventDidNotHappenTimeout(eventDidNotHappenTimeout) {}
+
+TestInputListener::~TestInputListener() {}
+
+template <class NotifyArgsType>
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+ if (queue.empty()) {
+ const bool eventReceived =
+ mCondition.wait_for(lock, mEventHappenedTimeout,
+ [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ if (!eventReceived) {
+ return;
+ }
+ }
+ if (outEventArgs) {
+ *outEventArgs = *queue.begin();
+ }
+ queue.erase(queue.begin());
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertNotCalled(std::string message) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+ const bool eventReceived =
+ mCondition.wait_for(lock, mEventDidNotHappenTimeout,
+ [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ if (eventReceived) {
+ return;
+ }
+}
+
+template <class NotifyArgsType>
+void TestInputListener::notify(const NotifyArgsType* args) {
+ std::scoped_lock<std::mutex> lock(mLock);
+
+ std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+ queue.push_back(*args);
+ mCondition.notify_all();
+}
+
+void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+ notify<NotifyConfigurationChangedArgs>(args);
+}
+
+void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+ notify<NotifyDeviceResetArgs>(args);
+}
+
+void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
+ notify<NotifyKeyArgs>(args);
+}
+
+void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
+ notify<NotifyMotionArgs>(args);
+}
+
+void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
+ notify<NotifySwitchArgs>(args);
+}
+
+void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
+ notify<NotifyPointerCaptureChangedArgs>(args);
+}
+
+void TestInputListener::notifySensor(const NotifySensorArgs* args) {
+ notify<NotifySensorArgs>(args);
+}
+
+void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
+ notify<NotifyVibratorStateArgs>(args);
+}
+
+} // namespace android
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.h b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.h
new file mode 100644
index 0000000..067ac83
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/* 'frameworks/native/services/inputflinger/tests/TestInputListener.cpp'
+ * is used as reference to come up with file
+ * Only code pertaining to gtest 'Process_DeactivateViewport_AbortTouches' is
+ * retained
+ */
+
+#ifndef _UI_TEST_INPUT_LISTENER_H
+#define _UI_TEST_INPUT_LISTENER_H
+
+#include <android-base/thread_annotations.h>
+#include "InputListener.h"
+
+using std::chrono_literals::operator""ms;
+
+namespace android {
+
+// --- TestInputListener ---
+
+class TestInputListener : public InputListenerInterface {
+protected:
+ virtual ~TestInputListener();
+
+public:
+ TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms,
+ std::chrono::milliseconds eventDidNotHappenTimeout = 0ms);
+
+ template <class NotifyArgsType>
+ void assertCalled(NotifyArgsType* outEventArgs, std::string message);
+
+ template <class NotifyArgsType>
+ void assertNotCalled(std::string message);
+
+ template <class NotifyArgsType>
+ void notify(const NotifyArgsType* args);
+
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+ virtual void notifyKey(const NotifyKeyArgs* args) override;
+
+ virtual void notifyMotion(const NotifyMotionArgs* args) override;
+
+ virtual void notifySwitch(const NotifySwitchArgs* args) override;
+
+ virtual void notifySensor(const NotifySensorArgs* args) override;
+
+ virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+
+ virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ const std::chrono::milliseconds mEventHappenedTimeout;
+ const std::chrono::milliseconds mEventDidNotHappenTimeout;
+
+ std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
+ std::vector<NotifyDeviceResetArgs>, //
+ std::vector<NotifyKeyArgs>, //
+ std::vector<NotifyMotionArgs>, //
+ std::vector<NotifySwitchArgs>, //
+ std::vector<NotifySensorArgs>, //
+ std::vector<NotifyVibratorStateArgs>, //
+ std::vector<NotifyPointerCaptureChangedArgs>> //
+ mQueues GUARDED_BY(mLock);
+};
+
+} // namespace android
+#endif
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/poc.cpp
new file mode 100644
index 0000000..13b33b6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/poc.cpp
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+/* 'frameworks/native/services/inputflinger/tests/TestInputListener.cpp'
+ * is used as reference to come up with file
+ * Only code pertaining to gtest 'Process_DeactivateViewport_AbortTouches' is
+ * retained
+ */
+
+#include <InputMapper.h>
+#include <InputReader.h>
+#include <InputReaderBase.h>
+#include <InputReaderFactory.h>
+#include <MultiTouchInputMapper.h>
+#include <TestInputListener.h>
+
+namespace android {
+
+using std::chrono_literals::operator""ms;
+using namespace android::flag_operators;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
+// An arbitrary time value.
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
+
+// Arbitrary display properties.
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt;
+static constexpr int32_t BATTERY_STATUS = 4;
+static constexpr int32_t BATTERY_CAPACITY = 66;
+static constexpr int32_t RAW_X_MIN = 25;
+static constexpr int32_t RAW_X_MAX = 1019;
+static constexpr int32_t RAW_Y_MIN = 30;
+static constexpr int32_t RAW_Y_MAX = 1009;
+constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+constexpr int32_t DEVICE_GENERATION = 2;
+
+const char* DEVICE_NAME = "device";
+const char* DEVICE_LOCATION = "USB1";
+const Flags<InputDeviceClass> DEVICE_CLASSES = Flags<InputDeviceClass>(0);
+constexpr int32_t EVENTHUB_ID = 1;
+const std::string UNIQUE_ID = "local:0";
+
+template <typename T>
+static inline T min(T a, T b) {
+ return a < b ? a : b;
+}
+
+// --- TestPointerController ---
+
+class TestPointerController : public PointerControllerInterface {
+ bool mHaveBounds;
+ float mMinX, mMinY, mMaxX, mMaxY;
+ float mX, mY;
+ int32_t mButtonState;
+ int32_t mDisplayId;
+
+public:
+ TestPointerController()
+ : mHaveBounds(false),
+ mMinX(0),
+ mMinY(0),
+ mMaxX(0),
+ mMaxY(0),
+ mX(0),
+ mY(0),
+ mButtonState(0),
+ mDisplayId(ADISPLAY_ID_DEFAULT) {}
+
+ virtual ~TestPointerController() {}
+
+ void setBounds(float minX, float minY, float maxX, float maxY) {
+ mHaveBounds = true;
+ mMinX = minX;
+ mMinY = minY;
+ mMaxX = maxX;
+ mMaxY = maxY;
+ }
+
+ void setPosition(float x, float y) override {
+ mX = x;
+ mY = y;
+ }
+
+ void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
+
+ int32_t getButtonState() const override { return mButtonState; }
+
+ void getPosition(float* outX, float* outY) const override {
+ *outX = mX;
+ *outY = mY;
+ }
+
+ int32_t getDisplayId() const override { return mDisplayId; }
+
+ void setDisplayViewport(const DisplayViewport& viewport) override {
+ mDisplayId = viewport.displayId;
+ }
+
+ const std::map<int32_t, std::vector<int32_t>>& getSpots() { return mSpotsByDisplay; }
+
+private:
+ bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
+ *outMinX = mMinX;
+ *outMinY = mMinY;
+ *outMaxX = mMaxX;
+ *outMaxY = mMaxY;
+ return mHaveBounds;
+ }
+
+ void move(float deltaX, float deltaY) override {
+ mX += deltaX;
+ if (mX < mMinX) mX = mMinX;
+ if (mX > mMaxX) mX = mMaxX;
+ mY += deltaY;
+ if (mY < mMinY) mY = mMinY;
+ if (mY > mMaxY) mY = mMaxY;
+ }
+
+ void fade(Transition) override {}
+
+ void unfade(Transition) override {}
+
+ void setPresentation(Presentation) override {}
+
+ void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+ int32_t displayId) override {
+ std::vector<int32_t> newSpots;
+ // Add spots for fingers that are down.
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ newSpots.push_back(id);
+ }
+
+ mSpotsByDisplay[displayId] = newSpots;
+ }
+
+ void clearSpots() override {}
+
+ std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
+};
+
+// --- TestInputReaderPolicy---
+
+class TestInputReaderPolicy : public InputReaderPolicyInterface {
+ std::mutex mLock;
+ std::condition_variable mDevicesChangedCondition;
+
+ InputReaderConfiguration mConfig;
+ std::unordered_map<int32_t, std::shared_ptr<TestPointerController>> mPointerControllers;
+ std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+ bool mInputDevicesChanged GUARDED_BY(mLock){false};
+ std::vector<DisplayViewport> mViewports;
+ TouchAffineTransformation transform;
+
+protected:
+ virtual ~TestInputReaderPolicy() {}
+
+public:
+ TestInputReaderPolicy() {}
+
+ virtual void clearViewports() {
+ mViewports.clear();
+ mConfig.setDisplayViewports(mViewports);
+ }
+
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
+ return mConfig.getDisplayViewportByUniqueId(uniqueId);
+ }
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
+ return mConfig.getDisplayViewportByType(type);
+ }
+
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
+ return mConfig.getDisplayViewportByPort(displayPort);
+ }
+
+ void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+ bool isActive, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType viewportType) {
+ const DisplayViewport viewport =
+ createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId,
+ physicalPort, viewportType);
+ mViewports.push_back(viewport);
+ mConfig.setDisplayViewports(mViewports);
+ }
+
+ bool updateViewport(const DisplayViewport& viewport) {
+ size_t count = mViewports.size();
+ for (size_t i = 0; i < count; i++) {
+ const DisplayViewport& currentViewport = mViewports[i];
+ if (currentViewport.displayId == viewport.displayId) {
+ mViewports[i] = viewport;
+ mConfig.setDisplayViewports(mViewports);
+ return true;
+ }
+ }
+ // no viewport found.
+ return false;
+ }
+
+ void addExcludedDeviceName(const std::string& deviceName) {
+ mConfig.excludedDeviceNames.push_back(deviceName);
+ }
+
+ void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
+ mConfig.portAssociations.insert({inputPort, displayPort});
+ }
+
+ void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId) {
+ mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+ }
+
+ void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
+
+ void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
+
+ void setPointerController(int32_t deviceId, std::shared_ptr<TestPointerController> controller) {
+ mPointerControllers.insert_or_assign(deviceId, std::move(controller));
+ }
+
+ const InputReaderConfiguration* getReaderConfiguration() const { return &mConfig; }
+
+ const std::vector<InputDeviceInfo>& getInputDevices() const { return mInputDevices; }
+
+ TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+ int32_t surfaceRotation) {
+ return transform;
+ }
+
+ void setTouchAffineTransformation(const TouchAffineTransformation t) { transform = t; }
+
+ void setPointerCapture(bool enabled) { mConfig.pointerCapture = enabled; }
+
+ void setShowTouches(bool enabled) { mConfig.showTouches = enabled; }
+
+ void setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+ mConfig.defaultPointerDisplayId = pointerDisplayId;
+ }
+
+ float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
+
+private:
+ DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ int32_t orientation, bool isActive,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType type) {
+ bool isRotated =
+ (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+ return v;
+ }
+
+ void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
+ *outConfig = mConfig;
+ }
+
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
+ return mPointerControllers[deviceId];
+ }
+
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mInputDevices = inputDevices;
+ mInputDevicesChanged = true;
+ mDevicesChangedCondition.notify_all();
+ }
+
+ std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) override {
+ return nullptr;
+ }
+
+ std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
+
+ void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mInputDevicesChanged;
+ });
+ mInputDevicesChanged = false;
+ }
+};
+
+// --- TestEventHub ---
+
+class TestEventHub : public EventHubInterface {
+ struct KeyInfo {
+ int32_t keyCode;
+ uint32_t flags;
+ };
+
+ struct SensorInfo {
+ InputDeviceSensorType sensorType;
+ int32_t sensorDataIndex;
+ };
+
+ struct Device {
+ InputDeviceIdentifier identifier;
+ Flags<InputDeviceClass> classes;
+ PropertyMap configuration;
+ KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
+ KeyedVector<int, bool> relativeAxes;
+ KeyedVector<int32_t, int32_t> keyCodeStates;
+ KeyedVector<int32_t, int32_t> scanCodeStates;
+ KeyedVector<int32_t, int32_t> switchStates;
+ KeyedVector<int32_t, int32_t> absoluteAxisValue;
+ KeyedVector<int32_t, KeyInfo> keysByScanCode;
+ KeyedVector<int32_t, KeyInfo> keysByUsageCode;
+ KeyedVector<int32_t, bool> leds;
+ std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
+ BitArray<MSC_MAX> mscBitmask;
+ std::vector<VirtualKeyDefinition> virtualKeys;
+ bool enabled;
+
+ status_t enable() {
+ enabled = true;
+ return OK;
+ }
+
+ status_t disable() {
+ enabled = false;
+ return OK;
+ }
+
+ explicit Device(Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
+ };
+
+ std::mutex mLock;
+ std::condition_variable mEventsCondition;
+
+ KeyedVector<int32_t, Device*> mDevices;
+ std::vector<std::string> mExcludedDevices;
+ std::vector<RawEvent> mEvents GUARDED_BY(mLock);
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
+ std::vector<int32_t> mVibrators = {0, 1};
+ std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
+ // Simulates a device light brightness, from light id to light brightness.
+ std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
+ // Simulates a device light intensities, from light id to light intensities map.
+ std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
+ mLightIntensities;
+
+public:
+ virtual ~TestEventHub() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+ }
+
+ TestEventHub() {}
+
+ void addDevice(int32_t deviceId, const std::string& name, Flags<InputDeviceClass> classes) {
+ Device* device = new Device(classes);
+ device->identifier.name = name;
+ mDevices.add(deviceId, device);
+
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+ }
+
+ void removeDevice(int32_t deviceId) {
+ delete mDevices.valueFor(deviceId);
+ mDevices.removeItem(deviceId);
+
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+ }
+
+ bool isDeviceEnabled(int32_t deviceId) {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return false;
+ }
+ return device->enabled;
+ }
+
+ status_t enableDevice(int32_t deviceId) {
+ status_t result;
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
+ return OK;
+ }
+ result = device->enable();
+ return result;
+ }
+
+ status_t disableDevice(int32_t deviceId) {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (!device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
+ return OK;
+ }
+ return device->disable();
+ }
+
+ void finishDeviceScan() {
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+ }
+
+ void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
+ Device* device = getDevice(deviceId);
+ device->configuration.addProperty(key, value);
+ }
+
+ void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
+ Device* device = getDevice(deviceId);
+ device->configuration.addAll(configuration);
+ }
+
+ void addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat,
+ int fuzz, int resolution = 0) {
+ Device* device = getDevice(deviceId);
+
+ RawAbsoluteAxisInfo info;
+ info.valid = true;
+ info.minValue = minValue;
+ info.maxValue = maxValue;
+ info.flat = flat;
+ info.fuzz = fuzz;
+ info.resolution = resolution;
+ device->absoluteAxes.add(axis, info);
+ }
+
+ void addRelativeAxis(int32_t deviceId, int32_t axis) {
+ Device* device = getDevice(deviceId);
+ device->relativeAxes.add(axis, true);
+ }
+
+ void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
+ Device* device = getDevice(deviceId);
+ device->keyCodeStates.replaceValueFor(keyCode, state);
+ }
+
+ void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
+ Device* device = getDevice(deviceId);
+ device->scanCodeStates.replaceValueFor(scanCode, state);
+ }
+
+ void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
+ Device* device = getDevice(deviceId);
+ device->switchStates.replaceValueFor(switchCode, state);
+ }
+
+ void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
+ Device* device = getDevice(deviceId);
+ device->absoluteAxisValue.replaceValueFor(axis, value);
+ }
+
+ void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+ uint32_t flags) {
+ Device* device = getDevice(deviceId);
+ KeyInfo info;
+ info.keyCode = keyCode;
+ info.flags = flags;
+ if (scanCode) {
+ device->keysByScanCode.add(scanCode, info);
+ }
+ if (usageCode) {
+ device->keysByUsageCode.add(usageCode, info);
+ }
+ }
+
+ void addLed(int32_t deviceId, int32_t led, bool initialState) {
+ Device* device = getDevice(deviceId);
+ device->leds.add(led, initialState);
+ }
+
+ void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
+ int32_t sensorDataIndex) {
+ Device* device = getDevice(deviceId);
+ SensorInfo info;
+ info.sensorType = sensorType;
+ info.sensorDataIndex = sensorDataIndex;
+ device->sensorsByAbsCode.emplace(absCode, info);
+ }
+
+ void setMscEvent(int32_t deviceId, int32_t mscEvent) {
+ Device* device = getDevice(deviceId);
+ typename BitArray<MSC_MAX>::Buffer buffer;
+ buffer[mscEvent / 32] = 1 << mscEvent % 32;
+ device->mscBitmask.loadFromBuffer(buffer);
+ }
+
+ void addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
+ mRawLightInfos.emplace(rawId, std::move(info));
+ }
+
+ void testLightBrightness(int32_t rawId, int32_t brightness) {
+ mLightBrightness.emplace(rawId, brightness);
+ }
+
+ void testLightIntensities(int32_t rawId,
+ const std::unordered_map<LightColor, int32_t> intensities) {
+ mLightIntensities.emplace(rawId, std::move(intensities));
+ }
+
+ bool getLedState(int32_t deviceId, int32_t led) {
+ Device* device = getDevice(deviceId);
+ return device->leds.valueFor(led);
+ }
+
+ std::vector<std::string>& getExcludedDevices() { return mExcludedDevices; }
+
+ void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
+ Device* device = getDevice(deviceId);
+ device->virtualKeys.push_back(definition);
+ }
+
+ void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
+ int32_t value) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = deviceId;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ mEvents.push_back(event);
+
+ if (type == EV_ABS) {
+ setAbsoluteAxisValue(deviceId, code, value);
+ }
+ }
+
+ void setVideoFrames(
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames) {
+ mVideoFrames = std::move(videoFrames);
+ }
+
+private:
+ Device* getDevice(int32_t deviceId) const {
+ ssize_t index = mDevices.indexOfKey(deviceId);
+ return index >= 0 ? mDevices.valueAt(index) : nullptr;
+ }
+
+ Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
+ Device* device = getDevice(deviceId);
+ return device ? device->classes : Flags<InputDeviceClass>(0);
+ }
+
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
+ Device* device = getDevice(deviceId);
+ return device ? device->identifier : InputDeviceIdentifier();
+ }
+
+ int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
+
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ *outConfiguration = device->configuration;
+ }
+ }
+
+ status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const override {
+ Device* device = getDevice(deviceId);
+ if (device && device->enabled) {
+ ssize_t index = device->absoluteAxes.indexOfKey(axis);
+ if (index >= 0) {
+ *outAxisInfo = device->absoluteAxes.valueAt(index);
+ return OK;
+ }
+ }
+ outAxisInfo->clear();
+ return -1;
+ }
+
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return device->relativeAxes.indexOfKey(axis) >= 0;
+ }
+ return false;
+ }
+
+ bool hasInputProperty(int32_t, int) const override { return false; }
+
+ bool hasMscEvent(int32_t deviceId, int mscEvent) const override final {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
+ }
+ return false;
+ }
+
+ status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ const KeyInfo* key = getKey(device, scanCode, usageCode);
+ if (key) {
+ if (outKeycode) {
+ *outKeycode = key->keyCode;
+ }
+ if (outFlags) {
+ *outFlags = key->flags;
+ }
+ if (outMetaState) {
+ *outMetaState = metaState;
+ }
+ return OK;
+ }
+ }
+ return NAME_NOT_FOUND;
+ }
+
+ const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const {
+ if (usageCode) {
+ ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
+ if (index >= 0) {
+ return &device->keysByUsageCode.valueAt(index);
+ }
+ }
+ if (scanCode) {
+ ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+ if (index >= 0) {
+ return &device->keysByScanCode.valueAt(index);
+ }
+ }
+ return nullptr;
+ }
+
+ status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
+
+ base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
+ int32_t absCode) {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return Errorf("Sensor device not found.");
+ }
+ auto it = device->sensorsByAbsCode.find(absCode);
+ if (it == device->sensorsByAbsCode.end()) {
+ return Errorf("Sensor map not found.");
+ }
+ const SensorInfo& info = it->second;
+ return std::make_pair(info.sensorType, info.sensorDataIndex);
+ }
+
+ void setExcludedDevices(const std::vector<std::string>& devices) override {
+ mExcludedDevices = devices;
+ }
+
+ size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+ std::scoped_lock lock(mLock);
+
+ const size_t filledSize = std::min(mEvents.size(), bufferSize);
+ std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+
+ mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
+ mEventsCondition.notify_all();
+ return filledSize;
+ }
+
+ std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
+ auto it = mVideoFrames.find(deviceId);
+ if (it != mVideoFrames.end()) {
+ std::vector<TouchVideoFrame> frames = std::move(it->second);
+ mVideoFrames.erase(deviceId);
+ return frames;
+ }
+ return {};
+ }
+
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
+ if (index >= 0) {
+ return device->scanCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
+ if (index >= 0) {
+ return device->keyCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->switchStates.indexOfKey(sw);
+ if (index >= 0) {
+ return device->switchStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
+ if (index >= 0) {
+ *outValue = device->absoluteAxisValue.valueAt(index);
+ return OK;
+ }
+ }
+ *outValue = 0;
+ return -1;
+ }
+
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const override {
+ bool result = false;
+ Device* device = getDevice(deviceId);
+ if (device) {
+ result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
+ for (size_t i = 0; i < numCodes; i++) {
+ for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
+ if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ }
+ }
+ for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+ if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+ return index >= 0;
+ }
+ return false;
+ }
+
+ bool hasLed(int32_t deviceId, int32_t led) const override {
+ Device* device = getDevice(deviceId);
+ return device && device->leds.indexOfKey(led) >= 0;
+ }
+
+ void setLedState(int32_t deviceId, int32_t led, bool on) override {}
+
+ void getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
+ outVirtualKeys.clear();
+
+ Device* device = getDevice(deviceId);
+ if (device) {
+ outVirtualKeys = device->virtualKeys;
+ }
+ }
+
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
+ return nullptr;
+ }
+
+ bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
+ return false;
+ }
+
+ void vibrate(int32_t, const VibrationElement&) override {}
+
+ void cancelVibrate(int32_t) override {}
+
+ std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
+
+ std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
+ return BATTERY_CAPACITY;
+ }
+
+ std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
+ return BATTERY_STATUS;
+ }
+
+ const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; }
+
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+ return std::nullopt;
+ }
+
+ const std::vector<int32_t> getRawLightIds(int32_t deviceId) override {
+ std::vector<int32_t> ids;
+ for (const auto& [rawId, info] : mRawLightInfos) {
+ ids.push_back(rawId);
+ }
+ return ids;
+ }
+
+ std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override {
+ auto it = mRawLightInfos.find(lightId);
+ if (it == mRawLightInfos.end()) {
+ return std::nullopt;
+ }
+ return it->second;
+ }
+
+ void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override {
+ mLightBrightness.emplace(lightId, brightness);
+ }
+
+ void setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) override {
+ mLightIntensities.emplace(lightId, intensities);
+ };
+
+ std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override {
+ auto lightIt = mLightBrightness.find(lightId);
+ if (lightIt == mLightBrightness.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+ }
+
+ std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t deviceId, int32_t lightId) override {
+ auto lightIt = mLightIntensities.find(lightId);
+ if (lightIt == mLightIntensities.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+ };
+
+ virtual bool isExternal(int32_t) const { return false; }
+
+ void dump(std::string&) override {}
+
+ void monitor() override {}
+
+ void requestReopenDevices() override {}
+
+ void wake() override {}
+};
+
+// --- TestInputMapper---
+
+class TestInputMapper : public InputMapper {
+ uint32_t mSources;
+ int32_t mKeyboardType;
+ int32_t mMetaState;
+ KeyedVector<int32_t, int32_t> mKeyCodeStates;
+ KeyedVector<int32_t, int32_t> mScanCodeStates;
+ KeyedVector<int32_t, int32_t> mSwitchStates;
+ std::vector<int32_t> mSupportedKeyCodes;
+
+ std::mutex mLock;
+ std::condition_variable mStateChangedCondition;
+ bool mConfigureWasCalled GUARDED_BY(mLock);
+ bool mResetWasCalled GUARDED_BY(mLock);
+ bool mProcessWasCalled GUARDED_BY(mLock);
+ RawEvent mLastEvent GUARDED_BY(mLock);
+
+ std::optional<DisplayViewport> mViewport;
+
+public:
+ TestInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
+ : InputMapper(deviceContext),
+ mSources(sources),
+ mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE),
+ mMetaState(0),
+ mConfigureWasCalled(false),
+ mResetWasCalled(false),
+ mProcessWasCalled(false) {}
+
+ virtual ~TestInputMapper() {}
+
+ void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
+
+ void setMetaState(int32_t metaState) { mMetaState = metaState; }
+ void setKeyCodeState(int32_t keyCode, int32_t state) {
+ mKeyCodeStates.replaceValueFor(keyCode, state);
+ }
+
+ void setScanCodeState(int32_t scanCode, int32_t state) {
+ mScanCodeStates.replaceValueFor(scanCode, state);
+ }
+
+ void setSwitchState(int32_t switchCode, int32_t state) {
+ mSwitchStates.replaceValueFor(switchCode, state);
+ }
+
+ void addSupportedKeyCode(int32_t keyCode) { mSupportedKeyCodes.push_back(keyCode); }
+
+private:
+ uint32_t getSources() override { return mSources; }
+
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
+ InputMapper::populateDeviceInfo(deviceInfo);
+
+ if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
+ deviceInfo->setKeyboardType(mKeyboardType);
+ }
+ }
+
+ void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mConfigureWasCalled = true;
+
+ // Find the associated viewport if exist.
+ const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
+ if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mViewport = config->getDisplayViewportByPort(*displayPort);
+ }
+
+ mStateChangedCondition.notify_all();
+ }
+
+ void reset(nsecs_t) override {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mResetWasCalled = true;
+ mStateChangedCondition.notify_all();
+ }
+
+ void process(const RawEvent* rawEvent) override {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mLastEvent = *rawEvent;
+ mProcessWasCalled = true;
+ mStateChangedCondition.notify_all();
+ }
+
+ int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
+ ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
+ return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
+ }
+
+ int32_t getScanCodeState(uint32_t, int32_t scanCode) override {
+ ssize_t index = mScanCodeStates.indexOfKey(scanCode);
+ return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
+ }
+
+ int32_t getSwitchState(uint32_t, int32_t switchCode) override {
+ ssize_t index = mSwitchStates.indexOfKey(switchCode);
+ return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
+ }
+
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override {
+ for (size_t i = 0; i < numCodes; i++) {
+ for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
+ if (keyCodes[i] == mSupportedKeyCodes[j]) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+ bool result = mSupportedKeyCodes.size() > 0;
+ return result;
+ }
+
+ virtual int32_t getMetaState() { return mMetaState; }
+
+ virtual void fadePointer() {}
+
+ virtual std::optional<int32_t> getAssociatedDisplay() {
+ if (mViewport) {
+ return std::make_optional(mViewport->displayId);
+ }
+ return std::nullopt;
+ }
+};
+
+// --- InstrumentedInputReader ---
+
+class InstrumentedInputReader : public InputReader {
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
+
+public:
+ InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputListenerInterface>& listener)
+ : InputReader(eventHub, policy, listener), mTestContext(this) {}
+
+ virtual ~InstrumentedInputReader() {}
+
+ void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location = "") {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ int32_t generation = deviceId + 1;
+ return std::make_shared<InputDevice>(&mTestContext, deviceId, generation, identifier);
+ }
+
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
+protected:
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId,
+ const InputDeviceIdentifier& identifier)
+ REQUIRES(mLock) {
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
+ return device;
+ }
+ return InputReader::createDeviceLocked(eventHubId, identifier);
+ }
+
+ // --- TestInputReaderContext ---
+ class TestInputReaderContext : public ContextImpl {
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
+
+ public:
+ TestInputReaderContext(InputReader* reader)
+ : ContextImpl(reader),
+ mGlobalMetaState(0),
+ mUpdateGlobalMetaStateWasCalled(false),
+ mGeneration(1) {}
+
+ virtual ~TestInputReaderContext() {}
+
+ void assertUpdateGlobalMetaStateWasCalled() { mUpdateGlobalMetaStateWasCalled = false; }
+
+ void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+ uint32_t getGeneration() { return mGeneration; }
+
+ void updateGlobalMetaState() override {
+ mUpdateGlobalMetaStateWasCalled = true;
+ ContextImpl::updateGlobalMetaState();
+ }
+
+ int32_t getGlobalMetaState() override {
+ return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+ }
+
+ int32_t bumpGeneration() override {
+ mGeneration = ContextImpl::bumpGeneration();
+ return mGeneration;
+ }
+ } mTestContext;
+
+public:
+ TestInputReaderContext* getContext() { return &mTestContext; }
+};
+
+// --- InputMapperTest ---
+
+class InputMapperTest {
+public:
+ std::shared_ptr<TestEventHub> mTestEventHub;
+ sp<TestInputReaderPolicy> mTestPolicy;
+ sp<TestInputListener> mTestListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(Flags<InputDeviceClass> classes) {
+ mTestEventHub = std::make_unique<TestEventHub>();
+ mTestPolicy = new TestInputReaderPolicy();
+ mTestListener = new TestInputListener();
+ mReader = std::make_unique<InstrumentedInputReader>(mTestEventHub, mTestPolicy,
+ mTestListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+ }
+
+ void SetUp() { SetUp(DEVICE_CLASSES); }
+
+ void TearDown() {
+ mTestListener.clear();
+ mTestPolicy.clear();
+ }
+ virtual ~InputMapperTest() {}
+
+ void addConfigurationProperty(const char* key, const char* value) {
+ mTestEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value));
+ }
+
+ void configureDevice(uint32_t changes) {
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ mDevice->configure(ARBITRARY_TIME, mTestPolicy->getReaderConfiguration(), changes);
+ }
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ Flags<InputDeviceClass> classes) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mTestEventHub->addDevice(eventHubId, name, classes);
+ mReader->loopOnce();
+ return device;
+ }
+
+ template <class T, typename... Args>
+ T& addMapperAndConfigure(Args... args) {
+ T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
+ configureDevice(0);
+ mDevice->reset(ARBITRARY_TIME);
+ mapper.reset(ARBITRARY_TIME);
+ return mapper;
+ }
+
+ void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ int32_t orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ mTestPolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
+ uniqueId, physicalPort, viewportType);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ }
+
+ void clearViewports() { mTestPolicy->clearViewports(); }
+
+ void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
+ int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = mapper.getDeviceContext().getEventHubId();
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ mapper.process(&event);
+ mReader->loopOnce();
+ }
+ void Process_DeactivateViewport_AbortTouches();
+};
+
+void InputMapperTest::Process_DeactivateViewport_AbortTouches() {
+ SetUp();
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ mTestPolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
+ ViewportType::INTERNAL);
+ std::optional<DisplayViewport> optionalDisplayViewport =
+ mTestPolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
+ DisplayViewport displayViewport = *optionalDisplayViewport;
+
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ mTestEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+ mTestEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // Finger down
+ int32_t x = 100, y = 100;
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+
+ NotifyMotionArgs motionArgs;
+
+ // Deactivate display viewport
+ displayViewport.isActive = false;
+ mTestPolicy->updateViewport(displayViewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Finger move
+ x += 10, y += 10;
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+
+ // Reactivate display viewport
+ displayViewport.isActive = true;
+ mTestPolicy->updateViewport(displayViewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+ // Finger move again
+ x += 10, y += 10;
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+}
+
+} // namespace android
+
+int main() {
+ android::InputMapperTest inputMapperTest;
+ inputMapperTest.Process_DeactivateViewport_AbortTouches();
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0684.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0684.java
new file mode 100644
index 0000000..4df0f6f
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0684.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2021 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.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0684 extends SecurityTestCase {
+
+ /**
+ * b/179839665
+ * Vulnerability Behaviour: SIGSEGV in Self
+ */
+ @AsbSecurityTest(cveBugId = 179839665)
+ @Test
+ public void testPocCVE_2021_0684() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2021-0684", null, getDevice());
+ }
+}