summaryrefslogtreecommitdiffstats
path: root/modules/input/evdev/InputHub.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/input/evdev/InputHub.cpp')
-rw-r--r--modules/input/evdev/InputHub.cpp802
1 files changed, 802 insertions, 0 deletions
diff --git a/modules/input/evdev/InputHub.cpp b/modules/input/evdev/InputHub.cpp
new file mode 100644
index 0000000..e72ac2e
--- /dev/null
+++ b/modules/input/evdev/InputHub.cpp
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2015 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 "InputHub"
+#define LOG_NDEBUG 0
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "InputHub.h"
+
+#include <android/input.h>
+#include <hardware_legacy/power.h>
+#include <linux/input.h>
+
+#include <utils/Log.h>
+
+namespace android {
+
+static const char WAKE_LOCK_ID[] = "KeyEvents";
+static const int NO_TIMEOUT = -1;
+static const int EPOLL_MAX_EVENTS = 16;
+static const int INPUT_MAX_EVENTS = 128;
+
+static constexpr bool testBit(int bit, const uint8_t arr[]) {
+ return arr[bit / 8] & (1 << (bit % 8));
+}
+
+static constexpr size_t sizeofBitArray(size_t bits) {
+ return (bits + 7) / 8;
+}
+
+static void getLinuxRelease(int* major, int* minor) {
+ struct utsname info;
+ if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) {
+ *major = 0, *minor = 0;
+ ALOGE("Could not get linux version: %s", strerror(errno));
+ }
+}
+
+static bool processHasCapability(int capability) {
+ LOG_ALWAYS_FATAL_IF(!cap_valid(capability), "invalid linux capability: %d", capability);
+ struct __user_cap_header_struct cap_header_data;
+ struct __user_cap_data_struct cap_data_data[2];
+ cap_user_header_t caphdr = &cap_header_data;
+ cap_user_data_t capdata = cap_data_data;
+ caphdr->pid = 0;
+ caphdr->version = _LINUX_CAPABILITY_VERSION_3;
+ LOG_ALWAYS_FATAL_IF(capget(caphdr, capdata) != 0,
+ "Could not get process capabilities. errno=%d", errno);
+ ALOGV("effective capabilities: %08x %08x", capdata[0].effective, capdata[1].effective);
+ int idx = CAP_TO_INDEX(capability);
+ return capdata[idx].effective & CAP_TO_MASK(capability);
+}
+
+class EvdevDeviceNode : public InputDeviceNode {
+public:
+ static EvdevDeviceNode* openDeviceNode(const std::string& path);
+
+ virtual ~EvdevDeviceNode() {
+ ALOGV("closing %s (fd=%d)", mPath.c_str(), mFd);
+ if (mFd >= 0) {
+ ::close(mFd);
+ }
+ }
+
+ virtual int getFd() const { return mFd; }
+ virtual const std::string& getPath() const override { return mPath; }
+ virtual const std::string& getName() const override { return mName; }
+ virtual const std::string& getLocation() const override { return mLocation; }
+ virtual const std::string& getUniqueId() const override { return mUniqueId; }
+
+ virtual uint16_t getBusType() const override { return mBusType; }
+ virtual uint16_t getVendorId() const override { return mVendorId; }
+ virtual uint16_t getProductId() const override { return mProductId; }
+ virtual uint16_t getVersion() const override { return mVersion; }
+
+ virtual bool hasKey(int32_t key) const override;
+ virtual bool hasRelativeAxis(int axis) const override;
+ virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const override;
+ virtual bool hasInputProperty(int property) const override;
+
+ virtual int32_t getKeyState(int32_t key) const override;
+ virtual int32_t getSwitchState(int32_t sw) const override;
+ virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const override;
+
+ virtual void vibrate(nsecs_t duration) override;
+ virtual void cancelVibrate(int32_t deviceId) override;
+
+ virtual void disableDriverKeyRepeat() override;
+
+private:
+ EvdevDeviceNode(const std::string& path, int fd) :
+ mFd(fd), mPath(path) {}
+
+ status_t queryProperties();
+ void queryAxisInfo();
+
+ int mFd;
+ std::string mPath;
+
+ std::string mName;
+ std::string mLocation;
+ std::string mUniqueId;
+
+ uint16_t mBusType;
+ uint16_t mVendorId;
+ uint16_t mProductId;
+ uint16_t mVersion;
+
+ uint8_t mKeyBitmask[KEY_CNT / 8];
+ uint8_t mAbsBitmask[ABS_CNT / 8];
+ uint8_t mRelBitmask[REL_CNT / 8];
+ uint8_t mSwBitmask[SW_CNT / 8];
+ uint8_t mLedBitmask[LED_CNT / 8];
+ uint8_t mFfBitmask[FF_CNT / 8];
+ uint8_t mPropBitmask[INPUT_PROP_CNT / 8];
+
+ std::unordered_map<uint32_t, std::unique_ptr<AbsoluteAxisInfo>> mAbsInfo;
+
+ bool mFfEffectPlaying = false;
+ int16_t mFfEffectId = -1;
+};
+
+EvdevDeviceNode* EvdevDeviceNode::openDeviceNode(const std::string& path) {
+ auto fd = TEMP_FAILURE_RETRY(::open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd < 0) {
+ ALOGE("could not open evdev device %s. err=%d", path.c_str(), errno);
+ return nullptr;
+ }
+
+ // Tell the kernel that we want to use the monotonic clock for reporting
+ // timestamps associated with input events. This is important because the
+ // input system uses the timestamps extensively and assumes they were
+ // recorded using the monotonic clock.
+ //
+ // The EVIOCSCLOCKID ioctl was introduced in Linux 3.4.
+ int clockId = CLOCK_MONOTONIC;
+ if (TEMP_FAILURE_RETRY(ioctl(fd, EVIOCSCLOCKID, &clockId)) < 0) {
+ ALOGW("Could not set input clock id to CLOCK_MONOTONIC. errno=%d", errno);
+ }
+
+ auto node = new EvdevDeviceNode(path, fd);
+ status_t ret = node->queryProperties();
+ if (ret != OK) {
+ ALOGE("could not open evdev device %s: failed to read properties. errno=%d",
+ path.c_str(), ret);
+ delete node;
+ return nullptr;
+ }
+ return node;
+}
+
+status_t EvdevDeviceNode::queryProperties() {
+ char buffer[80];
+
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGNAME(sizeof(buffer) - 1), buffer)) < 1) {
+ ALOGV("could not get device name for %s.", mPath.c_str());
+ } else {
+ buffer[sizeof(buffer) - 1] = '\0';
+ mName = buffer;
+ }
+
+ int driverVersion;
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGVERSION, &driverVersion))) {
+ ALOGE("could not get driver version for %s. err=%d", mPath.c_str(), errno);
+ return -errno;
+ }
+
+ struct input_id inputId;
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGID, &inputId))) {
+ ALOGE("could not get device input id for %s. err=%d", mPath.c_str(), errno);
+ return -errno;
+ }
+ mBusType = inputId.bustype;
+ mVendorId = inputId.vendor;
+ mProductId = inputId.product;
+ mVersion = inputId.version;
+
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGPHYS(sizeof(buffer) - 1), buffer)) < 1) {
+ ALOGV("could not get location for %s.", mPath.c_str());
+ } else {
+ buffer[sizeof(buffer) - 1] = '\0';
+ mLocation = buffer;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGUNIQ(sizeof(buffer) - 1), buffer)) < 1) {
+ ALOGV("could not get unique id for %s.", mPath.c_str());
+ } else {
+ buffer[sizeof(buffer) - 1] = '\0';
+ mUniqueId = buffer;
+ }
+
+ ALOGV("add device %s", mPath.c_str());
+ ALOGV(" bus: %04x\n"
+ " vendor: %04x\n"
+ " product: %04x\n"
+ " version: %04x\n",
+ mBusType, mVendorId, mProductId, mVersion);
+ ALOGV(" name: \"%s\"\n"
+ " location: \"%s\"\n"
+ " unique_id: \"%s\"\n"
+ " descriptor: (TODO)\n"
+ " driver: v%d.%d.%d",
+ mName.c_str(), mLocation.c_str(), mUniqueId.c_str(),
+ driverVersion >> 16, (driverVersion >> 8) & 0xff, (driverVersion >> 16) & 0xff);
+
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_KEY, sizeof(mKeyBitmask)), mKeyBitmask));
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_ABS, sizeof(mAbsBitmask)), mAbsBitmask));
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_REL, sizeof(mRelBitmask)), mRelBitmask));
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_SW, sizeof(mSwBitmask)), mSwBitmask));
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_LED, sizeof(mLedBitmask)), mLedBitmask));
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGBIT(EV_FF, sizeof(mFfBitmask)), mFfBitmask));
+ TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGPROP(sizeof(mPropBitmask)), mPropBitmask));
+
+ queryAxisInfo();
+
+ return OK;
+}
+
+void EvdevDeviceNode::queryAxisInfo() {
+ for (int32_t axis = 0; axis < ABS_MAX; ++axis) {
+ if (testBit(axis, mAbsBitmask)) {
+ struct input_absinfo info;
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGABS(axis), &info))) {
+ ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
+ axis, mPath.c_str(), mFd, errno);
+ continue;
+ }
+
+ mAbsInfo[axis] = std::unique_ptr<AbsoluteAxisInfo>(new AbsoluteAxisInfo{
+ .minValue = info.minimum,
+ .maxValue = info.maximum,
+ .flat = info.flat,
+ .fuzz = info.fuzz,
+ .resolution = info.resolution
+ });
+ }
+ }
+}
+
+bool EvdevDeviceNode::hasKey(int32_t key) const {
+ if (key >= 0 && key <= KEY_MAX) {
+ return testBit(key, mKeyBitmask);
+ }
+ return false;
+}
+
+bool EvdevDeviceNode::hasRelativeAxis(int axis) const {
+ if (axis >= 0 && axis <= REL_MAX) {
+ return testBit(axis, mRelBitmask);
+ }
+ return false;
+}
+
+const AbsoluteAxisInfo* EvdevDeviceNode::getAbsoluteAxisInfo(int32_t axis) const {
+ if (axis < 0 || axis > ABS_MAX) {
+ return nullptr;
+ }
+
+ const auto absInfo = mAbsInfo.find(axis);
+ if (absInfo != mAbsInfo.end()) {
+ return absInfo->second.get();
+ }
+ return nullptr;
+}
+
+bool EvdevDeviceNode::hasInputProperty(int property) const {
+ if (property >= 0 && property <= INPUT_PROP_MAX) {
+ return testBit(property, mPropBitmask);
+ }
+ return false;
+}
+
+int32_t EvdevDeviceNode::getKeyState(int32_t key) const {
+ if (key >= 0 && key <= KEY_MAX) {
+ if (testBit(key, mKeyBitmask)) {
+ uint8_t keyState[sizeofBitArray(KEY_CNT)];
+ memset(keyState, 0, sizeof(keyState));
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGKEY(sizeof(keyState)), keyState)) >= 0) {
+ return testBit(key, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ }
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t EvdevDeviceNode::getSwitchState(int32_t sw) const {
+ if (sw >= 0 && sw <= SW_MAX) {
+ if (testBit(sw, mSwBitmask)) {
+ uint8_t swState[sizeofBitArray(SW_CNT)];
+ memset(swState, 0, sizeof(swState));
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGSW(sizeof(swState)), swState)) >= 0) {
+ return testBit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ }
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+status_t EvdevDeviceNode::getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const {
+ *outValue = 0;
+
+ if (axis >= 0 && axis <= ABS_MAX) {
+ if (testBit(axis, mAbsBitmask)) {
+ struct input_absinfo info;
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCGABS(axis), &info))) {
+ ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
+ axis, mPath.c_str(), mFd, errno);
+ return -errno;
+ }
+
+ *outValue = info.value;
+ return OK;
+ }
+ }
+ return -1;
+}
+
+void EvdevDeviceNode::vibrate(nsecs_t duration) {
+ ff_effect effect{};
+ effect.type = FF_RUMBLE;
+ effect.id = mFfEffectId;
+ effect.u.rumble.strong_magnitude = 0xc000;
+ effect.u.rumble.weak_magnitude = 0xc000;
+ effect.replay.length = (duration + 999'999LL) / 1'000'000LL;
+ effect.replay.delay = 0;
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCSFF, &effect))) {
+ ALOGW("Could not upload force feedback effect to device %s due to error %d.",
+ mPath.c_str(), errno);
+ return;
+ }
+ mFfEffectId = effect.id;
+
+ struct input_event ev{};
+ ev.type = EV_FF;
+ ev.code = mFfEffectId;
+ ev.value = 1;
+ size_t written = TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(ev)));
+ if (written != sizeof(ev)) {
+ ALOGW("Could not start force feedback effect on device %s due to error %d.",
+ mPath.c_str(), errno);
+ return;
+ }
+ mFfEffectPlaying = true;
+}
+
+void EvdevDeviceNode::cancelVibrate(int32_t deviceId) {
+ if (mFfEffectPlaying) {
+ mFfEffectPlaying = false;
+
+ struct input_event ev{};
+ ev.type = EV_FF;
+ ev.code = mFfEffectId;
+ ev.value = 0;
+ size_t written = TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(ev)));
+ if (written != sizeof(ev)) {
+ ALOGW("Could not stop force feedback effect on device %s due to error %d.",
+ mPath.c_str(), errno);
+ return;
+ }
+ }
+}
+
+void EvdevDeviceNode::disableDriverKeyRepeat() {
+ unsigned int repeatRate[] = {0, 0};
+ if (TEMP_FAILURE_RETRY(ioctl(mFd, EVIOCSREP, repeatRate))) {
+ ALOGW("Unable to disable kernel key repeat for %s due to error %d.",
+ mPath.c_str(), errno);
+ }
+}
+
+InputHub::InputHub(std::shared_ptr<InputCallbackInterface> cb) :
+ mInputCallback(cb) {
+ // Determine the type of suspend blocking we can do on this device. There
+ // are 3 options, in decreasing order of preference:
+ // 1) EPOLLWAKEUP: introduced in Linux kernel 3.5, this flag can be set on
+ // an epoll event to indicate that a wake lock should be held from the
+ // time an fd has data until the next epoll_wait (or the epoll fd is
+ // closed).
+ // 2) EVIOCSSUSPENDBLOCK: introduced into the Android kernel's evdev
+ // driver, this ioctl blocks suspend while the event queue for the fd is
+ // not empty. This was never accepted into the mainline kernel, and it was
+ // replaced by EPOLLWAKEUP.
+ // 3) explicit wake locks: use acquire_wake_lock to manage suspend
+ // blocking explicitly in the InputHub code.
+ //
+ // (1) can be checked by simply observing the Linux kernel version. (2)
+ // requires an fd from an evdev node, which cannot be done in the InputHub
+ // constructor. So we assume (3) unless (1) is true, and we can verify
+ // whether (2) is true once we have an evdev fd (and we're not in (1)).
+ int major, minor;
+ getLinuxRelease(&major, &minor);
+ if (major > 3 || (major == 3 && minor >= 5)) {
+ ALOGI("Using EPOLLWAKEUP to block suspend while processing input events.");
+ mWakeupMechanism = WakeMechanism::EPOLL_WAKEUP;
+ mNeedToCheckSuspendBlockIoctl = false;
+ }
+ if (manageWakeLocks()) {
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+ }
+
+ // epoll_create argument is ignored, but it must be > 0.
+ mEpollFd = epoll_create(1);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
+
+ mINotifyFd = inotify_init();
+ LOG_ALWAYS_FATAL_IF(mINotifyFd < 0, "Could not create inotify instance. errno=%d", errno);
+
+ struct epoll_event eventItem;
+ memset(&eventItem, 0, sizeof(eventItem));
+ eventItem.events = EPOLLIN;
+ if (mWakeupMechanism == WakeMechanism::EPOLL_WAKEUP) {
+ eventItem.events |= EPOLLWAKEUP;
+ }
+ eventItem.data.u32 = mINotifyFd;
+ int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
+
+ int wakeFds[2];
+ result = pipe(wakeFds);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
+
+ mWakeEventFd = eventfd(0, EFD_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(mWakeEventFd == -1, "Could not create wake event fd. errno=%d", errno);
+
+ eventItem.data.u32 = mWakeEventFd;
+ result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, &eventItem);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance. errno=%d", errno);
+}
+
+InputHub::~InputHub() {
+ ::close(mEpollFd);
+ ::close(mINotifyFd);
+ ::close(mWakeEventFd);
+
+ if (manageWakeLocks()) {
+ release_wake_lock(WAKE_LOCK_ID);
+ }
+}
+
+status_t InputHub::registerDevicePath(const std::string& path) {
+ ALOGV("registering device path %s", path.c_str());
+ int wd = inotify_add_watch(mINotifyFd, path.c_str(), IN_DELETE | IN_CREATE);
+ if (wd < 0) {
+ ALOGE("Could not add %s to INotify watch. errno=%d", path.c_str(), errno);
+ return -errno;
+ }
+ mWatchedPaths[wd] = path;
+ scanDir(path);
+ return OK;
+}
+
+status_t InputHub::unregisterDevicePath(const std::string& path) {
+ int wd = -1;
+ for (auto pair : mWatchedPaths) {
+ if (pair.second == path) {
+ wd = pair.first;
+ break;
+ }
+ }
+
+ if (wd == -1) {
+ return BAD_VALUE;
+ }
+ mWatchedPaths.erase(wd);
+ if (inotify_rm_watch(mINotifyFd, wd) != 0) {
+ return -errno;
+ }
+ return OK;
+}
+
+status_t InputHub::poll() {
+ bool deviceChange = false;
+
+ if (manageWakeLocks()) {
+ // Mind the wake lock dance!
+ // If we're relying on wake locks, we hold a wake lock at all times
+ // except during epoll_wait(). This works due to some subtle
+ // choreography. When a device driver has pending (unread) events, it
+ // acquires a kernel wake lock. However, once the last pending event
+ // has been read, the device driver will release the kernel wake lock.
+ // To prevent the system from going to sleep when this happens, the
+ // InputHub holds onto its own user wake lock while the client is
+ // processing events. Thus the system can only sleep if there are no
+ // events pending or currently being processed.
+ release_wake_lock(WAKE_LOCK_ID);
+ }
+
+ struct epoll_event pendingEventItems[EPOLL_MAX_EVENTS];
+ int pollResult = epoll_wait(mEpollFd, pendingEventItems, EPOLL_MAX_EVENTS, NO_TIMEOUT);
+
+ if (manageWakeLocks()) {
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+ }
+
+ if (pollResult == 0) {
+ ALOGW("epoll_wait should not return 0 with no timeout");
+ return UNKNOWN_ERROR;
+ }
+ if (pollResult < 0) {
+ // An error occurred. Return even if it's EINTR, and let the caller
+ // restart the poll.
+ ALOGE("epoll_wait returned with errno=%d", errno);
+ return -errno;
+ }
+
+ // pollResult > 0: there are events to process
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ std::vector<int> removedDeviceFds;
+ int inputFd = -1;
+ std::shared_ptr<InputDeviceNode> deviceNode;
+ for (int i = 0; i < pollResult; ++i) {
+ const struct epoll_event& eventItem = pendingEventItems[i];
+
+ int dataFd = static_cast<int>(eventItem.data.u32);
+ if (dataFd == mINotifyFd) {
+ if (eventItem.events & EPOLLIN) {
+ deviceChange = true;
+ } else {
+ ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
+ }
+ continue;
+ }
+
+ if (dataFd == mWakeEventFd) {
+ if (eventItem.events & EPOLLIN) {
+ ALOGV("awoken after wake()");
+ uint64_t u;
+ ssize_t nRead = TEMP_FAILURE_RETRY(read(mWakeEventFd, &u, sizeof(uint64_t)));
+ if (nRead != sizeof(uint64_t)) {
+ ALOGW("Could not read event fd; waking anyway.");
+ }
+ } else {
+ ALOGW("Received unexpected epoll event 0x%08x for wake event.",
+ eventItem.events);
+ }
+ continue;
+ }
+
+ // Update the fd and device node when the fd changes. When several
+ // events are read back-to-back with the same fd, this saves many reads
+ // from the hash table.
+ if (inputFd != dataFd) {
+ inputFd = dataFd;
+ deviceNode = mDeviceNodes[inputFd];
+ }
+ if (deviceNode == nullptr) {
+ ALOGE("could not find device node for fd %d", inputFd);
+ continue;
+ }
+ if (eventItem.events & EPOLLIN) {
+ struct input_event ievs[INPUT_MAX_EVENTS];
+ for (;;) {
+ ssize_t readSize = TEMP_FAILURE_RETRY(read(inputFd, ievs, sizeof(ievs)));
+ if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
+ ALOGW("could not get event, removed? (fd: %d, size: %d errno: %d)",
+ inputFd, readSize, errno);
+
+ removedDeviceFds.push_back(inputFd);
+ break;
+ } else if (readSize < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ ALOGW("could not get event. errno=%d", errno);
+ }
+ break;
+ } else if (readSize % sizeof(input_event) != 0) {
+ ALOGE("could not get event. wrong size=%d", readSize);
+ break;
+ } else {
+ size_t count = static_cast<size_t>(readSize) / sizeof(struct input_event);
+ for (size_t i = 0; i < count; ++i) {
+ auto& iev = ievs[i];
+ auto when = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
+ InputEvent inputEvent = { when, iev.type, iev.code, iev.value };
+ mInputCallback->onInputEvent(deviceNode, inputEvent, now);
+ }
+ }
+ }
+ } else if (eventItem.events & EPOLLHUP) {
+ ALOGI("Removing device fd %d due to epoll hangup event.", inputFd);
+ removedDeviceFds.push_back(inputFd);
+ } else {
+ ALOGW("Received unexpected epoll event 0x%08x for device fd %d",
+ eventItem.events, inputFd);
+ }
+ }
+
+ if (removedDeviceFds.size()) {
+ for (auto deviceFd : removedDeviceFds) {
+ auto deviceNode = mDeviceNodes[deviceFd];
+ if (deviceNode != nullptr) {
+ status_t ret = closeNodeByFd(deviceFd);
+ if (ret != OK) {
+ ALOGW("Could not close device with fd %d. errno=%d", deviceFd, ret);
+ } else {
+ mInputCallback->onDeviceRemoved(deviceNode);
+ }
+ }
+ }
+ }
+
+ if (deviceChange) {
+ readNotify();
+ }
+
+ return OK;
+}
+
+status_t InputHub::wake() {
+ ALOGV("wake() called");
+
+ uint64_t u = 1;
+ ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &u, sizeof(uint64_t)));
+
+ if (nWrite != sizeof(uint64_t) && errno != EAGAIN) {
+ ALOGW("Could not write wake signal, errno=%d", errno);
+ return -errno;
+ }
+ return OK;
+}
+
+void InputHub::dump(String8& dump) {
+ // TODO
+}
+
+status_t InputHub::readNotify() {
+ char event_buf[512];
+ struct inotify_event* event;
+
+ ssize_t res = TEMP_FAILURE_RETRY(read(mINotifyFd, event_buf, sizeof(event_buf)));
+ if (res < static_cast<int>(sizeof(*event))) {
+ ALOGW("could not get inotify event, %s\n", strerror(errno));
+ return -errno;
+ }
+
+ size_t event_pos = 0;
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ while (res >= static_cast<int>(sizeof(*event))) {
+ event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
+ if (event->len) {
+ std::string path = mWatchedPaths[event->wd];
+ path.append("/").append(event->name);
+ ALOGV("inotify event for path %s", path.c_str());
+
+ if (event->mask & IN_CREATE) {
+ std::shared_ptr<InputDeviceNode> deviceNode;
+ status_t res = openNode(path, &deviceNode);
+ if (res != OK) {
+ ALOGE("could not open device node %s. err=%d", path.c_str(), res);
+ } else {
+ mInputCallback->onDeviceAdded(deviceNode);
+ }
+ } else {
+ auto deviceNode = findNodeByPath(path);
+ if (deviceNode != nullptr) {
+ status_t ret = closeNode(deviceNode);
+ if (ret != OK) {
+ ALOGW("Could not close device %s. errno=%d", path.c_str(), ret);
+ } else {
+ mInputCallback->onDeviceRemoved(deviceNode);
+ }
+ } else {
+ ALOGW("could not find device node for %s", path.c_str());
+ }
+ }
+ }
+ int event_size = sizeof(*event) + event->len;
+ res -= event_size;
+ event_pos += event_size;
+ }
+
+ return OK;
+}
+
+status_t InputHub::scanDir(const std::string& path) {
+ auto dir = ::opendir(path.c_str());
+ if (dir == nullptr) {
+ ALOGE("could not open device path %s to scan for devices. err=%d", path.c_str(), errno);
+ return -errno;
+ }
+
+ while (auto dirent = readdir(dir)) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+ std::string filename = path + "/" + dirent->d_name;
+ std::shared_ptr<InputDeviceNode> node;
+ if (openNode(filename, &node) != OK) {
+ ALOGE("could not open device node %s", filename.c_str());
+ } else {
+ mInputCallback->onDeviceAdded(node);
+ }
+ }
+ ::closedir(dir);
+ return OK;
+}
+
+status_t InputHub::openNode(const std::string& path,
+ std::shared_ptr<InputDeviceNode>* outNode) {
+ ALOGV("opening %s...", path.c_str());
+ auto evdevNode = std::shared_ptr<EvdevDeviceNode>(EvdevDeviceNode::openDeviceNode(path));
+ if (evdevNode == nullptr) {
+ return UNKNOWN_ERROR;
+ }
+
+ auto fd = evdevNode->getFd();
+ ALOGV("opened %s with fd %d", path.c_str(), fd);
+ *outNode = std::static_pointer_cast<InputDeviceNode>(evdevNode);
+ mDeviceNodes[fd] = *outNode;
+ struct epoll_event eventItem{};
+ eventItem.events = EPOLLIN;
+ if (mWakeupMechanism == WakeMechanism::EPOLL_WAKEUP) {
+ eventItem.events |= EPOLLWAKEUP;
+ }
+ eventItem.data.u32 = fd;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
+ ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
+ return -errno;
+ }
+
+ if (mNeedToCheckSuspendBlockIoctl) {
+#ifndef EVIOCSSUSPENDBLOCK
+ // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels
+ // will use an epoll flag instead, so as long as we want to support this
+ // feature, we need to be prepared to define the ioctl ourselves.
+#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int)
+#endif
+ if (TEMP_FAILURE_RETRY(ioctl(fd, EVIOCSSUSPENDBLOCK, 1))) {
+ // no wake mechanism, continue using explicit wake locks
+ ALOGI("Using explicit wakelocks to block suspend while processing input events.");
+ } else {
+ mWakeupMechanism = WakeMechanism::LEGACY_EVDEV_SUSPENDBLOCK_IOCTL;
+ // release any held wakelocks since we won't need them anymore
+ release_wake_lock(WAKE_LOCK_ID);
+ ALOGI("Using EVIOCSSUSPENDBLOCK to block suspend while processing input events.");
+ }
+ mNeedToCheckSuspendBlockIoctl = false;
+ }
+
+ return OK;
+}
+
+status_t InputHub::closeNode(const std::shared_ptr<InputDeviceNode>& node) {
+ for (auto pair : mDeviceNodes) {
+ if (pair.second.get() == node.get()) {
+ return closeNodeByFd(pair.first);
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t InputHub::closeNodeByFd(int fd) {
+ status_t ret = OK;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL)) {
+ ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
+ ret = -errno;
+ }
+ mDeviceNodes.erase(fd);
+ ::close(fd);
+ return ret;
+}
+
+std::shared_ptr<InputDeviceNode> InputHub::findNodeByPath(const std::string& path) {
+ for (auto pair : mDeviceNodes) {
+ if (pair.second->getPath() == path) return pair.second;
+ }
+ return nullptr;
+}
+
+bool InputHub::manageWakeLocks() const {
+ return mWakeupMechanism != WakeMechanism::EPOLL_WAKEUP;
+}
+
+} // namespace android