From 473174ea68fa5703ef162d85fe2805d7d313e41a Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 27 Dec 2017 16:44:42 -0800 Subject: Add a new InputClassifier stage The new InputClassifier stage could be used for additional processing of input events prior to sending them to InputDispatcher. The new flow of events will be InputReader -> InputClassifier -> InputDispatcher. Here, we are calling the InputClassifier HAL and setting the MotionEvent classification appropriately. Bug: 62940136 Test: override notifyMotion to add extra flags to NotifyMotionArgs for certain types of input events. Test: atest inputflinger_tests Change-Id: I2f390dc69f587ea25a3be8e4b8d5a207a5d529bf --- services/inputflinger/InputClassifier.cpp | 692 ++++++++++++++++++++++++++++++ 1 file changed, 692 insertions(+) create mode 100644 services/inputflinger/InputClassifier.cpp (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp new file mode 100644 index 0000000000..11427c3135 --- /dev/null +++ b/services/inputflinger/InputClassifier.cpp @@ -0,0 +1,692 @@ +/* + * 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. + */ + +#define LOG_TAG "InputClassifier" + +#include "InputClassifier.h" + +#include +#include +#include +#include +#if defined(__linux__) + #include +#endif +#include + +#include + +using android::hardware::hidl_bitfield; +using android::hardware::hidl_vec; +using namespace android::hardware::input; + +namespace android { + +static constexpr bool DEBUG = false; + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +// Feature flag name for the deep press feature +static const char* DEEP_PRESS_ENABLED = "deep_press_enabled"; + +//Max number of elements to store in mEvents. +static constexpr size_t MAX_EVENTS = 5; + +template +static V getValueForKey(const std::unordered_map& map, K key, V defaultValue) { + auto it = map.find(key); + if (it == map.end()) { + return defaultValue; + } + return it->second; +} + +static common::V1_0::Source getSource(uint32_t source) { + static_assert(static_cast(AINPUT_SOURCE_UNKNOWN) == + common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch"); + static_assert(static_cast(AINPUT_SOURCE_KEYBOARD) == + common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_DPAD) == + common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_GAMEPAD) == + common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TOUCHSCREEN) == + common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch"); + static_assert(static_cast(AINPUT_SOURCE_MOUSE) == + common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch"); + static_assert(static_cast(AINPUT_SOURCE_STYLUS) == + common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch"); + static_assert(static_cast(AINPUT_SOURCE_BLUETOOTH_STYLUS) == + common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TRACKBALL) == + common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch"); + static_assert(static_cast(AINPUT_SOURCE_MOUSE_RELATIVE) == + common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TOUCHPAD) == + common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TOUCH_NAVIGATION) == + common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch"); + static_assert(static_cast(AINPUT_SOURCE_JOYSTICK) == + common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch"); + static_assert(static_cast(AINPUT_SOURCE_ROTARY_ENCODER) == + common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch"); + static_assert(static_cast(AINPUT_SOURCE_ANY) == + common::V1_0::Source::ANY, "SOURCE_ANY mismatch"); + return static_cast(source); +} + +static common::V1_0::Action getAction(int32_t actionMasked) { + static_assert(static_cast(AMOTION_EVENT_ACTION_DOWN) == + common::V1_0::Action::DOWN, "ACTION_DOWN mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_UP) == + common::V1_0::Action::UP, "ACTION_UP mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_MOVE) == + common::V1_0::Action::MOVE, "ACTION_MOVE mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_CANCEL) == + common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_OUTSIDE) == + common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_POINTER_DOWN) == + common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_POINTER_UP) == + common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch"); + static_assert(static_cast( AMOTION_EVENT_ACTION_HOVER_MOVE) == + common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_SCROLL) == + common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_HOVER_ENTER) == + common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_HOVER_EXIT) == + common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_BUTTON_PRESS) == + common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_BUTTON_RELEASE) == + common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch"); + return static_cast(actionMasked); +} + +static common::V1_0::Button getActionButton(int32_t actionButton) { + static_assert(static_cast(0) == + common::V1_0::Button::NONE, "BUTTON_NONE mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_PRIMARY) == + common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_SECONDARY) == + common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_TERTIARY) == + common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_BACK) == + common::V1_0::Button::BACK, "BUTTON_BACK mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_FORWARD) == + common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) == + common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) == + common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch"); + return static_cast(actionButton); +} + +static hidl_bitfield getFlags(int32_t flags) { + static_assert(static_cast(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) == + common::V1_0::Flag::WINDOW_IS_OBSCURED); + static_assert(static_cast(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) == + common::V1_0::Flag::IS_GENERATED_GESTURE); + static_assert(static_cast(AMOTION_EVENT_FLAG_TAINTED) == + common::V1_0::Flag::TAINTED); + return static_cast>(flags); +} + +static hidl_bitfield getPolicyFlags(int32_t flags) { + static_assert(static_cast(POLICY_FLAG_WAKE) == + common::V1_0::PolicyFlag::WAKE); + static_assert(static_cast(POLICY_FLAG_VIRTUAL) == + common::V1_0::PolicyFlag::VIRTUAL); + static_assert(static_cast(POLICY_FLAG_FUNCTION) == + common::V1_0::PolicyFlag::FUNCTION); + static_assert(static_cast(POLICY_FLAG_GESTURE) == + common::V1_0::PolicyFlag::GESTURE); + static_assert(static_cast(POLICY_FLAG_INJECTED) == + common::V1_0::PolicyFlag::INJECTED); + static_assert(static_cast(POLICY_FLAG_TRUSTED) == + common::V1_0::PolicyFlag::TRUSTED); + static_assert(static_cast(POLICY_FLAG_FILTERED) == + common::V1_0::PolicyFlag::FILTERED); + static_assert(static_cast(POLICY_FLAG_DISABLE_KEY_REPEAT) == + common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT); + static_assert(static_cast(POLICY_FLAG_INTERACTIVE) == + common::V1_0::PolicyFlag::INTERACTIVE); + static_assert(static_cast(POLICY_FLAG_PASS_TO_USER) == + common::V1_0::PolicyFlag::PASS_TO_USER); + return static_cast>(flags); +} + +static hidl_bitfield getEdgeFlags(int32_t flags) { + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_NONE) == + common::V1_0::EdgeFlag::NONE); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_TOP) == + common::V1_0::EdgeFlag::TOP); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_BOTTOM) == + common::V1_0::EdgeFlag::BOTTOM); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_LEFT) == + common::V1_0::EdgeFlag::LEFT); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_RIGHT) == + common::V1_0::EdgeFlag::RIGHT); + return static_cast>(flags); +} + +static hidl_bitfield getMetastate(int32_t state) { + static_assert(static_cast(AMETA_NONE) == + common::V1_0::Meta::NONE); + static_assert(static_cast(AMETA_ALT_ON) == + common::V1_0::Meta::ALT_ON); + static_assert(static_cast(AMETA_ALT_LEFT_ON) == + common::V1_0::Meta::ALT_LEFT_ON); + static_assert(static_cast(AMETA_ALT_RIGHT_ON) == + common::V1_0::Meta::ALT_RIGHT_ON); + static_assert(static_cast(AMETA_SHIFT_ON) == + common::V1_0::Meta::SHIFT_ON); + static_assert(static_cast(AMETA_SHIFT_LEFT_ON) == + common::V1_0::Meta::SHIFT_LEFT_ON); + static_assert(static_cast(AMETA_SHIFT_RIGHT_ON) == + common::V1_0::Meta::SHIFT_RIGHT_ON); + static_assert(static_cast(AMETA_SYM_ON) == + common::V1_0::Meta::SYM_ON); + static_assert(static_cast(AMETA_FUNCTION_ON) == + common::V1_0::Meta::FUNCTION_ON); + static_assert(static_cast(AMETA_CTRL_ON) == + common::V1_0::Meta::CTRL_ON); + static_assert(static_cast(AMETA_CTRL_LEFT_ON) == + common::V1_0::Meta::CTRL_LEFT_ON); + static_assert(static_cast(AMETA_CTRL_RIGHT_ON) == + common::V1_0::Meta::CTRL_RIGHT_ON); + static_assert(static_cast(AMETA_META_ON) == + common::V1_0::Meta::META_ON); + static_assert(static_cast(AMETA_META_LEFT_ON) == + common::V1_0::Meta::META_LEFT_ON); + static_assert(static_cast(AMETA_META_RIGHT_ON) == + common::V1_0::Meta::META_RIGHT_ON); + static_assert(static_cast(AMETA_CAPS_LOCK_ON) == + common::V1_0::Meta::CAPS_LOCK_ON); + static_assert(static_cast(AMETA_NUM_LOCK_ON) == + common::V1_0::Meta::NUM_LOCK_ON); + static_assert(static_cast(AMETA_SCROLL_LOCK_ON) == + common::V1_0::Meta::SCROLL_LOCK_ON); + return static_cast>(state); +} + +static hidl_bitfield getButtonState(int32_t buttonState) { + // No need for static_assert here. + // The button values have already been asserted in getActionButton(..) above + return static_cast>(buttonState); +} + +static common::V1_0::ToolType getToolType(int32_t toolType) { + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) == + common::V1_0::ToolType::UNKNOWN); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_FINGER) == + common::V1_0::ToolType::FINGER); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_STYLUS) == + common::V1_0::ToolType::STYLUS); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_MOUSE) == + common::V1_0::ToolType::MOUSE); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_ERASER) == + common::V1_0::ToolType::ERASER); + return static_cast(toolType); +} + +static common::V1_0::Axis getAxis(uint64_t axis) { + static_assert(static_cast(AMOTION_EVENT_AXIS_X) == + common::V1_0::Axis::X); + static_assert(static_cast(AMOTION_EVENT_AXIS_Y) == + common::V1_0::Axis::Y); + static_assert(static_cast(AMOTION_EVENT_AXIS_PRESSURE) == + common::V1_0::Axis::PRESSURE); + static_assert(static_cast(AMOTION_EVENT_AXIS_SIZE) == + common::V1_0::Axis::SIZE); + static_assert(static_cast(AMOTION_EVENT_AXIS_TOUCH_MAJOR) == + common::V1_0::Axis::TOUCH_MAJOR); + static_assert(static_cast(AMOTION_EVENT_AXIS_TOUCH_MINOR) == + common::V1_0::Axis::TOUCH_MINOR); + static_assert(static_cast(AMOTION_EVENT_AXIS_TOOL_MAJOR) == + common::V1_0::Axis::TOOL_MAJOR); + static_assert(static_cast(AMOTION_EVENT_AXIS_TOOL_MINOR) == + common::V1_0::Axis::TOOL_MINOR); + static_assert(static_cast(AMOTION_EVENT_AXIS_ORIENTATION) == + common::V1_0::Axis::ORIENTATION); + static_assert(static_cast(AMOTION_EVENT_AXIS_VSCROLL) == + common::V1_0::Axis::VSCROLL); + static_assert(static_cast(AMOTION_EVENT_AXIS_HSCROLL) == + common::V1_0::Axis::HSCROLL); + static_assert(static_cast(AMOTION_EVENT_AXIS_Z) == + common::V1_0::Axis::Z); + static_assert(static_cast(AMOTION_EVENT_AXIS_RX) == + common::V1_0::Axis::RX); + static_assert(static_cast(AMOTION_EVENT_AXIS_RY) == + common::V1_0::Axis::RY); + static_assert(static_cast(AMOTION_EVENT_AXIS_RZ) == + common::V1_0::Axis::RZ); + static_assert(static_cast(AMOTION_EVENT_AXIS_HAT_X) == + common::V1_0::Axis::HAT_X); + static_assert(static_cast(AMOTION_EVENT_AXIS_HAT_Y) == + common::V1_0::Axis::HAT_Y); + static_assert(static_cast(AMOTION_EVENT_AXIS_LTRIGGER) == + common::V1_0::Axis::LTRIGGER); + static_assert(static_cast(AMOTION_EVENT_AXIS_RTRIGGER) == + common::V1_0::Axis::RTRIGGER); + static_assert(static_cast(AMOTION_EVENT_AXIS_THROTTLE) == + common::V1_0::Axis::THROTTLE); + static_assert(static_cast(AMOTION_EVENT_AXIS_RUDDER) == + common::V1_0::Axis::RUDDER); + static_assert(static_cast(AMOTION_EVENT_AXIS_WHEEL) == + common::V1_0::Axis::WHEEL); + static_assert(static_cast(AMOTION_EVENT_AXIS_GAS) == + common::V1_0::Axis::GAS); + static_assert(static_cast(AMOTION_EVENT_AXIS_BRAKE) == + common::V1_0::Axis::BRAKE); + static_assert(static_cast(AMOTION_EVENT_AXIS_DISTANCE) == + common::V1_0::Axis::DISTANCE); + static_assert(static_cast(AMOTION_EVENT_AXIS_TILT) == + common::V1_0::Axis::TILT); + static_assert(static_cast(AMOTION_EVENT_AXIS_SCROLL) == + common::V1_0::Axis::SCROLL); + static_assert(static_cast(AMOTION_EVENT_AXIS_RELATIVE_X) == + common::V1_0::Axis::RELATIVE_X); + static_assert(static_cast(AMOTION_EVENT_AXIS_RELATIVE_Y) == + common::V1_0::Axis::RELATIVE_Y); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_1) == + common::V1_0::Axis::GENERIC_1); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_2) == + common::V1_0::Axis::GENERIC_2); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_3) == + common::V1_0::Axis::GENERIC_3); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_4) == + common::V1_0::Axis::GENERIC_4); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_5) == + common::V1_0::Axis::GENERIC_5); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_6) == + common::V1_0::Axis::GENERIC_6); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_7) == + common::V1_0::Axis::GENERIC_7); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_8) == + common::V1_0::Axis::GENERIC_8); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_9) == + common::V1_0::Axis::GENERIC_9); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_10) == + common::V1_0::Axis::GENERIC_10); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_11) == + common::V1_0::Axis::GENERIC_11); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_12) == + common::V1_0::Axis::GENERIC_12); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_13) == + common::V1_0::Axis::GENERIC_13); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_14) == + common::V1_0::Axis::GENERIC_14); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_15) == + common::V1_0::Axis::GENERIC_15); + static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_16) == + common::V1_0::Axis::GENERIC_16); + return static_cast(axis); +} + +static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) { + common::V1_0::VideoFrame out; + out.width = frame.getWidth(); + out.height = frame.getHeight(); + out.data = frame.getData(); + struct timeval timestamp = frame.getTimestamp(); + out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) + + microseconds_to_nanoseconds(timestamp.tv_usec); + return out; +} + +static std::vector convertVideoFrames( + const std::vector& frames) { + std::vector out; + for (const TouchVideoFrame& frame : frames) { + out.push_back(getHalVideoFrame(frame)); + } + return out; +} + +static uint8_t getActionIndex(int32_t action) { + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> + AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; +} + +static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, + std::vector* outPointerProperties, + std::vector* outPointerCoords) { + outPointerProperties->reserve(args.pointerCount); + outPointerCoords->reserve(args.pointerCount); + for (size_t i = 0; i < args.pointerCount; i++) { + common::V1_0::PointerProperties properties; + properties.id = args.pointerProperties[i].id; + properties.toolType = getToolType(args.pointerProperties[i].toolType); + outPointerProperties->push_back(properties); + + common::V1_0::PointerCoords coords; + BitSet64 bits (args.pointerCoords[i].bits); + std::vector values; + size_t index = 0; + while (!bits.isEmpty()) { + uint32_t axis = bits.clearFirstMarkedBit(); + coords.bits |= 1 << static_cast(getAxis(axis)); + float value = args.pointerCoords[i].values[index++]; + values.push_back(value); + } + coords.values = values; + outPointerCoords->push_back(coords); + } +} + +static common::V1_0::MotionEvent getMotionEvent(const NotifyMotionArgs& args) { + common::V1_0::MotionEvent event; + event.deviceId = args.deviceId; + event.source = getSource(args.source); + event.displayId = args.displayId; + event.downTime = args.downTime; + event.eventTime = args.eventTime; + event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); + event.actionIndex = getActionIndex(args.action); + event.actionButton = getActionButton(args.actionButton); + event.flags = getFlags(args.flags); + event.policyFlags = getPolicyFlags(args.policyFlags); + event.edgeFlags = getEdgeFlags(args.edgeFlags); + event.metaState = getMetastate(args.metaState); + event.buttonState = getButtonState(args.buttonState); + event.xPrecision = args.xPrecision; + event.yPrecision = args.yPrecision; + + std::vector pointerProperties; + std::vector pointerCoords; + getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords); + event.pointerProperties = pointerProperties; + event.pointerCoords = pointerCoords; + + event.deviceTimestamp = args.deviceTimestamp; + event.frames = convertVideoFrames(args.videoFrames); + + return event; +} + +static MotionClassification getMotionClassification(common::V1_0::Classification classification) { + static_assert(MotionClassification::NONE == + static_cast(common::V1_0::Classification::NONE)); + static_assert(MotionClassification::AMBIGUOUS_GESTURE == + static_cast(common::V1_0::Classification::AMBIGUOUS_GESTURE)); + static_assert(MotionClassification::DEEP_PRESS == + static_cast(common::V1_0::Classification::DEEP_PRESS)); + return static_cast(classification); +} + +static bool isTouchEvent(const NotifyMotionArgs& args) { + return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN; +} + +// Check if the "deep touch" feature is on. +static bool deepPressEnabled() { + std::string flag_value = server_configurable_flags::GetServerConfigurableFlag( + INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true"); + std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower); + if (flag_value == "1" || flag_value == "true") { + ALOGI("Deep press feature enabled."); + return true; + } + ALOGI("Deep press feature is not enabled."); + return false; +} + + +// --- ClassifierEvent --- + +ClassifierEvent::ClassifierEvent(std::unique_ptr args) : + type(ClassifierEventType::MOTION), args(std::move(args)) { }; +ClassifierEvent::ClassifierEvent(std::unique_ptr args) : + type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { }; +ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr args) : + type(type), args(std::move(args)) { }; + +ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) : + type(other.type), args(std::move(other.args)) { }; + +ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) { + type = other.type; + args = std::move(other.args); + return *this; +} + +ClassifierEvent ClassifierEvent::createHalResetEvent() { + return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr); +} + +ClassifierEvent ClassifierEvent::createExitEvent() { + return ClassifierEvent(ClassifierEventType::EXIT, nullptr); +} + +std::optional ClassifierEvent::getDeviceId() const { + switch (type) { + case ClassifierEventType::MOTION: { + NotifyMotionArgs* motionArgs = static_cast(args.get()); + return motionArgs->deviceId; + } + case ClassifierEventType::DEVICE_RESET: { + NotifyDeviceResetArgs* deviceResetArgs = + static_cast(args.get()); + return deviceResetArgs->deviceId; + } + case ClassifierEventType::HAL_RESET: { + return std::nullopt; + } + case ClassifierEventType::EXIT: { + return std::nullopt; + } + } +} + +// --- MotionClassifier --- + +MotionClassifier::MotionClassifier( + sp service) : + mEvents(MAX_EVENTS), mService(service) { + mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this); +#if defined(__linux__) + // Set the thread name for debugging + pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); +#endif +} + +MotionClassifier::~MotionClassifier() { + requestExit(); + mHalThread.join(); +} + +void MotionClassifier::ensureHalThread(const char* function) { + if (DEBUG) { + if (std::this_thread::get_id() != mHalThread.get_id()) { + ALOGE("Function %s should only be called from InputClassifier thread", function); + } + } +} + +/** + * Obtain the classification from the HAL for a given MotionEvent. + * Should only be called from the InputClassifier thread (mHalThread). + * Should not be called from the thread that notifyMotion runs on. + * + * There is no way to provide a timeout for a HAL call. So if the HAL takes too long + * to return a classification, this would directly impact the touch latency. + * To remove any possibility of negatively affecting the touch latency, the HAL + * is called from a dedicated thread. + */ +void MotionClassifier::callInputClassifierHal() { + ensureHalThread(__func__); + while (true) { + ClassifierEvent event = mEvents.pop(); + switch (event.type) { + case ClassifierEventType::MOTION: { + NotifyMotionArgs* motionArgs = static_cast(event.args.get()); + common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs); + common::V1_0::Classification halClassification = mService->classify(motionEvent); + updateClassification(motionArgs->deviceId, motionArgs->eventTime, + getMotionClassification(halClassification)); + break; + } + case ClassifierEventType::DEVICE_RESET: { + const int32_t deviceId = *(event.getDeviceId()); + mService->resetDevice(deviceId); + setClassification(deviceId, MotionClassification::NONE); + break; + } + case ClassifierEventType::HAL_RESET: { + mService->reset(); + clearClassifications(); + break; + } + case ClassifierEventType::EXIT: { + clearClassifications(); + return; + } + } + } +} + +void MotionClassifier::requestExit() { + reset(); + mEvents.push(ClassifierEvent::createExitEvent()); +} + +void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime, + MotionClassification classification) { + std::scoped_lock lock(mLock); + const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast(0)); + if (eventTime < lastDownTime) { + // HAL just finished processing an event that belonged to an earlier gesture, + // but new gesture is already in progress. Drop this classification. + ALOGW("Received late classification. Late by at least %" PRId64 " ms.", + nanoseconds_to_milliseconds(lastDownTime - eventTime)); + return; + } + mClassifications[deviceId] = classification; +} + +void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) { + std::scoped_lock lock(mLock); + mClassifications[deviceId] = classification; +} + +void MotionClassifier::clearClassifications() { + std::scoped_lock lock(mLock); + mClassifications.clear(); +} + +MotionClassification MotionClassifier::getClassification(int32_t deviceId) { + std::scoped_lock lock(mLock); + return getValueForKey(mClassifications, deviceId, MotionClassification::NONE); +} + +void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) { + std::scoped_lock lock(mLock); + mLastDownTimes[deviceId] = downTime; + mClassifications[deviceId] = MotionClassification::NONE; +} + +MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) { + if (!mService) { + // If HAL is not present, do nothing + return MotionClassification::NONE; + } + if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) { + updateLastDownTime(args.deviceId, args.downTime); + } + + ClassifierEvent event(std::make_unique(args)); + bool elementAdded = mEvents.push(std::move(event)); + if (!elementAdded) { + // Queue should not ever overfill. Suspect HAL is slow. + ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime); + reset(); + return MotionClassification::NONE; + } + return getClassification(args.deviceId); +} + +void MotionClassifier::reset() { + mEvents.clear(); + mEvents.push(ClassifierEvent::createHalResetEvent()); +} + +/** + * Per-device reset. Clear the outstanding events that are going to be sent to HAL. + * Request InputClassifier thread to call resetDevice for this particular device. + */ +void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { + int32_t deviceId = args.deviceId; + // Clear the pending events right away, to avoid unnecessary work done by the HAL. + mEvents.erase([deviceId](const ClassifierEvent& event) { + std::optional eventDeviceId = event.getDeviceId(); + return eventDeviceId && (*eventDeviceId == deviceId); + }); + mEvents.push(std::make_unique(args)); +} + +// --- InputClassifier --- + +InputClassifier::InputClassifier(const sp& listener) : + mListener(listener) { + if (deepPressEnabled()) { + sp service = + classifier::V1_0::IInputClassifier::getService(); + if (service) { + mMotionClassifier = std::make_unique(service); + } else { + ALOGI("Could not obtain InputClassifier HAL"); + } + } +}; + +void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { + // pass through + mListener->notifyConfigurationChanged(args); +} + +void InputClassifier::notifyKey(const NotifyKeyArgs* args) { + // pass through + mListener->notifyKey(args); +} + +void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { + if (mMotionClassifier && isTouchEvent(*args)) { + // We only cover touch events, for now. + NotifyMotionArgs newArgs = NotifyMotionArgs(*args); + newArgs.classification = mMotionClassifier->classify(newArgs); + args = &newArgs; + } + mListener->notifyMotion(args); +} + +void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { + // pass through + mListener->notifySwitch(args); +} + +void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + if (mMotionClassifier) { + mMotionClassifier->reset(*args); + } + // continue to next stage + mListener->notifyDeviceReset(args); +} + +} // namespace android \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 6dbc3f6c686adbcbb4eff28aec2f2018664e261b Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 4 Feb 2019 14:30:11 -0800 Subject: Reset InputClassifier HAL when system starts Under normal operation, the system and the HAL will both start at the same time, and will have initial state. However, since they are in separate, and independent, processes, one can crash while another would remain functional. For example, if the system crashes, the HAL may still be operational. To account for these situations, send a complete reset to the HAL when the system loads. This would help ensure that HAL has consistent state every time the system starts. Test: none Bug: 117935272 Change-Id: I90e53fd4205f018c87af8eb8ac16d54fe9a62011 --- services/inputflinger/InputClassifier.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 11427c3135..7ade0d497f 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -505,6 +505,11 @@ MotionClassifier::MotionClassifier( // Set the thread name for debugging pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); #endif + // Under normal operation, we do not need to reset the HAL here. But in the case where system + // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already + // have received events in the past. That means, that HAL could be in an inconsistent state + // once it receives events from the newly created MotionClassifier. + mEvents.push(ClassifierEvent::createHalResetEvent()); } MotionClassifier::~MotionClassifier() { -- cgit v1.2.3-59-g8ed1b From a028c449a64a98b62a0553418fd817ec1392e6cc Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 4 Feb 2019 14:33:23 -0800 Subject: Add dump to Input Classifier Currently, the InputClassifier stage that is positioned between InputReader and InputDispatcher is not accounted for when doing a 'dumpsys input'. But that stage may contain important information about the operation and the InputClassifier HAL. Add the dump(..) command to both InputClassifier and MotionClassifier. Test: dumpsys input and inspect the resulting text Next, crash the InputClassifier HAL (killall android.hardware.input.classifier@1.0-service Then again 'dumpsys input' to check that the HAL status is 'not responding' Bug: 117935272 Change-Id: Ie7ddca2e355e094f93236a9bc667faad7a99ee70 --- services/inputflinger/BlockingQueue.h | 10 +++++++ services/inputflinger/InputClassifier.cpp | 45 +++++++++++++++++++++++++++++++ services/inputflinger/InputClassifier.h | 15 +++++++++++ services/inputflinger/InputManager.cpp | 4 +++ services/inputflinger/InputManager.h | 1 + 5 files changed, 75 insertions(+) (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h index b892120d41..c9eb683fcb 100644 --- a/services/inputflinger/BlockingQueue.h +++ b/services/inputflinger/BlockingQueue.h @@ -80,6 +80,16 @@ public: mQueue.clear(); }; + /** + * How many elements are currently stored in the queue. + * Primary used for debugging. + * Does not block. + */ + size_t size() { + std::scoped_lock lock(mLock); + return mQueue.size(); + } + private: size_t mCapacity; /** diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 7ade0d497f..09a004c1bb 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -19,6 +19,7 @@ #include "InputClassifier.h" #include +#include #include #include #include @@ -26,9 +27,17 @@ #include #endif #include +#include #include +#define INDENT1 " " +#define INDENT2 " " +#define INDENT3 " " +#define INDENT4 " " +#define INDENT5 " " + +using android::base::StringPrintf; using android::hardware::hidl_bitfield; using android::hardware::hidl_vec; using namespace android::hardware::input; @@ -646,6 +655,30 @@ void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { mEvents.push(std::make_unique(args)); } +void MotionClassifier::dump(std::string& dump) { + std::scoped_lock lock(mLock); + std::string serviceStatus = mService->ping().isOk() ? "running" : " not responding"; + dump += StringPrintf(INDENT2 "mService status: %s\n", serviceStatus.c_str()); + dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n", + mEvents.size(), MAX_EVENTS); + dump += INDENT2 "mClassifications, mLastDownTimes:\n"; + dump += INDENT3 "Device Id\tClassification\tLast down time"; + // Combine mClassifications and mLastDownTimes into a single table. + // Create a superset of device ids. + std::unordered_set deviceIds; + std::for_each(mClassifications.begin(), mClassifications.end(), + [&deviceIds](auto pair){ deviceIds.insert(pair.first); }); + std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(), + [&deviceIds](auto pair){ deviceIds.insert(pair.first); }); + for(int32_t deviceId : deviceIds) { + const MotionClassification classification = + getValueForKey(mClassifications, deviceId, MotionClassification::NONE); + const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast(0)); + dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64, + deviceId, motionClassificationToString(classification), downTime); + } +} + // --- InputClassifier --- InputClassifier::InputClassifier(const sp& listener) : @@ -694,4 +727,16 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); } +void InputClassifier::dump(std::string& dump) { + dump += "Input Classifier State:\n"; + + dump += INDENT1 "Motion Classifier:\n"; + if (mMotionClassifier) { + mMotionClassifier->dump(dump); + } else { + dump += INDENT2 ""; + } + dump += "\n"; +} + } // namespace android \ No newline at end of file diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index cb4649417f..4b9dae275b 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -70,6 +70,11 @@ public: virtual MotionClassification classify(const NotifyMotionArgs& args) = 0; virtual void reset() = 0; virtual void reset(const NotifyDeviceResetArgs& args) = 0; + + /** + * Dump the state of the motion classifier + */ + virtual void dump(std::string& dump) = 0; }; /** @@ -77,6 +82,12 @@ public: * Provides classification to events. */ class InputClassifierInterface : public virtual RefBase, public InputListenerInterface { +public: + /** + * Dump the state of the input classifier. + * This method may be called on any thread (usually by the input manager). + */ + virtual void dump(std::string& dump) = 0; protected: InputClassifierInterface() { } virtual ~InputClassifierInterface() { } @@ -110,6 +121,8 @@ public: virtual void reset() override; virtual void reset(const NotifyDeviceResetArgs& args) override; + virtual void dump(std::string& dump) override; + private: // The events that need to be sent to the HAL. BlockingQueue mEvents; @@ -186,6 +199,8 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + virtual void dump(std::string& dump) override; + private: std::unique_ptr mMotionClassifier = nullptr; // The next stage to pass input events to diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index b3b9e3e101..a7fd9bab95 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -84,6 +84,10 @@ sp InputManager::getReader() { return mReader; } +sp InputManager::getClassifier() { + return mClassifier; +} + sp InputManager::getDispatcher() { return mDispatcher; } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 142ec0c1ea..e632da3f63 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -90,6 +90,7 @@ public: virtual status_t stop(); virtual sp getReader(); + virtual sp getClassifier(); virtual sp getDispatcher(); virtual void setInputWindows(const Vector& handles); -- cgit v1.2.3-59-g8ed1b From f2320abad7787cf57830bf6ace932954c107f176 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 11 Feb 2019 12:37:09 -0800 Subject: Fix use after free in InputClassifier Do not send address of a variable outside of the scope of the variable. Test: presubmit Bug: 124232816 Change-Id: I18b4e27ef333f3a8354bdeba89ea2a912106cdaa --- services/inputflinger/InputClassifier.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 09a004c1bb..3905ed05b0 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -705,13 +705,12 @@ void InputClassifier::notifyKey(const NotifyKeyArgs* args) { } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { + NotifyMotionArgs copyArgs = NotifyMotionArgs(*args); if (mMotionClassifier && isTouchEvent(*args)) { // We only cover touch events, for now. - NotifyMotionArgs newArgs = NotifyMotionArgs(*args); - newArgs.classification = mMotionClassifier->classify(newArgs); - args = &newArgs; + copyArgs.classification = mMotionClassifier->classify(copyArgs); } - mListener->notifyMotion(args); + mListener->notifyMotion(©Args); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { -- cgit v1.2.3-59-g8ed1b From 15b66d1475adfa0d31a9aac8c76b124f6f4dfeda Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 4 Feb 2019 14:27:29 -0800 Subject: Do not crash if InputClassifier HAL crashes If InputClassifier HAL crashes for some reason, we don't have to crash the system. Instead, just check return type and move on. The InputClassifier stage is not critical to input dispatch, and the phone can remain perfectly functional without this stage. Log an error message instead. We are also assuming in InputClassifier that HAL is always present. There are 2 lines of defense here: 1) MotionClassifier always checks the returns from the HAL. If any of the returns are not OK, then MotionClassifier thread exits. This is safe to do always, but a downside of this is that logspam will occur if events are not able to be added to the queue (since the thread that is to be consuming them is no longer running). 2) Register HAL death recipient in InputClassifier. When the HAL death occurs, mMotionClassifier will be set to null, thus preventing further events from going into the queue. This will avoid the logspam from 1). Test: ps -A | grep -i input. Then interact with the phone. Then kill the HAL process, 'killall android.hardware.input.classifier@1.0-service-example'. Then make sure that phone remains functional. Bug: 117935272 Change-Id: I7e8f676d3baa0703198f0731273678c3575bdf60 --- services/inputflinger/InputClassifier.cpp | 110 +++++++++++++++++++++--------- services/inputflinger/InputClassifier.h | 30 ++++++-- 2 files changed, 105 insertions(+), 35 deletions(-) (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 3905ed05b0..b4db338687 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -40,6 +40,7 @@ using android::base::StringPrintf; using android::hardware::hidl_bitfield; using android::hardware::hidl_vec; +using android::hardware::Return; using namespace android::hardware::input; namespace android { @@ -548,23 +549,28 @@ void MotionClassifier::callInputClassifierHal() { ensureHalThread(__func__); while (true) { ClassifierEvent event = mEvents.pop(); + bool halResponseOk = true; switch (event.type) { case ClassifierEventType::MOTION: { NotifyMotionArgs* motionArgs = static_cast(event.args.get()); common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs); - common::V1_0::Classification halClassification = mService->classify(motionEvent); - updateClassification(motionArgs->deviceId, motionArgs->eventTime, - getMotionClassification(halClassification)); + Return response = mService->classify(motionEvent); + halResponseOk = response.isOk(); + if (halResponseOk) { + common::V1_0::Classification halClassification = response; + updateClassification(motionArgs->deviceId, motionArgs->eventTime, + getMotionClassification(halClassification)); + } break; } case ClassifierEventType::DEVICE_RESET: { const int32_t deviceId = *(event.getDeviceId()); - mService->resetDevice(deviceId); + halResponseOk = mService->resetDevice(deviceId).isOk(); setClassification(deviceId, MotionClassification::NONE); break; } case ClassifierEventType::HAL_RESET: { - mService->reset(); + halResponseOk = mService->reset().isOk(); clearClassifications(); break; } @@ -573,6 +579,21 @@ void MotionClassifier::callInputClassifierHal() { return; } } + if (!halResponseOk) { + ALOGE("Error communicating with InputClassifier HAL. " + "Exiting MotionClassifier HAL thread"); + clearClassifications(); + return; + } + } +} + +void MotionClassifier::enqueueEvent(ClassifierEvent&& event) { + bool eventAdded = mEvents.push(std::move(event)); + if (!eventAdded) { + // If the queue is full, suspect the HAL is slow in processing the events. + ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime); + reset(); } } @@ -617,22 +638,12 @@ void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) { } MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) { - if (!mService) { - // If HAL is not present, do nothing - return MotionClassification::NONE; - } if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) { updateLastDownTime(args.deviceId, args.downTime); } ClassifierEvent event(std::make_unique(args)); - bool elementAdded = mEvents.push(std::move(event)); - if (!elementAdded) { - // Queue should not ever overfill. Suspect HAL is slow. - ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime); - reset(); - return MotionClassification::NONE; - } + enqueueEvent(std::move(event)); return getClassification(args.deviceId); } @@ -652,7 +663,7 @@ void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { std::optional eventDeviceId = event.getDeviceId(); return eventDeviceId && (*eventDeviceId == deviceId); }); - mEvents.push(std::make_unique(args)); + enqueueEvent(std::make_unique(args)); } void MotionClassifier::dump(std::string& dump) { @@ -679,20 +690,39 @@ void MotionClassifier::dump(std::string& dump) { } } + // --- InputClassifier --- InputClassifier::InputClassifier(const sp& listener) : mListener(listener) { - if (deepPressEnabled()) { - sp service = - classifier::V1_0::IInputClassifier::getService(); - if (service) { - mMotionClassifier = std::make_unique(service); - } else { - ALOGI("Could not obtain InputClassifier HAL"); - } + // The rest of the initialization is done in onFirstRef, because we need to obtain + // an sp to 'this' in order to register for HAL death notifications +} + +void InputClassifier::onFirstRef() { + std::scoped_lock lock(mLock); + if (!deepPressEnabled()) { + // If feature is not enabled, the InputClassifier will just be in passthrough + // mode, and will forward all events to the next InputListener, unmodified + return; + } + + sp service = + classifier::V1_0::IInputClassifier::getService(); + if (!service) { + // Not really an error, maybe the device does not have this HAL, + // but somehow the feature flag is flipped + ALOGI("Could not obtain InputClassifier HAL"); + return; + } + const bool linked = service->linkToDeath(this, 0 /* cookie */).withDefault(false); + if (!linked) { + ALOGE("Could not link android::InputClassifier to the HAL death"); + return; } -}; + + mMotionClassifier = std::make_unique(service); +} void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through @@ -705,12 +735,17 @@ void InputClassifier::notifyKey(const NotifyKeyArgs* args) { } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { - NotifyMotionArgs copyArgs = NotifyMotionArgs(*args); - if (mMotionClassifier && isTouchEvent(*args)) { - // We only cover touch events, for now. - copyArgs.classification = mMotionClassifier->classify(copyArgs); + std::scoped_lock lock(mLock); + // MotionClassifier is only used for touch events, for now + const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); + if (!sendToMotionClassifier) { + mListener->notifyMotion(args); + return; } - mListener->notifyMotion(©Args); + + NotifyMotionArgs newArgs(*args); + newArgs.classification = mMotionClassifier->classify(newArgs); + mListener->notifyMotion(&newArgs); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { @@ -719,6 +754,7 @@ void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + std::scoped_lock lock(mLock); if (mMotionClassifier) { mMotionClassifier->reset(*args); } @@ -726,7 +762,19 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); } +void InputClassifier::serviceDied(uint64_t /*cookie*/, + const wp& who) { + std::scoped_lock lock(mLock); + ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); + mMotionClassifier = nullptr; + sp service = who.promote(); + if (service) { + service->unlinkToDeath(this); + } +} + void InputClassifier::dump(std::string& dump) { + std::scoped_lock lock(mLock); dump += "Input Classifier State:\n"; dump += INDENT1 "Motion Classifier:\n"; diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index b97357dfd7..0b1483fdd5 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -69,7 +69,13 @@ public: * provide a MotionClassification for the current gesture. */ virtual MotionClassification classify(const NotifyMotionArgs& args) = 0; + /** + * Reset all internal HAL state. + */ virtual void reset() = 0; + /** + * Reset HAL state for a specific device. + */ virtual void reset(const NotifyDeviceResetArgs& args) = 0; /** @@ -107,6 +113,9 @@ protected: */ class MotionClassifier final : public MotionClassifierInterface { public: + /** + * The provided pointer to the service cannot be null. + */ MotionClassifier(sp service); ~MotionClassifier(); /** @@ -127,6 +136,10 @@ public: private: // The events that need to be sent to the HAL. BlockingQueue mEvents; + /** + * Add an event to the queue mEvents. + */ + void enqueueEvent(ClassifierEvent&& event); /** * Thread that will communicate with InputClassifier HAL. * This should be the only thread that communicates with InputClassifier HAL, @@ -143,9 +156,9 @@ private: */ void callInputClassifierHal(); /** - * Access to the InputClassifier HAL + * Access to the InputClassifier HAL. Can always be safely dereferenced. */ - sp mService; + const sp mService; std::mutex mLock; /** * Per-device input classifications. Should only be accessed using the @@ -184,15 +197,19 @@ private: void requestExit(); }; + /** * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. */ -class InputClassifier : public InputClassifierInterface { +class InputClassifier : public InputClassifierInterface, + public android::hardware::hidl_death_recipient { public: explicit InputClassifier(const sp& listener); + // Some of the constructor logic is finished in onFirstRef + virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -200,10 +217,15 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + virtual void serviceDied(uint64_t cookie, + const wp& who) override; + virtual void dump(std::string& dump) override; private: - std::unique_ptr mMotionClassifier = nullptr; + // Protect access to mMotionClassifier, since it may become null via a hidl callback + std::mutex mLock; + std::unique_ptr mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to sp mListener; }; -- cgit v1.2.3-59-g8ed1b From 4bdbb6ae90238834229ac20184a1c8c7bcb71ce0 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 11 Apr 2019 09:42:09 -0700 Subject: Initialize MotionClassifier from a separate thread We are currently calling getService directly from the thread that calls InputManager constructor. That means, the function call directly affects the start-up time of the system. But InputClassifier HAL is not mission-critical, and the system can function perfectly fine without it. Moreover, we are already allowing for mMotionClassifier to be null in our code. In this change, mMotionClassifier will become non-null right away, but may not be initialized when it is first created. The initialization will happen in a separate thread. Once initialized, MotionClassifier may become null inside InputClassifier if the latter receives a HAL death callback. To account for the possibility of the MotionClassifier not being valid right away, we are allowing mService variable in the MotionClassifier to be nullable. The contract for mService is as follows: mService is null by default. If MotionClassifier initializes successfully (may take ~ 100 ms), then mService will become non-null. Once non-null, it stays non-null. This allows MotionClassifier's separate thread to not have to check the nullness of mService. Other threads, however, must assume that mService can be null. They are checking the value with a lock held. If MotionClassifier init fails, it will also call the deathRecipient's serviceDied call, to signal that it is no longer useful. This should prompt the owner of MotionClassifier to dispose it. Bug: 130184032 Test: atest google/perf/boottime/boottime-test Test: atest libinput_tests inputflinger_tests Change-Id: Id4f9c316ef6725d96abebc55a96079946647eb39 --- services/inputflinger/InputClassifier.cpp | 96 ++++++++++++++++------ services/inputflinger/InputClassifier.h | 42 ++++++++-- .../inputflinger/tests/InputClassifier_test.cpp | 26 ++---- 3 files changed, 110 insertions(+), 54 deletions(-) (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index b4db338687..4a6efa67a7 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -507,19 +507,53 @@ std::optional ClassifierEvent::getDeviceId() const { // --- MotionClassifier --- -MotionClassifier::MotionClassifier( - sp service) : - mEvents(MAX_EVENTS), mService(service) { +MotionClassifier::MotionClassifier(sp deathRecipient) : + mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) { mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this); #if defined(__linux__) // Set the thread name for debugging pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); #endif +} + +/** + * This function may block for some time to initialize the HAL, so it should only be called + * from the "InputClassifier HAL" thread. + */ +bool MotionClassifier::init() { + ensureHalThread(__func__); + sp service = + classifier::V1_0::IInputClassifier::getService(); + if (!service) { + // Not really an error, maybe the device does not have this HAL, + // but somehow the feature flag is flipped + ALOGI("Could not obtain InputClassifier HAL"); + return false; + } + + sp recipient = mDeathRecipient.promote(); + if (recipient != nullptr) { + const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false); + if (!linked) { + ALOGE("Could not link MotionClassifier to the HAL death"); + return false; + } + } + // Under normal operation, we do not need to reset the HAL here. But in the case where system // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already // have received events in the past. That means, that HAL could be in an inconsistent state // once it receives events from the newly created MotionClassifier. mEvents.push(ClassifierEvent::createHalResetEvent()); + + { + std::scoped_lock lock(mLock); + if (mService) { + ALOGE("MotionClassifier::%s should only be called once", __func__); + } + mService = service; + } + return true; } MotionClassifier::~MotionClassifier() { @@ -530,7 +564,7 @@ MotionClassifier::~MotionClassifier() { void MotionClassifier::ensureHalThread(const char* function) { if (DEBUG) { if (std::this_thread::get_id() != mHalThread.get_id()) { - ALOGE("Function %s should only be called from InputClassifier thread", function); + LOG_FATAL("Function %s should only be called from InputClassifier thread", function); } } } @@ -547,6 +581,21 @@ void MotionClassifier::ensureHalThread(const char* function) { */ void MotionClassifier::callInputClassifierHal() { ensureHalThread(__func__); + const bool initialized = init(); + if (!initialized) { + // MotionClassifier no longer useful. + // Deliver death notification from a separate thread + // because ~MotionClassifier may be invoked, which calls mHalThread.join() + std::thread([deathRecipient = mDeathRecipient](){ + sp recipient = deathRecipient.promote(); + if (recipient != nullptr) { + recipient->serviceDied(0 /*cookie*/, nullptr); + } + }).detach(); + return; + } + // From this point on, mService is guaranteed to be non-null. + while (true) { ClassifierEvent event = mEvents.pop(); bool halResponseOk = true; @@ -666,10 +715,19 @@ void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { enqueueEvent(std::make_unique(args)); } +const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) { + if (!mService) { + return "null"; + } + if (mService->ping().isOk()) { + return "running"; + } + return "not responding"; +} + void MotionClassifier::dump(std::string& dump) { std::scoped_lock lock(mLock); - std::string serviceStatus = mService->ping().isOk() ? "running" : " not responding"; - dump += StringPrintf(INDENT2 "mService status: %s\n", serviceStatus.c_str()); + dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus()); dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n", mEvents.size(), MAX_EVENTS); dump += INDENT2 "mClassifications, mLastDownTimes:\n"; @@ -700,28 +758,14 @@ InputClassifier::InputClassifier(const sp& listener) : } void InputClassifier::onFirstRef() { - std::scoped_lock lock(mLock); if (!deepPressEnabled()) { - // If feature is not enabled, the InputClassifier will just be in passthrough - // mode, and will forward all events to the next InputListener, unmodified - return; - } - - sp service = - classifier::V1_0::IInputClassifier::getService(); - if (!service) { - // Not really an error, maybe the device does not have this HAL, - // but somehow the feature flag is flipped - ALOGI("Could not obtain InputClassifier HAL"); - return; - } - const bool linked = service->linkToDeath(this, 0 /* cookie */).withDefault(false); - if (!linked) { - ALOGE("Could not link android::InputClassifier to the HAL death"); + // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. + // When MotionClassifier is null, InputClassifier will forward all events + // to the next InputListener, unmodified. return; } - - mMotionClassifier = std::make_unique(service); + std::scoped_lock lock(mLock); + mMotionClassifier = std::make_unique(this); } void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { @@ -786,4 +830,4 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; } -} // namespace android \ No newline at end of file +} // namespace android diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 0b1483fdd5..47e20dbf75 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -114,15 +114,22 @@ protected: class MotionClassifier final : public MotionClassifierInterface { public: /** - * The provided pointer to the service cannot be null. + * The deathRecipient will be subscribed to the HAL death. If the death recipient + * owns MotionClassifier and receives HAL death, it should delete its copy of it. + * The callback serviceDied will also be sent if the MotionClassifier itself fails + * to initialize. If the MotionClassifier fails to initialize, it is not useful, and + * should be deleted. + * If no death recipient is supplied, then the registration step will be skipped, so there will + * be no listeners registered for the HAL death. This is useful for testing + * MotionClassifier in isolation. */ - MotionClassifier(sp service); + explicit MotionClassifier(sp deathRecipient = nullptr); ~MotionClassifier(); + /** * Classifies events asynchronously; that is, it doesn't block events on a classification, - * but instead sends them over to the classifier HAL - * and after a classification is determined, - * it then marks the next event it sees in the stream with it. + * but instead sends them over to the classifier HAL and after a classification is + * determined, it then marks the next event it sees in the stream with it. * * Therefore, it is acceptable to have the classifications be delayed by 1-2 events * in a particular gesture. @@ -134,6 +141,16 @@ public: virtual void dump(std::string& dump) override; private: + /** + * Initialize MotionClassifier. + * Return true if initializaion is successful. + */ + bool init(); + /** + * Entity that will be notified of the HAL death (most likely InputClassifier). + */ + wp mDeathRecipient; + // The events that need to be sent to the HAL. BlockingQueue mEvents; /** @@ -148,7 +165,7 @@ private: std::thread mHalThread; /** * Print an error message if the caller is not on the InputClassifier thread. - * Caller must supply the name of the calling function as __function__ + * Caller must supply the name of the calling function as __func__ */ void ensureHalThread(const char* function); /** @@ -156,9 +173,14 @@ private: */ void callInputClassifierHal(); /** - * Access to the InputClassifier HAL. Can always be safely dereferenced. + * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. + * When init() successfully completes, mService is guaranteed to remain non-null and to not + * change its value until MotionClassifier is destroyed. + * This variable is *not* guarded by mLock in the InputClassifier thread, because + * that thread knows exactly when this variable is initialized. + * When accessed in any other thread, mService is checked for nullness with a lock. */ - const sp mService; + sp mService; std::mutex mLock; /** * Per-device input classifications. Should only be accessed using the @@ -195,6 +217,10 @@ private: * Useful for tests to ensure proper cleanup. */ void requestExit(); + /** + * Return string status of mService + */ + const char* getServiceStatus() REQUIRES(mLock); }; diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 16510577bf..7cc17a2215 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -136,11 +136,7 @@ protected: std::unique_ptr mMotionClassifier; virtual void SetUp() override { - sp service = - classifier::V1_0::IInputClassifier::getService(); - if (service) { - mMotionClassifier = std::make_unique(service); - } + mMotionClassifier = std::make_unique(); } }; @@ -165,9 +161,7 @@ TEST_F(MotionClassifierTest, Classify_NoVideoFrames) { // We are not checking the return value, because we can't be making assumptions // about the HAL operation, since it will be highly hardware-dependent - if (mMotionClassifier) { - ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs)); - } + ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs)); } /** @@ -183,9 +177,7 @@ TEST_F(MotionClassifierTest, Classify_OneVideoFrame) { // We are not checking the return value, because we can't be making assumptions // about the HAL operation, since it will be highly hardware-dependent - if (mMotionClassifier) { - ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs)); - } + ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs)); } /** @@ -206,18 +198,14 @@ TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) { // We are not checking the return value, because we can't be making assumptions // about the HAL operation, since it will be highly hardware-dependent - if (mMotionClassifier) { - ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs)); - } + ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs)); } /** * Make sure MotionClassifier does not crash when it is reset. */ TEST_F(MotionClassifierTest, Reset_DoesNotCrash) { - if (mMotionClassifier) { - ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset()); - } + ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset()); } /** @@ -225,9 +213,7 @@ TEST_F(MotionClassifierTest, Reset_DoesNotCrash) { */ TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) { NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/); - if (mMotionClassifier) { - ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args)); - } + ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args)); } } // namespace android -- cgit v1.2.3-59-g8ed1b From a47a4d4f1032e2e013d1051eb34b966a9a92faae Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 6 May 2019 17:14:11 -0700 Subject: Use bitset api in InputClassifier We are currently doing some custom manipulations with bits in InputClassifier. But the manipulations are making incorrect assumptions about the behaviour of the bitset apis. To maximize consistency, extend the use of bitset apis to InputClassifier. This allows the same apis to be used in the HAL in order to read the axis values. Bug: 131927078 Test: atest inputflinger_tests Change-Id: Icab062c695ca07d5cc404082d3de7b334cbc0e19 --- services/inputflinger/Android.bp | 1 + services/inputflinger/InputClassifier.cpp | 371 +------------------- services/inputflinger/InputClassifierConverter.cpp | 384 +++++++++++++++++++++ services/inputflinger/InputClassifierConverter.h | 34 ++ services/inputflinger/tests/Android.bp | 1 + .../tests/InputClassifierConverter_test.cpp | 80 +++++ 6 files changed, 503 insertions(+), 368 deletions(-) create mode 100644 services/inputflinger/InputClassifierConverter.cpp create mode 100644 services/inputflinger/InputClassifierConverter.h create mode 100644 services/inputflinger/tests/InputClassifierConverter_test.cpp (limited to 'services/inputflinger/InputClassifier.cpp') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 63e759c0f8..8dd4d1df63 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -29,6 +29,7 @@ cc_library_shared { srcs: [ "InputClassifier.cpp", + "InputClassifierConverter.cpp", "InputDispatcher.cpp", "InputManager.cpp", ], diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 4a6efa67a7..ef1a2247e9 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "InputClassifier" #include "InputClassifier.h" +#include "InputClassifierConverter.h" #include #include @@ -64,373 +65,6 @@ static V getValueForKey(const std::unordered_map& map, K key, V defaultVal return it->second; } -static common::V1_0::Source getSource(uint32_t source) { - static_assert(static_cast(AINPUT_SOURCE_UNKNOWN) == - common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch"); - static_assert(static_cast(AINPUT_SOURCE_KEYBOARD) == - common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch"); - static_assert(static_cast(AINPUT_SOURCE_DPAD) == - common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch"); - static_assert(static_cast(AINPUT_SOURCE_GAMEPAD) == - common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch"); - static_assert(static_cast(AINPUT_SOURCE_TOUCHSCREEN) == - common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch"); - static_assert(static_cast(AINPUT_SOURCE_MOUSE) == - common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch"); - static_assert(static_cast(AINPUT_SOURCE_STYLUS) == - common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch"); - static_assert(static_cast(AINPUT_SOURCE_BLUETOOTH_STYLUS) == - common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch"); - static_assert(static_cast(AINPUT_SOURCE_TRACKBALL) == - common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch"); - static_assert(static_cast(AINPUT_SOURCE_MOUSE_RELATIVE) == - common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch"); - static_assert(static_cast(AINPUT_SOURCE_TOUCHPAD) == - common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch"); - static_assert(static_cast(AINPUT_SOURCE_TOUCH_NAVIGATION) == - common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch"); - static_assert(static_cast(AINPUT_SOURCE_JOYSTICK) == - common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch"); - static_assert(static_cast(AINPUT_SOURCE_ROTARY_ENCODER) == - common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch"); - static_assert(static_cast(AINPUT_SOURCE_ANY) == - common::V1_0::Source::ANY, "SOURCE_ANY mismatch"); - return static_cast(source); -} - -static common::V1_0::Action getAction(int32_t actionMasked) { - static_assert(static_cast(AMOTION_EVENT_ACTION_DOWN) == - common::V1_0::Action::DOWN, "ACTION_DOWN mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_UP) == - common::V1_0::Action::UP, "ACTION_UP mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_MOVE) == - common::V1_0::Action::MOVE, "ACTION_MOVE mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_CANCEL) == - common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_OUTSIDE) == - common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_POINTER_DOWN) == - common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_POINTER_UP) == - common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch"); - static_assert(static_cast( AMOTION_EVENT_ACTION_HOVER_MOVE) == - common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_SCROLL) == - common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_HOVER_ENTER) == - common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_HOVER_EXIT) == - common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_BUTTON_PRESS) == - common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch"); - static_assert(static_cast(AMOTION_EVENT_ACTION_BUTTON_RELEASE) == - common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch"); - return static_cast(actionMasked); -} - -static common::V1_0::Button getActionButton(int32_t actionButton) { - static_assert(static_cast(0) == - common::V1_0::Button::NONE, "BUTTON_NONE mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_PRIMARY) == - common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_SECONDARY) == - common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_TERTIARY) == - common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_BACK) == - common::V1_0::Button::BACK, "BUTTON_BACK mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_FORWARD) == - common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) == - common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch"); - static_assert(static_cast(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) == - common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch"); - return static_cast(actionButton); -} - -static hidl_bitfield getFlags(int32_t flags) { - static_assert(static_cast(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) == - common::V1_0::Flag::WINDOW_IS_OBSCURED); - static_assert(static_cast(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) == - common::V1_0::Flag::IS_GENERATED_GESTURE); - static_assert(static_cast(AMOTION_EVENT_FLAG_TAINTED) == - common::V1_0::Flag::TAINTED); - return static_cast>(flags); -} - -static hidl_bitfield getPolicyFlags(int32_t flags) { - static_assert(static_cast(POLICY_FLAG_WAKE) == - common::V1_0::PolicyFlag::WAKE); - static_assert(static_cast(POLICY_FLAG_VIRTUAL) == - common::V1_0::PolicyFlag::VIRTUAL); - static_assert(static_cast(POLICY_FLAG_FUNCTION) == - common::V1_0::PolicyFlag::FUNCTION); - static_assert(static_cast(POLICY_FLAG_GESTURE) == - common::V1_0::PolicyFlag::GESTURE); - static_assert(static_cast(POLICY_FLAG_INJECTED) == - common::V1_0::PolicyFlag::INJECTED); - static_assert(static_cast(POLICY_FLAG_TRUSTED) == - common::V1_0::PolicyFlag::TRUSTED); - static_assert(static_cast(POLICY_FLAG_FILTERED) == - common::V1_0::PolicyFlag::FILTERED); - static_assert(static_cast(POLICY_FLAG_DISABLE_KEY_REPEAT) == - common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT); - static_assert(static_cast(POLICY_FLAG_INTERACTIVE) == - common::V1_0::PolicyFlag::INTERACTIVE); - static_assert(static_cast(POLICY_FLAG_PASS_TO_USER) == - common::V1_0::PolicyFlag::PASS_TO_USER); - return static_cast>(flags); -} - -static hidl_bitfield getEdgeFlags(int32_t flags) { - static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_NONE) == - common::V1_0::EdgeFlag::NONE); - static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_TOP) == - common::V1_0::EdgeFlag::TOP); - static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_BOTTOM) == - common::V1_0::EdgeFlag::BOTTOM); - static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_LEFT) == - common::V1_0::EdgeFlag::LEFT); - static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_RIGHT) == - common::V1_0::EdgeFlag::RIGHT); - return static_cast>(flags); -} - -static hidl_bitfield getMetastate(int32_t state) { - static_assert(static_cast(AMETA_NONE) == - common::V1_0::Meta::NONE); - static_assert(static_cast(AMETA_ALT_ON) == - common::V1_0::Meta::ALT_ON); - static_assert(static_cast(AMETA_ALT_LEFT_ON) == - common::V1_0::Meta::ALT_LEFT_ON); - static_assert(static_cast(AMETA_ALT_RIGHT_ON) == - common::V1_0::Meta::ALT_RIGHT_ON); - static_assert(static_cast(AMETA_SHIFT_ON) == - common::V1_0::Meta::SHIFT_ON); - static_assert(static_cast(AMETA_SHIFT_LEFT_ON) == - common::V1_0::Meta::SHIFT_LEFT_ON); - static_assert(static_cast(AMETA_SHIFT_RIGHT_ON) == - common::V1_0::Meta::SHIFT_RIGHT_ON); - static_assert(static_cast(AMETA_SYM_ON) == - common::V1_0::Meta::SYM_ON); - static_assert(static_cast(AMETA_FUNCTION_ON) == - common::V1_0::Meta::FUNCTION_ON); - static_assert(static_cast(AMETA_CTRL_ON) == - common::V1_0::Meta::CTRL_ON); - static_assert(static_cast(AMETA_CTRL_LEFT_ON) == - common::V1_0::Meta::CTRL_LEFT_ON); - static_assert(static_cast(AMETA_CTRL_RIGHT_ON) == - common::V1_0::Meta::CTRL_RIGHT_ON); - static_assert(static_cast(AMETA_META_ON) == - common::V1_0::Meta::META_ON); - static_assert(static_cast(AMETA_META_LEFT_ON) == - common::V1_0::Meta::META_LEFT_ON); - static_assert(static_cast(AMETA_META_RIGHT_ON) == - common::V1_0::Meta::META_RIGHT_ON); - static_assert(static_cast(AMETA_CAPS_LOCK_ON) == - common::V1_0::Meta::CAPS_LOCK_ON); - static_assert(static_cast(AMETA_NUM_LOCK_ON) == - common::V1_0::Meta::NUM_LOCK_ON); - static_assert(static_cast(AMETA_SCROLL_LOCK_ON) == - common::V1_0::Meta::SCROLL_LOCK_ON); - return static_cast>(state); -} - -static hidl_bitfield getButtonState(int32_t buttonState) { - // No need for static_assert here. - // The button values have already been asserted in getActionButton(..) above - return static_cast>(buttonState); -} - -static common::V1_0::ToolType getToolType(int32_t toolType) { - static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) == - common::V1_0::ToolType::UNKNOWN); - static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_FINGER) == - common::V1_0::ToolType::FINGER); - static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_STYLUS) == - common::V1_0::ToolType::STYLUS); - static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_MOUSE) == - common::V1_0::ToolType::MOUSE); - static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_ERASER) == - common::V1_0::ToolType::ERASER); - return static_cast(toolType); -} - -static common::V1_0::Axis getAxis(uint64_t axis) { - static_assert(static_cast(AMOTION_EVENT_AXIS_X) == - common::V1_0::Axis::X); - static_assert(static_cast(AMOTION_EVENT_AXIS_Y) == - common::V1_0::Axis::Y); - static_assert(static_cast(AMOTION_EVENT_AXIS_PRESSURE) == - common::V1_0::Axis::PRESSURE); - static_assert(static_cast(AMOTION_EVENT_AXIS_SIZE) == - common::V1_0::Axis::SIZE); - static_assert(static_cast(AMOTION_EVENT_AXIS_TOUCH_MAJOR) == - common::V1_0::Axis::TOUCH_MAJOR); - static_assert(static_cast(AMOTION_EVENT_AXIS_TOUCH_MINOR) == - common::V1_0::Axis::TOUCH_MINOR); - static_assert(static_cast(AMOTION_EVENT_AXIS_TOOL_MAJOR) == - common::V1_0::Axis::TOOL_MAJOR); - static_assert(static_cast(AMOTION_EVENT_AXIS_TOOL_MINOR) == - common::V1_0::Axis::TOOL_MINOR); - static_assert(static_cast(AMOTION_EVENT_AXIS_ORIENTATION) == - common::V1_0::Axis::ORIENTATION); - static_assert(static_cast(AMOTION_EVENT_AXIS_VSCROLL) == - common::V1_0::Axis::VSCROLL); - static_assert(static_cast(AMOTION_EVENT_AXIS_HSCROLL) == - common::V1_0::Axis::HSCROLL); - static_assert(static_cast(AMOTION_EVENT_AXIS_Z) == - common::V1_0::Axis::Z); - static_assert(static_cast(AMOTION_EVENT_AXIS_RX) == - common::V1_0::Axis::RX); - static_assert(static_cast(AMOTION_EVENT_AXIS_RY) == - common::V1_0::Axis::RY); - static_assert(static_cast(AMOTION_EVENT_AXIS_RZ) == - common::V1_0::Axis::RZ); - static_assert(static_cast(AMOTION_EVENT_AXIS_HAT_X) == - common::V1_0::Axis::HAT_X); - static_assert(static_cast(AMOTION_EVENT_AXIS_HAT_Y) == - common::V1_0::Axis::HAT_Y); - static_assert(static_cast(AMOTION_EVENT_AXIS_LTRIGGER) == - common::V1_0::Axis::LTRIGGER); - static_assert(static_cast(AMOTION_EVENT_AXIS_RTRIGGER) == - common::V1_0::Axis::RTRIGGER); - static_assert(static_cast(AMOTION_EVENT_AXIS_THROTTLE) == - common::V1_0::Axis::THROTTLE); - static_assert(static_cast(AMOTION_EVENT_AXIS_RUDDER) == - common::V1_0::Axis::RUDDER); - static_assert(static_cast(AMOTION_EVENT_AXIS_WHEEL) == - common::V1_0::Axis::WHEEL); - static_assert(static_cast(AMOTION_EVENT_AXIS_GAS) == - common::V1_0::Axis::GAS); - static_assert(static_cast(AMOTION_EVENT_AXIS_BRAKE) == - common::V1_0::Axis::BRAKE); - static_assert(static_cast(AMOTION_EVENT_AXIS_DISTANCE) == - common::V1_0::Axis::DISTANCE); - static_assert(static_cast(AMOTION_EVENT_AXIS_TILT) == - common::V1_0::Axis::TILT); - static_assert(static_cast(AMOTION_EVENT_AXIS_SCROLL) == - common::V1_0::Axis::SCROLL); - static_assert(static_cast(AMOTION_EVENT_AXIS_RELATIVE_X) == - common::V1_0::Axis::RELATIVE_X); - static_assert(static_cast(AMOTION_EVENT_AXIS_RELATIVE_Y) == - common::V1_0::Axis::RELATIVE_Y); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_1) == - common::V1_0::Axis::GENERIC_1); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_2) == - common::V1_0::Axis::GENERIC_2); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_3) == - common::V1_0::Axis::GENERIC_3); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_4) == - common::V1_0::Axis::GENERIC_4); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_5) == - common::V1_0::Axis::GENERIC_5); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_6) == - common::V1_0::Axis::GENERIC_6); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_7) == - common::V1_0::Axis::GENERIC_7); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_8) == - common::V1_0::Axis::GENERIC_8); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_9) == - common::V1_0::Axis::GENERIC_9); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_10) == - common::V1_0::Axis::GENERIC_10); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_11) == - common::V1_0::Axis::GENERIC_11); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_12) == - common::V1_0::Axis::GENERIC_12); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_13) == - common::V1_0::Axis::GENERIC_13); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_14) == - common::V1_0::Axis::GENERIC_14); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_15) == - common::V1_0::Axis::GENERIC_15); - static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_16) == - common::V1_0::Axis::GENERIC_16); - return static_cast(axis); -} - -static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) { - common::V1_0::VideoFrame out; - out.width = frame.getWidth(); - out.height = frame.getHeight(); - out.data = frame.getData(); - struct timeval timestamp = frame.getTimestamp(); - out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) + - microseconds_to_nanoseconds(timestamp.tv_usec); - return out; -} - -static std::vector convertVideoFrames( - const std::vector& frames) { - std::vector out; - for (const TouchVideoFrame& frame : frames) { - out.push_back(getHalVideoFrame(frame)); - } - return out; -} - -static uint8_t getActionIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> - AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - -static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, - std::vector* outPointerProperties, - std::vector* outPointerCoords) { - outPointerProperties->reserve(args.pointerCount); - outPointerCoords->reserve(args.pointerCount); - for (size_t i = 0; i < args.pointerCount; i++) { - common::V1_0::PointerProperties properties; - properties.id = args.pointerProperties[i].id; - properties.toolType = getToolType(args.pointerProperties[i].toolType); - outPointerProperties->push_back(properties); - - common::V1_0::PointerCoords coords; - BitSet64 bits (args.pointerCoords[i].bits); - std::vector values; - size_t index = 0; - while (!bits.isEmpty()) { - uint32_t axis = bits.clearFirstMarkedBit(); - coords.bits |= 1 << static_cast(getAxis(axis)); - float value = args.pointerCoords[i].values[index++]; - values.push_back(value); - } - coords.values = values; - outPointerCoords->push_back(coords); - } -} - -static common::V1_0::MotionEvent getMotionEvent(const NotifyMotionArgs& args) { - common::V1_0::MotionEvent event; - event.deviceId = args.deviceId; - event.source = getSource(args.source); - event.displayId = args.displayId; - event.downTime = args.downTime; - event.eventTime = args.eventTime; - event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); - event.actionIndex = getActionIndex(args.action); - event.actionButton = getActionButton(args.actionButton); - event.flags = getFlags(args.flags); - event.policyFlags = getPolicyFlags(args.policyFlags); - event.edgeFlags = getEdgeFlags(args.edgeFlags); - event.metaState = getMetastate(args.metaState); - event.buttonState = getButtonState(args.buttonState); - event.xPrecision = args.xPrecision; - event.yPrecision = args.yPrecision; - - std::vector pointerProperties; - std::vector pointerCoords; - getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords); - event.pointerProperties = pointerProperties; - event.pointerCoords = pointerCoords; - - event.deviceTimestamp = args.deviceTimestamp; - event.frames = convertVideoFrames(args.videoFrames); - - return event; -} - static MotionClassification getMotionClassification(common::V1_0::Classification classification) { static_assert(MotionClassification::NONE == static_cast(common::V1_0::Classification::NONE)); @@ -602,7 +236,8 @@ void MotionClassifier::callInputClassifierHal() { switch (event.type) { case ClassifierEventType::MOTION: { NotifyMotionArgs* motionArgs = static_cast(event.args.get()); - common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs); + common::V1_0::MotionEvent motionEvent = + notifyMotionArgsToHalMotionEvent(*motionArgs); Return response = mService->classify(motionEvent); halResponseOk = response.isOk(); if (halResponseOk) { diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp new file mode 100644 index 0000000000..f82c8ef1fd --- /dev/null +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -0,0 +1,384 @@ +/* + * 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. + */ + +#include "InputClassifierConverter.h" + +using android::hardware::hidl_bitfield; +using namespace android::hardware::input; + +namespace android { + +static common::V1_0::Source getSource(uint32_t source) { + static_assert(static_cast(AINPUT_SOURCE_UNKNOWN) == + common::V1_0::Source::UNKNOWN, "SOURCE_UNKNOWN mismatch"); + static_assert(static_cast(AINPUT_SOURCE_KEYBOARD) == + common::V1_0::Source::KEYBOARD, "SOURCE_KEYBOARD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_DPAD) == + common::V1_0::Source::DPAD, "SOURCE_DPAD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_GAMEPAD) == + common::V1_0::Source::GAMEPAD, "SOURCE_GAMEPAD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TOUCHSCREEN) == + common::V1_0::Source::TOUCHSCREEN, "SOURCE_TOUCHSCREEN mismatch"); + static_assert(static_cast(AINPUT_SOURCE_MOUSE) == + common::V1_0::Source::MOUSE, "SOURCE_MOUSE mismatch"); + static_assert(static_cast(AINPUT_SOURCE_STYLUS) == + common::V1_0::Source::STYLUS, "SOURCE_STYLUS mismatch"); + static_assert(static_cast(AINPUT_SOURCE_BLUETOOTH_STYLUS) == + common::V1_0::Source::BLUETOOTH_STYLUS, "SOURCE_BLUETOOTH_STYLUS mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TRACKBALL) == + common::V1_0::Source::TRACKBALL, "SOURCE_TRACKBALL mismatch"); + static_assert(static_cast(AINPUT_SOURCE_MOUSE_RELATIVE) == + common::V1_0::Source::MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TOUCHPAD) == + common::V1_0::Source::TOUCHPAD, "SOURCE_TOUCHPAD mismatch"); + static_assert(static_cast(AINPUT_SOURCE_TOUCH_NAVIGATION) == + common::V1_0::Source::TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION mismatch"); + static_assert(static_cast(AINPUT_SOURCE_JOYSTICK) == + common::V1_0::Source::JOYSTICK, "SOURCE_JOYSTICK mismatch"); + static_assert(static_cast(AINPUT_SOURCE_ROTARY_ENCODER) == + common::V1_0::Source::ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER mismatch"); + static_assert(static_cast(AINPUT_SOURCE_ANY) == + common::V1_0::Source::ANY, "SOURCE_ANY mismatch"); + return static_cast(source); +} + +static common::V1_0::Action getAction(int32_t actionMasked) { + static_assert(static_cast(AMOTION_EVENT_ACTION_DOWN) == + common::V1_0::Action::DOWN, "ACTION_DOWN mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_UP) == + common::V1_0::Action::UP, "ACTION_UP mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_MOVE) == + common::V1_0::Action::MOVE, "ACTION_MOVE mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_CANCEL) == + common::V1_0::Action::CANCEL, "ACTION_CANCEL mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_OUTSIDE) == + common::V1_0::Action::OUTSIDE, "ACTION_OUTSIDE mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_POINTER_DOWN) == + common::V1_0::Action::POINTER_DOWN, "ACTION_POINTER_DOWN mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_POINTER_UP) == + common::V1_0::Action::POINTER_UP, "ACTION_POINTER_UP mismatch"); + static_assert(static_cast( AMOTION_EVENT_ACTION_HOVER_MOVE) == + common::V1_0::Action::HOVER_MOVE, "ACTION_HOVER_MOVE mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_SCROLL) == + common::V1_0::Action::SCROLL, "ACTION_SCROLL mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_HOVER_ENTER) == + common::V1_0::Action::HOVER_ENTER, "ACTION_HOVER_ENTER mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_HOVER_EXIT) == + common::V1_0::Action::HOVER_EXIT, "ACTION_HOVER_EXIT mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_BUTTON_PRESS) == + common::V1_0::Action::BUTTON_PRESS, "ACTION_BUTTON_PRESS mismatch"); + static_assert(static_cast(AMOTION_EVENT_ACTION_BUTTON_RELEASE) == + common::V1_0::Action::BUTTON_RELEASE, "ACTION_BUTTON_RELEASE mismatch"); + return static_cast(actionMasked); +} + +static common::V1_0::Button getActionButton(int32_t actionButton) { + static_assert(static_cast(0) == + common::V1_0::Button::NONE, "BUTTON_NONE mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_PRIMARY) == + common::V1_0::Button::PRIMARY, "BUTTON_PRIMARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_SECONDARY) == + common::V1_0::Button::SECONDARY, "BUTTON_SECONDARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_TERTIARY) == + common::V1_0::Button::TERTIARY, "BUTTON_TERTIARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_BACK) == + common::V1_0::Button::BACK, "BUTTON_BACK mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_FORWARD) == + common::V1_0::Button::FORWARD, "BUTTON_FORWARD mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) == + common::V1_0::Button::STYLUS_PRIMARY, "BUTTON_STYLUS_PRIMARY mismatch"); + static_assert(static_cast(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY) == + common::V1_0::Button::STYLUS_SECONDARY, "BUTTON_STYLUS_SECONDARY mismatch"); + return static_cast(actionButton); +} + +static hidl_bitfield getFlags(int32_t flags) { + static_assert(static_cast(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED) == + common::V1_0::Flag::WINDOW_IS_OBSCURED); + static_assert(static_cast(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE) == + common::V1_0::Flag::IS_GENERATED_GESTURE); + static_assert(static_cast(AMOTION_EVENT_FLAG_TAINTED) == + common::V1_0::Flag::TAINTED); + return static_cast>(flags); +} + +static hidl_bitfield getPolicyFlags(int32_t flags) { + static_assert(static_cast(POLICY_FLAG_WAKE) == + common::V1_0::PolicyFlag::WAKE); + static_assert(static_cast(POLICY_FLAG_VIRTUAL) == + common::V1_0::PolicyFlag::VIRTUAL); + static_assert(static_cast(POLICY_FLAG_FUNCTION) == + common::V1_0::PolicyFlag::FUNCTION); + static_assert(static_cast(POLICY_FLAG_GESTURE) == + common::V1_0::PolicyFlag::GESTURE); + static_assert(static_cast(POLICY_FLAG_INJECTED) == + common::V1_0::PolicyFlag::INJECTED); + static_assert(static_cast(POLICY_FLAG_TRUSTED) == + common::V1_0::PolicyFlag::TRUSTED); + static_assert(static_cast(POLICY_FLAG_FILTERED) == + common::V1_0::PolicyFlag::FILTERED); + static_assert(static_cast(POLICY_FLAG_DISABLE_KEY_REPEAT) == + common::V1_0::PolicyFlag::DISABLE_KEY_REPEAT); + static_assert(static_cast(POLICY_FLAG_INTERACTIVE) == + common::V1_0::PolicyFlag::INTERACTIVE); + static_assert(static_cast(POLICY_FLAG_PASS_TO_USER) == + common::V1_0::PolicyFlag::PASS_TO_USER); + return static_cast>(flags); +} + +static hidl_bitfield getEdgeFlags(int32_t flags) { + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_NONE) == + common::V1_0::EdgeFlag::NONE); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_TOP) == + common::V1_0::EdgeFlag::TOP); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_BOTTOM) == + common::V1_0::EdgeFlag::BOTTOM); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_LEFT) == + common::V1_0::EdgeFlag::LEFT); + static_assert(static_cast(AMOTION_EVENT_EDGE_FLAG_RIGHT) == + common::V1_0::EdgeFlag::RIGHT); + return static_cast>(flags); +} + +static hidl_bitfield getMetastate(int32_t state) { + static_assert(static_cast(AMETA_NONE) == + common::V1_0::Meta::NONE); + static_assert(static_cast(AMETA_ALT_ON) == + common::V1_0::Meta::ALT_ON); + static_assert(static_cast(AMETA_ALT_LEFT_ON) == + common::V1_0::Meta::ALT_LEFT_ON); + static_assert(static_cast(AMETA_ALT_RIGHT_ON) == + common::V1_0::Meta::ALT_RIGHT_ON); + static_assert(static_cast(AMETA_SHIFT_ON) == + common::V1_0::Meta::SHIFT_ON); + static_assert(static_cast(AMETA_SHIFT_LEFT_ON) == + common::V1_0::Meta::SHIFT_LEFT_ON); + static_assert(static_cast(AMETA_SHIFT_RIGHT_ON) == + common::V1_0::Meta::SHIFT_RIGHT_ON); + static_assert(static_cast(AMETA_SYM_ON) == + common::V1_0::Meta::SYM_ON); + static_assert(static_cast(AMETA_FUNCTION_ON) == + common::V1_0::Meta::FUNCTION_ON); + static_assert(static_cast(AMETA_CTRL_ON) == + common::V1_0::Meta::CTRL_ON); + static_assert(static_cast(AMETA_CTRL_LEFT_ON) == + common::V1_0::Meta::CTRL_LEFT_ON); + static_assert(static_cast(AMETA_CTRL_RIGHT_ON) == + common::V1_0::Meta::CTRL_RIGHT_ON); + static_assert(static_cast(AMETA_META_ON) == + common::V1_0::Meta::META_ON); + static_assert(static_cast(AMETA_META_LEFT_ON) == + common::V1_0::Meta::META_LEFT_ON); + static_assert(static_cast(AMETA_META_RIGHT_ON) == + common::V1_0::Meta::META_RIGHT_ON); + static_assert(static_cast(AMETA_CAPS_LOCK_ON) == + common::V1_0::Meta::CAPS_LOCK_ON); + static_assert(static_cast(AMETA_NUM_LOCK_ON) == + common::V1_0::Meta::NUM_LOCK_ON); + static_assert(static_cast(AMETA_SCROLL_LOCK_ON) == + common::V1_0::Meta::SCROLL_LOCK_ON); + return static_cast>(state); +} + +static hidl_bitfield getButtonState(int32_t buttonState) { + // No need for static_assert here. + // The button values have already been asserted in getActionButton(..) above + return static_cast>(buttonState); +} + +static common::V1_0::ToolType getToolType(int32_t toolType) { + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_UNKNOWN) == + common::V1_0::ToolType::UNKNOWN); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_FINGER) == + common::V1_0::ToolType::FINGER); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_STYLUS) == + common::V1_0::ToolType::STYLUS); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_MOUSE) == + common::V1_0::ToolType::MOUSE); + static_assert(static_cast(AMOTION_EVENT_TOOL_TYPE_ERASER) == + common::V1_0::ToolType::ERASER); + return static_cast(toolType); +} + +// MotionEvent axes asserts +static_assert(static_cast(AMOTION_EVENT_AXIS_X) == + common::V1_0::Axis::X); +static_assert(static_cast(AMOTION_EVENT_AXIS_Y) == + common::V1_0::Axis::Y); +static_assert(static_cast(AMOTION_EVENT_AXIS_PRESSURE) == + common::V1_0::Axis::PRESSURE); +static_assert(static_cast(AMOTION_EVENT_AXIS_SIZE) == + common::V1_0::Axis::SIZE); +static_assert(static_cast(AMOTION_EVENT_AXIS_TOUCH_MAJOR) == + common::V1_0::Axis::TOUCH_MAJOR); +static_assert(static_cast(AMOTION_EVENT_AXIS_TOUCH_MINOR) == + common::V1_0::Axis::TOUCH_MINOR); +static_assert(static_cast(AMOTION_EVENT_AXIS_TOOL_MAJOR) == + common::V1_0::Axis::TOOL_MAJOR); +static_assert(static_cast(AMOTION_EVENT_AXIS_TOOL_MINOR) == + common::V1_0::Axis::TOOL_MINOR); +static_assert(static_cast(AMOTION_EVENT_AXIS_ORIENTATION) == + common::V1_0::Axis::ORIENTATION); +static_assert(static_cast(AMOTION_EVENT_AXIS_VSCROLL) == + common::V1_0::Axis::VSCROLL); +static_assert(static_cast(AMOTION_EVENT_AXIS_HSCROLL) == + common::V1_0::Axis::HSCROLL); +static_assert(static_cast(AMOTION_EVENT_AXIS_Z) == + common::V1_0::Axis::Z); +static_assert(static_cast(AMOTION_EVENT_AXIS_RX) == + common::V1_0::Axis::RX); +static_assert(static_cast(AMOTION_EVENT_AXIS_RY) == + common::V1_0::Axis::RY); +static_assert(static_cast(AMOTION_EVENT_AXIS_RZ) == + common::V1_0::Axis::RZ); +static_assert(static_cast(AMOTION_EVENT_AXIS_HAT_X) == + common::V1_0::Axis::HAT_X); +static_assert(static_cast(AMOTION_EVENT_AXIS_HAT_Y) == + common::V1_0::Axis::HAT_Y); +static_assert(static_cast(AMOTION_EVENT_AXIS_LTRIGGER) == + common::V1_0::Axis::LTRIGGER); +static_assert(static_cast(AMOTION_EVENT_AXIS_RTRIGGER) == + common::V1_0::Axis::RTRIGGER); +static_assert(static_cast(AMOTION_EVENT_AXIS_THROTTLE) == + common::V1_0::Axis::THROTTLE); +static_assert(static_cast(AMOTION_EVENT_AXIS_RUDDER) == + common::V1_0::Axis::RUDDER); +static_assert(static_cast(AMOTION_EVENT_AXIS_WHEEL) == + common::V1_0::Axis::WHEEL); +static_assert(static_cast(AMOTION_EVENT_AXIS_GAS) == + common::V1_0::Axis::GAS); +static_assert(static_cast(AMOTION_EVENT_AXIS_BRAKE) == + common::V1_0::Axis::BRAKE); +static_assert(static_cast(AMOTION_EVENT_AXIS_DISTANCE) == + common::V1_0::Axis::DISTANCE); +static_assert(static_cast(AMOTION_EVENT_AXIS_TILT) == + common::V1_0::Axis::TILT); +static_assert(static_cast(AMOTION_EVENT_AXIS_SCROLL) == + common::V1_0::Axis::SCROLL); +static_assert(static_cast(AMOTION_EVENT_AXIS_RELATIVE_X) == + common::V1_0::Axis::RELATIVE_X); +static_assert(static_cast(AMOTION_EVENT_AXIS_RELATIVE_Y) == + common::V1_0::Axis::RELATIVE_Y); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_1) == + common::V1_0::Axis::GENERIC_1); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_2) == + common::V1_0::Axis::GENERIC_2); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_3) == + common::V1_0::Axis::GENERIC_3); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_4) == + common::V1_0::Axis::GENERIC_4); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_5) == + common::V1_0::Axis::GENERIC_5); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_6) == + common::V1_0::Axis::GENERIC_6); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_7) == + common::V1_0::Axis::GENERIC_7); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_8) == + common::V1_0::Axis::GENERIC_8); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_9) == + common::V1_0::Axis::GENERIC_9); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_10) == + common::V1_0::Axis::GENERIC_10); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_11) == + common::V1_0::Axis::GENERIC_11); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_12) == + common::V1_0::Axis::GENERIC_12); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_13) == + common::V1_0::Axis::GENERIC_13); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_14) == + common::V1_0::Axis::GENERIC_14); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_15) == + common::V1_0::Axis::GENERIC_15); +static_assert(static_cast(AMOTION_EVENT_AXIS_GENERIC_16) == + common::V1_0::Axis::GENERIC_16); + +static common::V1_0::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) { + common::V1_0::VideoFrame out; + out.width = frame.getWidth(); + out.height = frame.getHeight(); + out.data = frame.getData(); + struct timeval timestamp = frame.getTimestamp(); + out.timestamp = seconds_to_nanoseconds(timestamp.tv_sec) + + microseconds_to_nanoseconds(timestamp.tv_usec); + return out; +} + +static std::vector convertVideoFrames( + const std::vector& frames) { + std::vector out; + for (const TouchVideoFrame& frame : frames) { + out.push_back(getHalVideoFrame(frame)); + } + return out; +} + +static uint8_t getActionIndex(int32_t action) { + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> + AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; +} + +static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, + std::vector* outPointerProperties, + std::vector* outPointerCoords) { + outPointerProperties->reserve(args.pointerCount); + outPointerCoords->reserve(args.pointerCount); + for (size_t i = 0; i < args.pointerCount; i++) { + common::V1_0::PointerProperties properties; + properties.id = args.pointerProperties[i].id; + properties.toolType = getToolType(args.pointerProperties[i].toolType); + outPointerProperties->push_back(properties); + + common::V1_0::PointerCoords coords; + // OK to copy bits because we have static_assert for pointerCoords axes + coords.bits = args.pointerCoords[i].bits; + coords.values = std::vector( + args.pointerCoords[i].values, + args.pointerCoords[i].values + BitSet64::count(args.pointerCoords[i].bits)); + outPointerCoords->push_back(coords); + } +} + +common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArgs& args) { + common::V1_0::MotionEvent event; + event.deviceId = args.deviceId; + event.source = getSource(args.source); + event.displayId = args.displayId; + event.downTime = args.downTime; + event.eventTime = args.eventTime; + event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); + event.actionIndex = getActionIndex(args.action); + event.actionButton = getActionButton(args.actionButton); + event.flags = getFlags(args.flags); + event.policyFlags = getPolicyFlags(args.policyFlags); + event.edgeFlags = getEdgeFlags(args.edgeFlags); + event.metaState = getMetastate(args.metaState); + event.buttonState = getButtonState(args.buttonState); + event.xPrecision = args.xPrecision; + event.yPrecision = args.yPrecision; + + std::vector pointerProperties; + std::vector pointerCoords; + getHidlPropertiesAndCoords(args, /*out*/&pointerProperties, /*out*/&pointerCoords); + event.pointerProperties = pointerProperties; + event.pointerCoords = pointerCoords; + + event.deviceTimestamp = args.deviceTimestamp; + event.frames = convertVideoFrames(args.videoFrames); + + return event; +} + +} // namespace android diff --git a/services/inputflinger/InputClassifierConverter.h b/services/inputflinger/InputClassifierConverter.h new file mode 100644 index 0000000000..5154b0bd06 --- /dev/null +++ b/services/inputflinger/InputClassifierConverter.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef _UI_INPUT_CLASSIFIER_CONVERTER_H +#define _UI_INPUT_CLASSIFIER_CONVERTER_H + +#include "InputListener.h" +#include + + +namespace android { + +/** + * Convert from framework's NotifyMotionArgs to hidl's common::V1_0::MotionEvent + */ +::android::hardware::input::common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent( + const NotifyMotionArgs& args); + +} // namespace android + +#endif // _UI_INPUT_CLASSIFIER_CONVERTER_H diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 1835449b5d..9054316204 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -6,6 +6,7 @@ cc_test { "BlockingQueue_test.cpp", "TestInputListener.cpp", "InputClassifier_test.cpp", + "InputClassifierConverter_test.cpp", "InputDispatcher_test.cpp", "InputReader_test.cpp", ], diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp new file mode 100644 index 0000000000..813b69edbb --- /dev/null +++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#include "../InputClassifierConverter.h" + +#include +#include + + +using namespace android::hardware::input; + +namespace android { + +// --- InputClassifierConverterTest --- + +static NotifyMotionArgs generateBasicMotionArgs() { + // Create a basic motion event for testing + PointerProperties properties; + properties.id = 0; + properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2); + coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5); + static constexpr nsecs_t downTime = 2; + NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, + 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, + 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, + downTime, {}/*videoFrames*/); + return motionArgs; +} + +static float getMotionEventAxis(common::V1_0::PointerCoords coords, + common::V1_0::Axis axis) { + uint32_t index = BitSet64::getIndexOfBit(static_cast(coords.bits), + static_cast(axis)); + return coords.values[index]; +} + +/** + * Check that coordinates get converted properly from the framework's PointerCoords + * to the hidl PointerCoords in input::common. + */ +TEST(InputClassifierConverterTest, PointerCoordsAxes) { + const NotifyMotionArgs motionArgs = generateBasicMotionArgs(); + ASSERT_EQ(1, motionArgs.pointerCoords[0].getX()); + ASSERT_EQ(2, motionArgs.pointerCoords[0].getY()); + ASSERT_EQ(0.5, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE)); + ASSERT_EQ(3U, BitSet64::count(motionArgs.pointerCoords[0].bits)); + + common::V1_0::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs); + + ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::X), + motionArgs.pointerCoords[0].getX()); + ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::Y), + motionArgs.pointerCoords[0].getY()); + ASSERT_EQ(getMotionEventAxis(motionEvent.pointerCoords[0], common::V1_0::Axis::SIZE), + motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE)); + ASSERT_EQ(BitSet64::count(motionArgs.pointerCoords[0].bits), + BitSet64::count(motionEvent.pointerCoords[0].bits)); +} + +} // namespace android -- cgit v1.2.3-59-g8ed1b