Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e54cb857 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Adding feature: input device disable/enable

This functionality will only be available for signed system
services. A disable call will cause a file descriptor to the input
device driver to be closed, which in turn may cause the input
device to switch into a low-power mode. An enable call will
reopen the input device.

Bug: 30143923
Test: CTS test - android.view.cts.InputDeviceEnabledTest
Native test - WhenEnabledChanges_SendsDeviceResetNotification,
WhenDeviceCreated_EnabledIsTrue. Also developed a custom apk
with signature permission that calls disable/enable on a
touchscreen device. Verified that touchscreen stops working
when disable is called and starts working again when
enable is called. Verified that the file
handle to the driver is closed and reopened. Verified that
the notification onInputDeviceChanged is received in the
app.

Change-Id: I99a7866cebff873dc1848e11a39045ed2eaec07c
parent 50c3195e
Loading
Loading
Loading
Loading
+138 −68
Original line number Diff line number Diff line
@@ -150,7 +150,8 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path,
        fd(fd), id(id), path(path), identifier(identifier),
        classes(0), configuration(NULL), virtualKeyMap(NULL),
        ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
        timestampOverrideSec(0), timestampOverrideUsec(0) {
        timestampOverrideSec(0), timestampOverrideUsec(0), enabled(true),
        isVirtual(fd < 0) {
    memset(keyBitmask, 0, sizeof(keyBitmask));
    memset(absBitmask, 0, sizeof(absBitmask));
    memset(relBitmask, 0, sizeof(relBitmask));
@@ -173,6 +174,25 @@ void EventHub::Device::close() {
    }
}

status_t EventHub::Device::enable() {
    fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", path.string(), strerror(errno));
        return -errno;
    }
    enabled = true;
    return OK;
}

status_t EventHub::Device::disable() {
    close();
    enabled = false;
    return OK;
}

bool EventHub::Device::hasValidFd() {
    return !isVirtual && enabled;
}

// --- EventHub ---

@@ -286,7 +306,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
        AutoMutex _l(mLock);

        Device* device = getDeviceLocked(deviceId);
        if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
        if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
            struct input_absinfo info;
            if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
                ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
