You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
829 lines
28 KiB
829 lines
28 KiB
/* |
|
* 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 "InputHub.h" |
|
|
|
#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 <android/input.h> |
|
#include <hardware_legacy/power.h> |
|
#include <linux/input.h> |
|
|
|
#include <utils/Log.h> |
|
|
|
#include "BitUtils.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); |
|
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 hasKeyInRange(int32_t start, int32_t end) const override; |
|
virtual bool hasRelativeAxis(int32_t axis) const override; |
|
virtual bool hasAbsoluteAxis(int32_t axis) const override; |
|
virtual bool hasSwitch(int32_t sw) const override; |
|
virtual bool hasForceFeedback(int32_t ff) 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 const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const override; |
|
virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const override; |
|
|
|
virtual void vibrate(nsecs_t duration) override; |
|
virtual void cancelVibrate() 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::hasKeyInRange(int32_t startKey, int32_t endKey) const { |
|
return testBitInRange(mKeyBitmask, startKey, endKey); |
|
} |
|
|
|
bool EvdevDeviceNode::hasRelativeAxis(int axis) const { |
|
if (axis >= 0 && axis <= REL_MAX) { |
|
return testBit(axis, mRelBitmask); |
|
} |
|
return false; |
|
} |
|
|
|
bool EvdevDeviceNode::hasAbsoluteAxis(int axis) const { |
|
if (axis >= 0 && axis <= ABS_MAX) { |
|
return getAbsoluteAxisInfo(axis) != nullptr; |
|
} |
|
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::hasSwitch(int32_t sw) const { |
|
if (sw >= 0 && sw <= SW_MAX) { |
|
return testBit(sw, mSwBitmask); |
|
} |
|
return false; |
|
} |
|
|
|
bool EvdevDeviceNode::hasForceFeedback(int32_t ff) const { |
|
if (ff >= 0 && ff <= FF_MAX) { |
|
return testBit(ff, mFfBitmask); |
|
} |
|
return false; |
|
} |
|
|
|
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() { |
|
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(const 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: %zd 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=%zd", 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) { |
|
auto deviceNode = openNode(path); |
|
if (deviceNode == nullptr) { |
|
ALOGE("could not open device node %s. err=%zd", path.c_str(), res); |
|
} else { |
|
mInputCallback->onDeviceAdded(deviceNode); |
|
} |
|
} else { |
|
auto deviceNode = findNodeByPath(path); |
|
if (deviceNode != nullptr) { |
|
status_t ret = closeNode(deviceNode.get()); |
|
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; |
|
auto node = openNode(filename); |
|
if (node == nullptr) { |
|
ALOGE("could not open device node %s", filename.c_str()); |
|
} else { |
|
mInputCallback->onDeviceAdded(node); |
|
} |
|
} |
|
::closedir(dir); |
|
return OK; |
|
} |
|
|
|
std::shared_ptr<InputDeviceNode> InputHub::openNode(const std::string& path) { |
|
ALOGV("opening %s...", path.c_str()); |
|
auto evdevNode = std::shared_ptr<EvdevDeviceNode>(EvdevDeviceNode::openDeviceNode(path)); |
|
if (evdevNode == nullptr) { |
|
return nullptr; |
|
} |
|
|
|
auto fd = evdevNode->getFd(); |
|
ALOGV("opened %s with fd %d", path.c_str(), fd); |
|
mDeviceNodes[fd] = evdevNode; |
|
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 nullptr; |
|
} |
|
|
|
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 evdevNode; |
|
} |
|
|
|
status_t InputHub::closeNode(const InputDeviceNode* node) { |
|
for (auto pair : mDeviceNodes) { |
|
if (pair.second.get() == node) { |
|
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
|
|
|