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
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index d2f8995..50589b4 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -150,7 +150,8 @@
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 @@
}
}
+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 @@
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 @@
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 @@
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 @@
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 @@
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::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::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::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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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);
}
@@ -1500,7 +1573,7 @@
if (!device->keyMap.haveKeyLayout()) {
return false;
}
-
+
Vector<int32_t> scanCodes;
device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
const size_t N = scanCodes.size();
@@ -1510,7 +1583,7 @@
return true;
}
}
-
+
return false;
}
@@ -1556,11 +1629,7 @@
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 @@
}
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);
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h
index 6869253..727b73a 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/EventHub.h
@@ -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 @@
/* 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 @@
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 @@
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 @@
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 @@
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);
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 2705e13..8af9038 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -788,6 +788,18 @@
}
}
+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 @@
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 @@
}
}
+ 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];
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 3171526..647f271 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -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 @@
// 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 @@
// 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 @@
/* 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 @@
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 @@
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);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2e0bcd1..7082fe1 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,6 +16,7 @@
#include "../InputReader.h"
+#include <inttypes.h>
#include <utils/List.h>
#include <gtest/gtest.h>
#include <math.h>
@@ -161,6 +162,22 @@
mConfig.excludedDeviceNames.push(deviceName);
}
+ void addDisabledDevice(int32_t deviceId) {
+ ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
+ bool currentlyEnabled = index < 0;
+ if (currentlyEnabled) {
+ mConfig.disabledDevices.add(deviceId);
+ }
+ }
+
+ void removeDisabledDevice(int32_t deviceId) {
+ ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
+ bool currentlyEnabled = index < 0;
+ if (!currentlyEnabled) {
+ mConfig.disabledDevices.remove(deviceId);
+ }
+ }
+
void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
mPointerControllers.add(deviceId, controller);
}
@@ -236,6 +253,11 @@
mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
}
+ void assertNotifyConfigurationChangedWasNotCalled() {
+ ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
+ << "Expected notifyConfigurationChanged() to not have been called.";
+ }
+
void assertNotifyDeviceResetWasCalled(
NotifyDeviceResetArgs* outEventArgs = NULL) {
ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
@@ -246,6 +268,11 @@
mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
}
+ void assertNotifyDeviceResetWasNotCalled() {
+ ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
+ << "Expected notifyDeviceReset() to not have been called.";
+ }
+
void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) {
ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
<< "Expected notifyKey() to have been called.";
@@ -328,9 +355,20 @@
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
KeyedVector<int32_t, bool> leds;
Vector<VirtualKeyDefinition> virtualKeys;
+ bool enabled;
+
+ status_t enable() {
+ enabled = true;
+ return OK;
+ }
+
+ status_t disable() {
+ enabled = false;
+ return OK;
+ }
explicit Device(uint32_t classes) :
- classes(classes) {
+ classes(classes), enabled(true) {
}
};
@@ -363,6 +401,43 @@
enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
}
+ bool isDeviceEnabled(int32_t deviceId) {
+ Device* device = getDevice(deviceId);
+ if (device == NULL) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return false;
+ }
+ return device->enabled;
+ }
+
+ status_t enableDevice(int32_t deviceId) {
+ status_t result;
+ Device* device = getDevice(deviceId);
+ if (device == NULL) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
+ return OK;
+ }
+ result = device->enable();
+ return result;
+ }
+
+ status_t disableDevice(int32_t deviceId) {
+ Device* device = getDevice(deviceId);
+ if (device == NULL) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (!device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
+ return OK;
+ }
+ return device->disable();
+ }
+
void finishDeviceScan() {
enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
}
@@ -1020,6 +1095,20 @@
mFakeEventHub->assertQueueIsEmpty();
}
+ void disableDevice(int32_t deviceId, InputDevice* device) {
+ mFakePolicy->addDisabledDevice(deviceId);
+ configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+ }
+
+ void enableDevice(int32_t deviceId, InputDevice* device) {
+ mFakePolicy->removeDisabledDevice(deviceId);
+ configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+ }
+
+ void configureDevice(uint32_t changes, InputDevice* device) {
+ device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ }
+
FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
const String8& name, uint32_t classes, uint32_t sources,
const PropertyMap* configuration) {
@@ -1058,6 +1147,46 @@
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
+ constexpr int32_t deviceId = 1;
+ constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+ InputDevice* device = mReader->newDevice(deviceId, 0, String8("fake"), deviceClass);
+ // Must add at least one mapper or the device will be ignored!
+ FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
+ device->addMapper(mapper);
+ mReader->setNextDevice(device);
+ addDevice(deviceId, String8("fake"), deviceClass, NULL);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(NULL));
+
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+
+ ASSERT_EQ(device->isEnabled(), true);
+ disableDevice(deviceId, device);
+ mReader->loopOnce();
+
+ mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_EQ(device->isEnabled(), false);
+
+ disableDevice(deviceId, device);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyDeviceResetWasNotCalled();
+ mFakeListener->assertNotifyConfigurationChangedWasNotCalled();
+ ASSERT_EQ(device->isEnabled(), false);
+
+ enableDevice(deviceId, device);
+ mReader->loopOnce();
+ mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+ ASSERT_EQ(deviceId, resetArgs.deviceId);
+ ASSERT_EQ(device->isEnabled(), true);
+}
+
TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
FakeInputMapper* mapper = NULL;
ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"),
@@ -1255,6 +1384,10 @@
ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
}
+TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsTrue) {
+ ASSERT_EQ(mDevice->isEnabled(), true);
+}
+
TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
// Configuration.
InputReaderConfiguration config;