@@ -337,7 +357,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
        AutoMutex _l(mLock);

        Device* device = getDeviceLocked(deviceId);
        if (device && !device->isVirtual() && test_bit(scanCode, device->keyBitmask)) {
        if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) {
            uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
            memset(keyState, 0, sizeof(keyState));
            if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
@@ -352,7 +372,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
    AutoMutex _l(mLock);

    Device* device = getDeviceLocked(deviceId);
    if (device && !device->isVirtual() && device->keyMap.haveKeyLayout()) {
    if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) {
        Vector<int32_t> scanCodes;
        device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
        if (scanCodes.size() != 0) {
@@ -377,7 +397,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
        AutoMutex _l(mLock);

        Device* device = getDeviceLocked(deviceId);
        if (device && !device->isVirtual() && test_bit(sw, device->swBitmask)) {
        if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) {
            uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
            memset(swState, 0, sizeof(swState));
            if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
@@ -395,7 +415,7 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t*
        AutoMutex _l(mLock);

        Device* device = getDeviceLocked(deviceId);
        if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
        if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) {
            struct input_absinfo info;
            if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
                ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
@@ -532,7 +552,7 @@ void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {

void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) {
    int32_t sc;
    if (device && !device->isVirtual() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
    if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) {
        struct input_event ev;
        ev.time.tv_sec = 0;
        ev.time.tv_usec = 0;
@@ -636,7 +656,7 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) {
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device && !device->isVirtual()) {
    if (device && device->hasValidFd()) {
        ff_effect effect;
        memset(&effect, 0, sizeof(effect));
        effect.type = FF_RUMBLE;
@@ -670,7 +690,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
void EventHub::cancelVibrate(int32_t deviceId) {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device && !device->isVirtual()) {
    if (device && device->hasValidFd()) {
        if (device->ffEffectPlaying) {
            device->ffEffectPlaying = false;

@@ -1065,12 +1085,37 @@ static const int32_t GAMEPAD_KEYCODES[] = {
        AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
};

status_t EventHub::registerDeviceForEpollLocked(Device* device) {
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    if (mUsingEpollWakeup) {
        eventItem.events |= EPOLLWAKEUP;
    }
    eventItem.data.u32 = device->id;
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {
        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
        return -errno;
    }
    return OK;
}

status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) {
    if (device->hasValidFd()) {
        if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
            ALOGW("Could not remove device fd from epoll instance.  errno=%d", errno);
            return -errno;
        }
    }
    return OK;
}

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];

    ALOGV("Opening device: %s", devicePath);

    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
        return -1;
@@ -1135,13 +1180,6 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
    // Fill in the descriptor.
    assignDescriptorLocked(identifier);

    // Make file descriptor non-blocking for use with poll().
    if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
        ALOGE("Error %d making device file descriptor non-blocking.", errno);
        close(fd);
        return -1;
    }

    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
@@ -1303,12 +1341,6 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
                break;
            }
        }

        // Disable kernel key repeat since we handle it ourselves
        unsigned int repeatRate[] = {0,0};
        if (ioctl(fd, EVIOCSREP, repeatRate)) {
            ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno));
        }
    }

    // If the device isn't recognized as something we handle, don't monitor it.
@@ -1332,23 +1364,41 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
    if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
            && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        device->controllerNumber = getNextControllerNumberLocked(device);
        setLedForController(device);
        setLedForControllerLocked(device);
    }

    // Register with epoll.
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    if (mUsingEpollWakeup) {
        eventItem.events |= EPOLLWAKEUP;
    }
    eventItem.data.u32 = deviceId;
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);

    if (registerDeviceForEpollLocked(device) != OK) {
        delete device;
        return -1;
    }

    configureFd(device);

    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
            "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
         deviceId, fd, devicePath, device->identifier.name.string(),
         device->classes,
         device->configurationFile.string(),
         device->keyMap.keyLayoutFile.string(),
         device->keyMap.keyCharacterMapFile.string(),
         toString(mBuiltInKeyboardId == deviceId));

    addDeviceLocked(device);
    return OK;
}

void EventHub::configureFd(Device* device) {
    // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
    if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        // Disable kernel key repeat since we handle it ourselves
        unsigned int repeatRate[] = {0, 0};
        if (ioctl(device->fd, EVIOCSREP, repeatRate)) {
            ALOGW("Unable to disable kernel key repeat for %s: %s",
                  device->path.string(), strerror(errno));
        }
    }

    String8 wakeMechanism("EPOLLWAKEUP");
    if (!mUsingEpollWakeup) {
#ifndef EVIOCSSUSPENDBLOCK
@@ -1357,44 +1407,67 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
        // this feature, we need to be prepared to define the ioctl ourselves.
#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int)
#endif
        if (ioctl(fd, EVIOCSSUSPENDBLOCK, 1)) {
        if (ioctl(device->fd, EVIOCSSUSPENDBLOCK, 1)) {
            wakeMechanism = "<none>";
        } else {
            wakeMechanism = "EVIOCSSUSPENDBLOCK";
        }
    }

    // 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.
    //
    // In older kernel, before Linux 3.4, there was no way to tell the kernel which
    // clock to use to input event timestamps.  The standard kernel behavior was to
    // record a real time timestamp, which isn't what we want.  Android kernels therefore
    // contained a patch to the evdev_event() function in drivers/input/evdev.c to
    // replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic
    // clock to be used instead of the real time clock.
    //
    // As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock.
    // Therefore, we no longer require the Android-specific kernel patch described above
    // as long as we make sure to set select the monotonic clock.  We do that here.
    int clockId = CLOCK_MONOTONIC;
    bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
    bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
    ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.string(),
          toString(usingClockIoctl));
}

    ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
            "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
            "wakeMechanism=%s, usingClockIoctl=%s",
         deviceId, fd, devicePath, device->identifier.name.string(),
         device->classes,
         device->configurationFile.string(),
         device->keyMap.keyLayoutFile.string(),
         device->keyMap.keyCharacterMapFile.string(),
         toString(mBuiltInKeyboardId == deviceId),
         wakeMechanism.string(), toString(usingClockIoctl));
