text: /*
   * 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));
      }
  }
  
  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 (const 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;
      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 (const 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 (const auto& pair : mDeviceNodes) {
          if (pair.second->getPath() == path) return pair.second;
      }
      return nullptr;
  }
  
  bool InputHub::manageWakeLocks() const {
      return mWakeupMechanism != WakeMechanism::EPOLL_WAKEUP;
  }
  
  }  // namespace android
  