summaryrefslogtreecommitdiffstats
path: root/modules/input/evdev/InputHub.cpp
blob: e72ac2e97a0f2130f287f1c2db8c367b5ae03e14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
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