bool EventHub::isDeviceEnabled(int32_t deviceId) {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device == NULL) {
        ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
        return false;
    }
    return device->enabled;
}

    addDeviceLocked(device);
    return 0;
status_t EventHub::enableDevice(int32_t deviceId) {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device == NULL) {
        ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
        return BAD_VALUE;
    }
    if (device->enabled) {
        ALOGW("Duplicate call to %s, input device %" PRId32 " already enabled", __func__, deviceId);
        return OK;
    }
    status_t result = device->enable();
    if (result != OK) {
        ALOGE("Failed to enable device %" PRId32, deviceId);
        return result;
    }

    configureFd(device);

    return registerDeviceForEpollLocked(device);
}

status_t EventHub::disableDevice(int32_t deviceId) {
    AutoMutex _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    if (device == NULL) {
        ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
        return BAD_VALUE;
    }
    if (!device->enabled) {
        ALOGW("Duplicate call to %s, input device already disabled", __func__);
        return OK;
    }
    unregisterDeviceFromEpollLocked(device);
    return device->disable();
}

void EventHub::createVirtualKeyboardLocked() {
@@ -1490,7 +1563,7 @@ void EventHub::releaseControllerNumberLocked(Device* device) {
    mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1));
}

void EventHub::setLedForController(Device* device) {
void EventHub::setLedForControllerLocked(Device* device) {
    for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) {
        setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1);
    }
@@ -1556,11 +1629,7 @@ void EventHub::closeDeviceLocked(Device* device) {
        mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
    }

    if (!device->isVirtual()) {
        if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) {
            ALOGW("Could not remove device fd from epoll instance.  errno=%d", errno);
        }
    }
    unregisterDeviceFromEpollLocked(device);

    releaseControllerNumberLocked(device);

@@ -1691,6 +1760,7 @@ void EventHub::dump(String8& dump) {
            }
            dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
            dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
            dump.appendFormat(INDENT3 "Enabled: %s\n", toString(device->enabled));
            dump.appendFormat(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
            dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
            dump.appendFormat(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
+25 −5
Original line number Diff line number Diff line
@@ -25,9 +25,8 @@
#include <input/KeyCharacterMap.h>
#include <input/VirtualKeyMap.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Mutex.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
#include <utils/PropertyMap.h>
@@ -267,6 +266,15 @@ public:

    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
    virtual void monitor() = 0;

    /* Return true if the device is enabled. */
    virtual bool isDeviceEnabled(int32_t deviceId) = 0;

    /* Enable an input device */
    virtual status_t enableDevice(int32_t deviceId) = 0;

    /* Disable an input device. Closes file descriptor to that device. */
    virtual status_t disableDevice(int32_t deviceId) = 0;
};

class EventHub : public EventHubInterface
@@ -335,7 +343,7 @@ private:
    struct Device {
        Device* next;

        int fd; // may be -1 if device is virtual
        int fd; // may be -1 if device is closed
        const int32_t id;
        const String8 path;
        const InputDeviceIdentifier identifier;
@@ -371,7 +379,11 @@ private:

        void close();

        inline bool isVirtual() const { return fd < 0; }
        bool enabled; // initially true
        status_t enable();
        status_t disable();
        bool hasValidFd();
        const bool isVirtual; // set if fd < 0 is passed to constructor

        const sp<KeyCharacterMap>& getKeyCharacterMap() const {
            if (combinedKeyMap != NULL) {
@@ -390,6 +402,14 @@ private:
    void closeDeviceLocked(Device* device);
    void closeAllDevicesLocked();

    void configureFd(Device* device);

    bool isDeviceEnabled(int32_t deviceId);
    status_t enableDevice(int32_t deviceId);
    status_t disableDevice(int32_t deviceId);
    status_t registerDeviceForEpollLocked(Device* device);
    status_t unregisterDeviceFromEpollLocked(Device* device);

    status_t scanDirLocked(const char *dirname);
    void scanDevicesLocked();
    status_t readNotifyLocked();
@@ -409,7 +429,7 @@ private:

    int32_t getNextControllerNumberLocked(Device* device);
    void releaseControllerNumberLocked(Device* device);
    void setLedForController(Device* device);
    void setLedForControllerLocked(Device* device);

    status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const;
    void setLedStateLocked(Device* device, int32_t led, bool on);
+38 −0
Original line number Diff line number Diff line
@@ -788,6 +788,18 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
    }
}

bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
    AutoMutex _l(mLock);

    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        InputDevice* device = mDevices.valueAt(deviceIndex);
        return device->isEnabled();
    }
    ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
    return false;
}

void InputReader::dump(String8& dump) {
    AutoMutex _l(mLock);

@@ -961,6 +973,26 @@ InputDevice::~InputDevice() {
    mMappers.clear();
}

bool InputDevice::isEnabled() {
    return getEventHub()->isDeviceEnabled(mId);
}

void InputDevice::setEnabled(bool enabled, nsecs_t when) {
    if (isEnabled() == enabled) {
        return;
    }

    if (enabled) {
        getEventHub()->enableDevice(mId);
        reset(when);
    } else {
        reset(when);
        getEventHub()->disableDevice(mId);
    }
    // Must change generation to flag this device as changed
    bumpGeneration();
}

void InputDevice::dump(String8& dump) {
    InputDeviceInfo deviceInfo;
    getDeviceInfo(& deviceInfo);
@@ -1032,6 +1064,12 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config
            }
        }

        if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
            ssize_t index = config->disabledDevices.indexOf(mId);
            bool enabled = index < 0;
            setEnabled(enabled, when);
        }

        size_t numMappers = mMappers.size();
        for (size_t i = 0; i < numMappers; i++) {
            InputMapper* mapper = mMappers[i];
+18 −1
Original line number Diff line number Diff line
@@ -26,11 +26,14 @@
#include <input/VelocityTracker.h>
#include <ui/DisplayInfo.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <utils/Condition.h>
#include <utils/Thread.h>
#include <utils/Mutex.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/BitSet.h>
#include <utils/SortedVector.h>

#include <stddef.h>
#include <unistd.h>
@@ -147,6 +150,9 @@ struct InputReaderConfiguration {
        // The pointer capture mode has changed.
        CHANGE_POINTER_CAPTURE = 1 << 8,

        // The set of disabled input devices (disabledDevices) has changed.
        CHANGE_ENABLED_STATE = 1 << 9,

        // All devices must be reopened.
        CHANGE_MUST_REOPEN = 1 << 31,
    };
@@ -237,6 +243,9 @@ struct InputReaderConfiguration {
    // True if pointer capture is enabled.
    bool pointerCapture;

    // The set of currently disabled input devices.
    SortedVector<int32_t> disabledDevices;

    InputReaderConfiguration() :
            virtualKeyQuietTime(0),
            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
@@ -344,6 +353,9 @@ public:
    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
    virtual void monitor() = 0;

    /* Returns true if the input device is enabled. */
    virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;

    /* Runs a single iteration of the processing loop.
     * Nominally reads and processes one incoming message from the EventHub.
     *
@@ -463,6 +475,8 @@ public:

    virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices);

    virtual bool isInputDeviceEnabled(int32_t deviceId);

    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
            int32_t scanCode);
    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
@@ -608,6 +622,9 @@ public:

    inline bool isIgnored() { return mMappers.isEmpty(); }

    bool isEnabled();
    void setEnabled(bool enabled, nsecs_t when);

    void dump(String8& dump);
    void addMapper(InputMapper* mapper);
    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
+134 −1

File changed.

Preview size limit exceeded, changes collapsed.