2011-01-02 16:37:43 -08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2005 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.
|
|
|
|
*/
|
|
|
|
|
2009-03-03 19:31:44 -08:00
|
|
|
#define LOG_TAG "EventHub"
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// #define LOG_NDEBUG 0
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2011-01-02 16:37:43 -08:00
|
|
|
#include "EventHub.h"
|
|
|
|
|
2009-03-03 19:31:44 -08:00
|
|
|
#include <hardware_legacy/power.h>
|
|
|
|
|
|
|
|
#include <cutils/properties.h>
|
|
|
|
#include <utils/Log.h>
|
|
|
|
#include <utils/Timers.h>
|
2009-05-31 19:13:00 -07:00
|
|
|
#include <utils/threads.h>
|
|
|
|
#include <utils/Errors.h>
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <memory.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
2012-02-17 15:34:57 -08:00
|
|
|
#include <androidfw/KeyLayoutMap.h>
|
|
|
|
#include <androidfw/KeyCharacterMap.h>
|
|
|
|
#include <androidfw/VirtualKeyMap.h>
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2012-04-06 14:51:01 -07:00
|
|
|
#include <sha1.h>
|
2009-03-03 19:31:44 -08:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <dirent.h>
|
2011-06-14 17:09:25 -07:00
|
|
|
|
|
|
|
#include <sys/inotify.h>
|
|
|
|
#include <sys/epoll.h>
|
2009-03-03 19:31:44 -08:00
|
|
|
#include <sys/ioctl.h>
|
2011-06-14 17:09:25 -07:00
|
|
|
#include <sys/limits.h>
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
/* this macro is used to tell if "bit" is set in "array"
|
|
|
|
* it selects a byte from the array, and does a boolean AND
|
|
|
|
* operation with a byte that only has the relevant bit set.
|
|
|
|
* eg. to check for the 12th bit, we do (array[1] & 1<<4)
|
|
|
|
*/
|
|
|
|
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
|
|
|
|
|
2010-06-30 16:10:35 -07:00
|
|
|
/* this macro computes the number of bytes needed to represent a bit array of the specified size */
|
|
|
|
#define sizeof_bit_array(bits) ((bits + 7) / 8)
|
|
|
|
|
2010-10-01 17:46:21 -07:00
|
|
|
#define INDENT " "
|
|
|
|
#define INDENT2 " "
|
|
|
|
#define INDENT3 " "
|
|
|
|
|
2009-03-03 19:31:44 -08:00
|
|
|
namespace android {
|
|
|
|
|
|
|
|
static const char *WAKE_LOCK_ID = "KeyEvents";
|
2010-12-02 13:50:46 -08:00
|
|
|
static const char *DEVICE_PATH = "/dev/input";
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
/* return the larger integer */
|
|
|
|
static inline int max(int v1, int v2)
|
|
|
|
{
|
|
|
|
return (v1 > v2) ? v1 : v2;
|
|
|
|
}
|
|
|
|
|
2010-10-01 17:46:21 -07:00
|
|
|
static inline const char* toString(bool value) {
|
|
|
|
return value ? "true" : "false";
|
|
|
|
}
|
|
|
|
|
2012-04-06 14:51:01 -07:00
|
|
|
static String8 sha1(const String8& in) {
|
|
|
|
SHA1_CTX ctx;
|
|
|
|
SHA1Init(&ctx);
|
|
|
|
SHA1Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size());
|
|
|
|
u_char digest[SHA1_DIGEST_LENGTH];
|
|
|
|
SHA1Final(digest, &ctx);
|
|
|
|
|
|
|
|
String8 out;
|
|
|
|
for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) {
|
|
|
|
out.appendFormat("%02x", digest[i]);
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2012-04-10 14:30:49 -07:00
|
|
|
static void setDescriptor(InputDeviceIdentifier& identifier) {
|
|
|
|
// Compute a device descriptor that uniquely identifies the device.
|
|
|
|
// The descriptor is assumed to be a stable identifier. Its value should not
|
|
|
|
// change between reboots, reconnections, firmware updates or new releases of Android.
|
|
|
|
// Ideally, we also want the descriptor to be short and relatively opaque.
|
|
|
|
String8 rawDescriptor;
|
|
|
|
rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product);
|
|
|
|
if (!identifier.uniqueId.isEmpty()) {
|
|
|
|
rawDescriptor.append("uniqueId:");
|
|
|
|
rawDescriptor.append(identifier.uniqueId);
|
|
|
|
} if (identifier.vendor == 0 && identifier.product == 0) {
|
|
|
|
// If we don't know the vendor and product id, then the device is probably
|
|
|
|
// built-in so we need to rely on other information to uniquely identify
|
|
|
|
// the input device. Usually we try to avoid relying on the device name or
|
|
|
|
// location but for built-in input device, they are unlikely to ever change.
|
|
|
|
if (!identifier.name.isEmpty()) {
|
|
|
|
rawDescriptor.append("name:");
|
|
|
|
rawDescriptor.append(identifier.name);
|
|
|
|
} else if (!identifier.location.isEmpty()) {
|
|
|
|
rawDescriptor.append("location:");
|
|
|
|
rawDescriptor.append(identifier.location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
identifier.descriptor = sha1(rawDescriptor);
|
2012-04-11 18:27:33 -07:00
|
|
|
ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(),
|
|
|
|
identifier.descriptor.string());
|
2012-04-10 14:30:49 -07:00
|
|
|
}
|
|
|
|
|
2011-08-31 12:56:34 -07:00
|
|
|
// --- Global Functions ---
|
|
|
|
|
|
|
|
uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
|
|
|
|
// Touch devices get dibs on touch-related axes.
|
|
|
|
if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
|
|
|
|
switch (axis) {
|
|
|
|
case ABS_X:
|
|
|
|
case ABS_Y:
|
|
|
|
case ABS_PRESSURE:
|
|
|
|
case ABS_TOOL_WIDTH:
|
|
|
|
case ABS_DISTANCE:
|
|
|
|
case ABS_TILT_X:
|
|
|
|
case ABS_TILT_Y:
|
|
|
|
case ABS_MT_SLOT:
|
|
|
|
case ABS_MT_TOUCH_MAJOR:
|
|
|
|
case ABS_MT_TOUCH_MINOR:
|
|
|
|
case ABS_MT_WIDTH_MAJOR:
|
|
|
|
case ABS_MT_WIDTH_MINOR:
|
|
|
|
case ABS_MT_ORIENTATION:
|
|
|
|
case ABS_MT_POSITION_X:
|
|
|
|
case ABS_MT_POSITION_Y:
|
|
|
|
case ABS_MT_TOOL_TYPE:
|
|
|
|
case ABS_MT_BLOB_ID:
|
|
|
|
case ABS_MT_TRACKING_ID:
|
|
|
|
case ABS_MT_PRESSURE:
|
|
|
|
case ABS_MT_DISTANCE:
|
|
|
|
return INPUT_DEVICE_CLASS_TOUCH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Joystick devices get the rest.
|
|
|
|
return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
// --- EventHub::Device ---
|
|
|
|
|
|
|
|
EventHub::Device::Device(int fd, int32_t id, const String8& path,
|
|
|
|
const InputDeviceIdentifier& identifier) :
|
|
|
|
next(NULL),
|
|
|
|
fd(fd), id(id), path(path), identifier(identifier),
|
2012-04-13 04:09:27 -07:00
|
|
|
classes(0), configuration(NULL), virtualKeyMap(NULL),
|
|
|
|
ffEffectPlaying(false), ffEffectId(-1) {
|
2011-06-14 17:09:25 -07:00
|
|
|
memset(keyBitmask, 0, sizeof(keyBitmask));
|
|
|
|
memset(absBitmask, 0, sizeof(absBitmask));
|
|
|
|
memset(relBitmask, 0, sizeof(relBitmask));
|
|
|
|
memset(swBitmask, 0, sizeof(swBitmask));
|
|
|
|
memset(ledBitmask, 0, sizeof(ledBitmask));
|
2012-04-13 04:09:27 -07:00
|
|
|
memset(ffBitmask, 0, sizeof(ffBitmask));
|
2011-06-14 17:09:25 -07:00
|
|
|
memset(propBitmask, 0, sizeof(propBitmask));
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
EventHub::Device::~Device() {
|
|
|
|
close();
|
2010-11-29 17:37:49 -08:00
|
|
|
delete configuration;
|
2010-12-02 13:50:46 -08:00
|
|
|
delete virtualKeyMap;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
void EventHub::Device::close() {
|
|
|
|
if (fd >= 0) {
|
|
|
|
::close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- EventHub ---
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
const uint32_t EventHub::EPOLL_ID_INOTIFY;
|
|
|
|
const uint32_t EventHub::EPOLL_ID_WAKE;
|
|
|
|
const int EventHub::EPOLL_SIZE_HINT;
|
|
|
|
const int EventHub::EPOLL_MAX_EVENTS;
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
EventHub::EventHub(void) :
|
2012-04-10 14:30:49 -07:00
|
|
|
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),
|
2010-12-02 13:50:46 -08:00
|
|
|
mOpeningDevices(0), mClosingDevices(0),
|
2011-06-14 17:09:25 -07:00
|
|
|
mNeedToSendFinishedDeviceScan(false),
|
|
|
|
mNeedToReopenDevices(false), mNeedToScanDevices(true),
|
|
|
|
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
|
2009-03-03 19:31:44 -08:00
|
|
|
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
|
2011-03-18 18:14:26 -07:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
|
|
|
|
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
|
|
|
|
|
|
|
|
mINotifyFd = inotify_init();
|
|
|
|
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
|
|
|
|
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
|
|
|
|
DEVICE_PATH, errno);
|
|
|
|
|
|
|
|
struct epoll_event eventItem;
|
|
|
|
memset(&eventItem, 0, sizeof(eventItem));
|
|
|
|
eventItem.events = EPOLLIN;
|
|
|
|
eventItem.data.u32 = EPOLL_ID_INOTIFY;
|
|
|
|
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);
|
|
|
|
|
|
|
|
mWakeReadPipeFd = wakeFds[0];
|
|
|
|
mWakeWritePipeFd = wakeFds[1];
|
|
|
|
|
|
|
|
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
|
|
|
|
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
|
|
|
|
errno);
|
|
|
|
|
|
|
|
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
|
|
|
|
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
|
|
|
|
errno);
|
|
|
|
|
|
|
|
eventItem.data.u32 = EPOLL_ID_WAKE;
|
|
|
|
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
|
|
|
|
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
|
|
|
|
errno);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
EventHub::~EventHub(void) {
|
2011-06-14 17:09:25 -07:00
|
|
|
closeAllDevicesLocked();
|
|
|
|
|
|
|
|
while (mClosingDevices) {
|
|
|
|
Device* device = mClosingDevices;
|
|
|
|
mClosingDevices = device->next;
|
|
|
|
delete device;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
::close(mEpollFd);
|
|
|
|
::close(mINotifyFd);
|
|
|
|
::close(mWakeReadPipeFd);
|
|
|
|
::close(mWakeWritePipeFd);
|
|
|
|
|
|
|
|
release_wake_lock(WAKE_LOCK_ID);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2012-04-06 14:51:01 -07:00
|
|
|
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
|
2009-03-03 19:31:44 -08:00
|
|
|
AutoMutex _l(mLock);
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-06 14:51:01 -07:00
|
|
|
if (device == NULL) return InputDeviceIdentifier();
|
|
|
|
return device->identifier;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
|
2009-03-03 19:31:44 -08:00
|
|
|
AutoMutex _l(mLock);
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2009-03-03 19:31:44 -08:00
|
|
|
if (device == NULL) return 0;
|
|
|
|
return device->classes;
|
|
|
|
}
|
|
|
|
|
2010-11-29 17:37:49 -08:00
|
|
|
void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
|
|
|
|
AutoMutex _l(mLock);
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2010-11-29 17:37:49 -08:00
|
|
|
if (device && device->configuration) {
|
|
|
|
*outConfiguration = *device->configuration;
|
2010-11-18 20:53:46 -08:00
|
|
|
} else {
|
|
|
|
outConfiguration->clear();
|
2010-11-29 17:37:49 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-23 21:28:06 -07:00
|
|
|
status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
|
|
|
|
RawAbsoluteAxisInfo* outAxisInfo) const {
|
2010-08-30 03:02:23 -07:00
|
|
|
outAxisInfo->clear();
|
2010-07-23 21:28:06 -07:00
|
|
|
|
2011-08-10 15:07:05 -07:00
|
|
|
if (axis >= 0 && axis <= ABS_MAX) {
|
|
|
|
AutoMutex _l(mLock);
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2011-08-10 15:07:05 -07:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-10 14:30:49 -07:00
|
|
|
if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
|
2011-08-10 15:07:05 -07:00
|
|
|
struct input_absinfo info;
|
|
|
|
if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
|
2011-08-10 15:07:05 -07:00
|
|
|
axis, device->identifier.name.string(), device->fd, errno);
|
|
|
|
return -errno;
|
|
|
|
}
|
2010-07-23 21:28:06 -07:00
|
|
|
|
2011-08-10 15:07:05 -07:00
|
|
|
if (info.minimum != info.maximum) {
|
|
|
|
outAxisInfo->valid = true;
|
|
|
|
outAxisInfo->minValue = info.minimum;
|
|
|
|
outAxisInfo->maxValue = info.maximum;
|
|
|
|
outAxisInfo->flat = info.flat;
|
|
|
|
outAxisInfo->fuzz = info.fuzz;
|
|
|
|
outAxisInfo->resolution = info.resolution;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2011-08-10 15:07:05 -07:00
|
|
|
return -1;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2011-02-19 05:07:28 -08:00
|
|
|
bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
|
|
|
|
if (axis >= 0 && axis <= REL_MAX) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2011-08-10 15:07:05 -07:00
|
|
|
if (device) {
|
2011-02-19 05:07:28 -08:00
|
|
|
return test_bit(axis, device->relBitmask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-05-24 01:07:44 -07:00
|
|
|
bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
|
|
|
|
if (property >= 0 && property <= INPUT_PROP_MAX) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2011-08-10 15:07:05 -07:00
|
|
|
if (device) {
|
2011-05-24 01:07:44 -07:00
|
|
|
return test_bit(property, device->propBitmask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-07-23 21:28:06 -07:00
|
|
|
int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
if (scanCode >= 0 && scanCode <= KEY_MAX) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-10 14:30:49 -07:00
|
|
|
if (device && !device->isVirtual() && test_bit(scanCode, device->keyBitmask)) {
|
2011-08-10 15:07:05 -07:00
|
|
|
uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
|
|
|
|
memset(keyState, 0, sizeof(keyState));
|
|
|
|
if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
|
|
|
|
return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
}
|
2010-07-14 18:48:53 -07:00
|
|
|
return AKEY_STATE_UNKNOWN;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-07-23 21:28:06 -07:00
|
|
|
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
|
|
|
|
AutoMutex _l(mLock);
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-10 14:30:49 -07:00
|
|
|
if (device && !device->isVirtual() && device->keyMap.haveKeyLayout()) {
|
2011-08-10 15:07:05 -07:00
|
|
|
Vector<int32_t> scanCodes;
|
|
|
|
device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes);
|
|
|
|
if (scanCodes.size() != 0) {
|
|
|
|
uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)];
|
|
|
|
memset(keyState, 0, sizeof(keyState));
|
|
|
|
if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) {
|
|
|
|
for (size_t i = 0; i < scanCodes.size(); i++) {
|
|
|
|
int32_t sc = scanCodes.itemAt(i);
|
|
|
|
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) {
|
|
|
|
return AKEY_STATE_DOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return AKEY_STATE_UP;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-07-14 18:48:53 -07:00
|
|
|
return AKEY_STATE_UNKNOWN;
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
}
|
|
|
|
|
2010-07-23 21:28:06 -07:00
|
|
|
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
if (sw >= 0 && sw <= SW_MAX) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-10 14:30:49 -07:00
|
|
|
if (device && !device->isVirtual() && test_bit(sw, device->swBitmask)) {
|
2011-08-10 15:07:05 -07:00
|
|
|
uint8_t swState[sizeof_bit_array(SW_MAX + 1)];
|
|
|
|
memset(swState, 0, sizeof(swState));
|
|
|
|
if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) {
|
|
|
|
return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
|
|
|
|
}
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
}
|
|
|
|
}
|
2010-07-14 18:48:53 -07:00
|
|
|
return AKEY_STATE_UNKNOWN;
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
}
|
|
|
|
|
2011-06-30 23:53:07 -07:00
|
|
|
status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const {
|
2011-08-11 17:10:06 -07:00
|
|
|
*outValue = 0;
|
|
|
|
|
2011-06-30 23:53:07 -07:00
|
|
|
if (axis >= 0 && axis <= ABS_MAX) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-10 14:30:49 -07:00
|
|
|
if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) {
|
2011-08-10 15:07:05 -07:00
|
|
|
struct input_absinfo info;
|
|
|
|
if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d",
|
2011-08-10 15:07:05 -07:00
|
|
|
axis, device->identifier.name.string(), device->fd, errno);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
*outValue = info.value;
|
|
|
|
return OK;
|
2011-06-30 23:53:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-07-23 21:28:06 -07:00
|
|
|
bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
|
|
|
|
const int32_t* keyCodes, uint8_t* outFlags) const {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2011-08-10 15:07:05 -07:00
|
|
|
if (device && device->keyMap.haveKeyLayout()) {
|
|
|
|
Vector<int32_t> scanCodes;
|
|
|
|
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
|
|
|
|
scanCodes.clear();
|
|
|
|
|
|
|
|
status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(
|
|
|
|
keyCodes[codeIndex], &scanCodes);
|
|
|
|
if (! err) {
|
|
|
|
// check the possible scan codes identified by the layout map against the
|
|
|
|
// map of codes actually emitted by the driver
|
|
|
|
for (size_t sc = 0; sc < scanCodes.size(); sc++) {
|
|
|
|
if (test_bit(scanCodes[sc], device->keyBitmask)) {
|
|
|
|
outFlags[codeIndex] = 1;
|
|
|
|
break;
|
|
|
|
}
|
2010-07-23 21:28:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-08-10 15:07:05 -07:00
|
|
|
return true;
|
2010-07-23 21:28:06 -07:00
|
|
|
}
|
2011-08-10 15:07:05 -07:00
|
|
|
return false;
|
2010-07-23 21:28:06 -07:00
|
|
|
}
|
|
|
|
|
2012-04-11 18:27:33 -07:00
|
|
|
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
|
|
|
|
int32_t* outKeycode, uint32_t* outFlags) const {
|
2009-07-14 12:06:54 -07:00
|
|
|
AutoMutex _l(mLock);
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-11 18:27:33 -07:00
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
if (device && device->keyMap.haveKeyLayout()) {
|
2012-04-11 18:27:33 -07:00
|
|
|
status_t err = device->keyMap.keyLayoutMap->mapKey(
|
|
|
|
scanCode, usageCode, outKeycode, outFlags);
|
2009-07-14 12:06:54 -07:00
|
|
|
if (err == NO_ERROR) {
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
2012-04-11 18:27:33 -07:00
|
|
|
|
2009-07-14 12:06:54 -07:00
|
|
|
*outKeycode = 0;
|
|
|
|
*outFlags = 0;
|
|
|
|
return NAME_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2012-04-11 18:27:33 -07:00
|
|
|
status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const {
|
2011-02-19 01:08:02 -08:00
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
|
|
|
|
if (device && device->keyMap.haveKeyLayout()) {
|
2012-04-11 18:27:33 -07:00
|
|
|
status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo);
|
2011-02-19 01:08:02 -08:00
|
|
|
if (err == NO_ERROR) {
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NAME_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2011-06-02 01:26:32 -07:00
|
|
|
void EventHub::setExcludedDevices(const Vector<String8>& devices) {
|
2010-10-01 17:46:21 -07:00
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
2011-06-02 01:26:32 -07:00
|
|
|
mExcludedDevices = devices;
|
2009-07-16 11:11:18 -04:00
|
|
|
}
|
|
|
|
|
2011-07-01 17:37:58 -07:00
|
|
|
bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
if (device && scanCode >= 0 && scanCode <= KEY_MAX) {
|
|
|
|
if (test_bit(scanCode, device->keyBitmask)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-09-12 17:55:08 -07:00
|
|
|
bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
|
|
|
|
AutoMutex _l(mLock);
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2011-06-14 17:09:25 -07:00
|
|
|
if (device && led >= 0 && led <= LED_MAX) {
|
|
|
|
if (test_bit(led, device->ledBitmask)) {
|
|
|
|
return true;
|
2010-09-12 17:55:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
|
|
|
|
AutoMutex _l(mLock);
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = getDeviceLocked(deviceId);
|
2012-04-10 14:30:49 -07:00
|
|
|
if (device && !device->isVirtual() && led >= 0 && led <= LED_MAX) {
|
2010-09-12 17:55:08 -07:00
|
|
|
struct input_event ev;
|
|
|
|
ev.time.tv_sec = 0;
|
|
|
|
ev.time.tv_usec = 0;
|
|
|
|
ev.type = EV_LED;
|
|
|
|
ev.code = led;
|
|
|
|
ev.value = on ? 1 : 0;
|
|
|
|
|
|
|
|
ssize_t nWrite;
|
|
|
|
do {
|
|
|
|
nWrite = write(device->fd, &ev, sizeof(struct input_event));
|
|
|
|
} while (nWrite == -1 && errno == EINTR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
void EventHub::getVirtualKeyDefinitions(int32_t deviceId,
|
|
|
|
Vector<VirtualKeyDefinition>& outVirtualKeys) const {
|
|
|
|
outVirtualKeys.clear();
|
|
|
|
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
if (device && device->virtualKeyMap) {
|
|
|
|
outVirtualKeys.appendVector(device->virtualKeyMap->getVirtualKeys());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-10 14:30:49 -07:00
|
|
|
sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
|
2011-11-15 17:48:10 -08:00
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
if (device) {
|
2012-04-17 16:52:41 -07:00
|
|
|
if (device->combinedKeyMap != NULL) {
|
|
|
|
return device->combinedKeyMap;
|
|
|
|
}
|
2012-04-10 14:30:49 -07:00
|
|
|
return device->keyMap.keyCharacterMap;
|
2011-11-15 17:48:10 -08:00
|
|
|
}
|
2012-04-10 14:30:49 -07:00
|
|
|
return NULL;
|
2011-11-15 17:48:10 -08:00
|
|
|
}
|
|
|
|
|
2012-04-17 16:52:41 -07:00
|
|
|
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
|
|
|
|
const sp<KeyCharacterMap>& map) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
if (device) {
|
|
|
|
if (map != device->overlayKeyMap) {
|
|
|
|
device->overlayKeyMap = map;
|
|
|
|
device->combinedKeyMap = KeyCharacterMap::combine(
|
|
|
|
device->keyMap.keyCharacterMap, map);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-04-13 04:09:27 -07:00
|
|
|
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
if (device && !device->isVirtual()) {
|
|
|
|
ff_effect effect;
|
|
|
|
memset(&effect, 0, sizeof(effect));
|
|
|
|
effect.type = FF_RUMBLE;
|
|
|
|
effect.id = device->ffEffectId;
|
|
|
|
effect.u.rumble.strong_magnitude = 0xc000;
|
|
|
|
effect.u.rumble.weak_magnitude = 0xc000;
|
|
|
|
effect.replay.length = (duration + 999999LL) / 1000000LL;
|
|
|
|
effect.replay.delay = 0;
|
|
|
|
if (ioctl(device->fd, EVIOCSFF, &effect)) {
|
|
|
|
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
|
|
|
|
device->identifier.name.string(), errno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
device->ffEffectId = effect.id;
|
|
|
|
|
|
|
|
struct input_event ev;
|
|
|
|
ev.time.tv_sec = 0;
|
|
|
|
ev.time.tv_usec = 0;
|
|
|
|
ev.type = EV_FF;
|
|
|
|
ev.code = device->ffEffectId;
|
|
|
|
ev.value = 1;
|
|
|
|
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
|
|
|
|
ALOGW("Could not start force feedback effect on device %s due to error %d.",
|
|
|
|
device->identifier.name.string(), errno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
device->ffEffectPlaying = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventHub::cancelVibrate(int32_t deviceId) {
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
Device* device = getDeviceLocked(deviceId);
|
|
|
|
if (device && !device->isVirtual()) {
|
|
|
|
if (device->ffEffectPlaying) {
|
|
|
|
device->ffEffectPlaying = false;
|
|
|
|
|
|
|
|
struct input_event ev;
|
|
|
|
ev.time.tv_sec = 0;
|
|
|
|
ev.time.tv_usec = 0;
|
|
|
|
ev.type = EV_FF;
|
|
|
|
ev.code = device->ffEffectId;
|
|
|
|
ev.value = 0;
|
|
|
|
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
|
|
|
|
ALOGW("Could not stop force feedback effect on device %s due to error %d.",
|
|
|
|
device->identifier.name.string(), errno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
|
2012-04-10 14:30:49 -07:00
|
|
|
if (deviceId == BUILT_IN_KEYBOARD_ID) {
|
2010-12-02 13:50:46 -08:00
|
|
|
deviceId = mBuiltInKeyboardId;
|
|
|
|
}
|
2011-06-14 17:09:25 -07:00
|
|
|
ssize_t index = mDevices.indexOfKey(deviceId);
|
|
|
|
return index >= 0 ? mDevices.valueAt(index) : NULL;
|
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const {
|
|
|
|
for (size_t i = 0; i < mDevices.size(); i++) {
|
|
|
|
Device* device = mDevices.valueAt(i);
|
|
|
|
if (device->path == devicePath) {
|
2010-12-02 13:50:46 -08:00
|
|
|
return device;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-03-18 18:14:26 -07:00
|
|
|
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
|
2012-01-09 18:35:44 +00:00
|
|
|
ALOG_ASSERT(bufferSize >= 1);
|
2009-07-16 11:11:18 -04:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
AutoMutex _l(mLock);
|
2009-07-16 11:11:18 -04:00
|
|
|
|
2011-03-18 18:14:26 -07:00
|
|
|
struct input_event readBuffer[bufferSize];
|
|
|
|
|
|
|
|
RawEvent* event = buffer;
|
|
|
|
size_t capacity = bufferSize;
|
2011-06-14 17:09:25 -07:00
|
|
|
bool awoken = false;
|
2010-08-17 16:48:25 -07:00
|
|
|
for (;;) {
|
2011-03-18 18:14:26 -07:00
|
|
|
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
|
|
|
|
2011-06-02 01:26:32 -07:00
|
|
|
// Reopen input devices if needed.
|
2011-06-14 17:09:25 -07:00
|
|
|
if (mNeedToReopenDevices) {
|
|
|
|
mNeedToReopenDevices = false;
|
2011-06-02 01:26:32 -07:00
|
|
|
|
2012-01-04 20:05:49 +00:00
|
|
|
ALOGI("Reopening all input devices due to a configuration change.");
|
2011-06-02 01:26:32 -07:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
closeAllDevicesLocked();
|
2011-06-02 01:26:32 -07:00
|
|
|
mNeedToScanDevices = true;
|
|
|
|
break; // return to the caller before we actually rescan
|
|
|
|
}
|
|
|
|
|
2010-08-17 16:48:25 -07:00
|
|
|
// Report any devices that had last been added/removed.
|
2011-03-18 18:14:26 -07:00
|
|
|
while (mClosingDevices) {
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = mClosingDevices;
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("Reporting device closed: id=%d, name=%s\n",
|
2009-03-03 19:31:44 -08:00
|
|
|
device->id, device->path.string());
|
|
|
|
mClosingDevices = device->next;
|
2011-03-18 18:14:26 -07:00
|
|
|
event->when = now;
|
2012-04-10 14:30:49 -07:00
|
|
|
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
|
2011-03-18 18:14:26 -07:00
|
|
|
event->type = DEVICE_REMOVED;
|
|
|
|
event += 1;
|
2009-03-03 19:31:44 -08:00
|
|
|
delete device;
|
2010-10-01 18:55:43 -07:00
|
|
|
mNeedToSendFinishedDeviceScan = true;
|
2011-03-18 18:14:26 -07:00
|
|
|
if (--capacity == 0) {
|
|
|
|
break;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2010-07-23 21:28:06 -07:00
|
|
|
|
2011-06-02 01:26:32 -07:00
|
|
|
if (mNeedToScanDevices) {
|
|
|
|
mNeedToScanDevices = false;
|
2011-06-14 17:09:25 -07:00
|
|
|
scanDevicesLocked();
|
2011-06-02 01:26:32 -07:00
|
|
|
mNeedToSendFinishedDeviceScan = true;
|
|
|
|
}
|
|
|
|
|
2011-03-18 18:14:26 -07:00
|
|
|
while (mOpeningDevices != NULL) {
|
2010-12-02 13:50:46 -08:00
|
|
|
Device* device = mOpeningDevices;
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("Reporting device opened: id=%d, name=%s\n",
|
2009-03-03 19:31:44 -08:00
|
|
|
device->id, device->path.string());
|
|
|
|
mOpeningDevices = device->next;
|
2011-03-18 18:14:26 -07:00
|
|
|
event->when = now;
|
|
|
|
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
|
|
|
|
event->type = DEVICE_ADDED;
|
|
|
|
event += 1;
|
2010-10-01 18:55:43 -07:00
|
|
|
mNeedToSendFinishedDeviceScan = true;
|
2011-03-18 18:14:26 -07:00
|
|
|
if (--capacity == 0) {
|
|
|
|
break;
|
|
|
|
}
|
2010-10-01 18:55:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mNeedToSendFinishedDeviceScan) {
|
|
|
|
mNeedToSendFinishedDeviceScan = false;
|
2011-03-18 18:14:26 -07:00
|
|
|
event->when = now;
|
|
|
|
event->type = FINISHED_DEVICE_SCAN;
|
|
|
|
event += 1;
|
|
|
|
if (--capacity == 0) {
|
|
|
|
break;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-08-17 16:48:25 -07:00
|
|
|
// Grab the next input event.
|
2011-06-14 17:09:25 -07:00
|
|
|
bool deviceChanged = false;
|
|
|
|
while (mPendingEventIndex < mPendingEventCount) {
|
|
|
|
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
|
|
|
|
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
|
|
|
|
if (eventItem.events & EPOLLIN) {
|
|
|
|
mPendingINotify = true;
|
|
|
|
} else {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
|
2011-06-14 17:09:25 -07:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
|
|
|
|
if (eventItem.events & EPOLLIN) {
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("awoken after wake()");
|
2011-06-14 17:09:25 -07:00
|
|
|
awoken = true;
|
|
|
|
char buffer[16];
|
|
|
|
ssize_t nRead;
|
|
|
|
do {
|
|
|
|
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
|
|
|
|
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
|
|
|
|
} else {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
|
2011-06-14 17:09:25 -07:00
|
|
|
eventItem.events);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
|
|
|
|
if (deviceIndex < 0) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
|
2011-06-14 17:09:25 -07:00
|
|
|
eventItem.events, eventItem.data.u32);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Device* device = mDevices.valueAt(deviceIndex);
|
|
|
|
if (eventItem.events & EPOLLIN) {
|
|
|
|
int32_t readSize = read(device->fd, readBuffer,
|
|
|
|
sizeof(struct input_event) * capacity);
|
|
|
|
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
|
|
|
|
// Device was removed before INotify noticed.
|
2011-10-05 11:14:13 -07:00
|
|
|
ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d "
|
|
|
|
"capacity: %d errno: %d)\n",
|
|
|
|
device->fd, readSize, bufferSize, capacity, errno);
|
2011-06-14 17:09:25 -07:00
|
|
|
deviceChanged = true;
|
|
|
|
closeDeviceLocked(device);
|
|
|
|
} else if (readSize < 0) {
|
2010-08-17 16:48:25 -07:00
|
|
|
if (errno != EAGAIN && errno != EINTR) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("could not get event (errno=%d)", errno);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2010-08-17 16:48:25 -07:00
|
|
|
} else if ((readSize % sizeof(struct input_event)) != 0) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("could not get event (wrong size: %d)", readSize);
|
2010-08-17 16:48:25 -07:00
|
|
|
} else {
|
2011-03-18 18:14:26 -07:00
|
|
|
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
|
|
|
|
|
|
|
|
size_t count = size_t(readSize) / sizeof(struct input_event);
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
const struct input_event& iev = readBuffer[i];
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
|
2011-03-18 18:14:26 -07:00
|
|
|
device->path.string(),
|
|
|
|
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
|
|
|
|
iev.type, iev.code, iev.value);
|
|
|
|
|
2011-04-07 11:38:09 -07:00
|
|
|
#ifdef HAVE_POSIX_CLOCKS
|
|
|
|
// Use the time specified in the event instead of the current time
|
|
|
|
// so that downstream code can get more accurate estimates of
|
|
|
|
// event dispatch latency from the time the event is enqueued onto
|
|
|
|
// the evdev client buffer.
|
|
|
|
//
|
|
|
|
// The event's timestamp fortuitously uses the same monotonic clock
|
|
|
|
// time base as the rest of Android. The kernel event device driver
|
|
|
|
// (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
|
|
|
|
// The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
|
|
|
|
// calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
|
|
|
|
// system call that also queries ktime_get_ts().
|
|
|
|
event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
|
|
|
|
+ nsecs_t(iev.time.tv_usec) * 1000LL;
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("event time %lld, now %lld", event->when, now);
|
2011-04-07 11:38:09 -07:00
|
|
|
#else
|
2011-03-18 18:14:26 -07:00
|
|
|
event->when = now;
|
2011-04-07 11:38:09 -07:00
|
|
|
#endif
|
2011-03-18 18:14:26 -07:00
|
|
|
event->deviceId = deviceId;
|
|
|
|
event->type = iev.type;
|
2012-04-11 18:27:33 -07:00
|
|
|
event->code = iev.code;
|
2011-03-18 18:14:26 -07:00
|
|
|
event->value = iev.value;
|
|
|
|
event += 1;
|
|
|
|
}
|
|
|
|
capacity -= count;
|
|
|
|
if (capacity == 0) {
|
2011-06-14 17:09:25 -07:00
|
|
|
// The result buffer is full. Reset the pending event index
|
|
|
|
// so we will try to read the device again on the next iteration.
|
|
|
|
mPendingEventIndex -= 1;
|
2011-03-18 18:14:26 -07:00
|
|
|
break;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2012-04-12 17:32:48 -07:00
|
|
|
} else if (eventItem.events & EPOLLHUP) {
|
|
|
|
ALOGI("Removing device %s due to epoll hang-up event.",
|
|
|
|
device->identifier.name.string());
|
|
|
|
deviceChanged = true;
|
|
|
|
closeDeviceLocked(device);
|
2011-06-14 17:09:25 -07:00
|
|
|
} else {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
|
2011-06-14 17:09:25 -07:00
|
|
|
eventItem.events, device->identifier.name.string());
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
}
|
2010-08-17 16:48:25 -07:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// readNotify() will modify the list of devices so this must be done after
|
|
|
|
// processing all other events to ensure that we read all remaining events
|
|
|
|
// before closing the devices.
|
|
|
|
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
|
|
|
|
mPendingINotify = false;
|
|
|
|
readNotifyLocked();
|
|
|
|
deviceChanged = true;
|
2011-02-24 20:55:35 -08:00
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// Report added or removed devices immediately.
|
|
|
|
if (deviceChanged) {
|
|
|
|
continue;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2010-10-14 02:23:43 -07:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// Return now if we have collected any events or if we were explicitly awoken.
|
|
|
|
if (event != buffer || awoken) {
|
2011-03-18 18:14:26 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-08-17 16:48:25 -07:00
|
|
|
// Poll for events. Mind the wake lock dance!
|
2011-06-14 17:09:25 -07:00
|
|
|
// We hold a wake lock at all times except during epoll_wait(). This works due to some
|
2010-08-17 16:48:25 -07:00
|
|
|
// 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 EventHub 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.
|
2011-03-17 01:34:19 -07:00
|
|
|
//
|
|
|
|
// The timeout is advisory only. If the device is asleep, it will not wake just to
|
|
|
|
// service the timeout.
|
2011-06-14 17:09:25 -07:00
|
|
|
mPendingEventIndex = 0;
|
|
|
|
|
|
|
|
mLock.unlock(); // release lock before poll, must be before release_wake_lock
|
2010-08-17 16:48:25 -07:00
|
|
|
release_wake_lock(WAKE_LOCK_ID);
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
|
2010-08-17 16:48:25 -07:00
|
|
|
|
|
|
|
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
|
2011-06-14 17:09:25 -07:00
|
|
|
mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
|
2010-08-17 16:48:25 -07:00
|
|
|
|
2011-03-17 01:34:19 -07:00
|
|
|
if (pollResult == 0) {
|
2011-06-14 17:09:25 -07:00
|
|
|
// Timed out.
|
|
|
|
mPendingEventCount = 0;
|
|
|
|
break;
|
2011-03-17 01:34:19 -07:00
|
|
|
}
|
2011-06-14 17:09:25 -07:00
|
|
|
|
2011-03-17 01:34:19 -07:00
|
|
|
if (pollResult < 0) {
|
2011-06-14 17:09:25 -07:00
|
|
|
// An error occurred.
|
|
|
|
mPendingEventCount = 0;
|
|
|
|
|
2011-03-18 18:14:26 -07:00
|
|
|
// Sleep after errors to avoid locking up the system.
|
|
|
|
// Hopefully the error is transient.
|
2010-08-17 16:48:25 -07:00
|
|
|
if (errno != EINTR) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("poll failed (errno=%d)\n", errno);
|
2010-08-17 16:48:25 -07:00
|
|
|
usleep(100000);
|
|
|
|
}
|
2011-03-18 18:14:26 -07:00
|
|
|
} else {
|
2011-06-14 17:09:25 -07:00
|
|
|
// Some events occurred.
|
|
|
|
mPendingEventCount = size_t(pollResult);
|
2010-08-17 16:48:25 -07:00
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2011-03-18 18:14:26 -07:00
|
|
|
|
|
|
|
// All done, return the number of events we read.
|
|
|
|
return event - buffer;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
void EventHub::wake() {
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("wake() called");
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
ssize_t nWrite;
|
|
|
|
do {
|
|
|
|
nWrite = write(mWakeWritePipeFd, "W", 1);
|
|
|
|
} while (nWrite == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
if (nWrite != 1 && errno != EAGAIN) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("Could not write wake signal, errno=%d", errno);
|
2011-06-14 17:09:25 -07:00
|
|
|
}
|
2011-06-02 01:26:32 -07:00
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
void EventHub::scanDevicesLocked() {
|
|
|
|
status_t res = scanDirLocked(DEVICE_PATH);
|
2009-03-03 19:31:44 -08:00
|
|
|
if(res < 0) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("scan dir failed for %s\n", DEVICE_PATH);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2012-04-10 14:30:49 -07:00
|
|
|
if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
|
|
|
|
createVirtualKeyboardLocked();
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2010-06-30 16:10:35 -07:00
|
|
|
static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
|
|
|
|
const uint8_t* end = array + endIndex;
|
|
|
|
array += startIndex;
|
|
|
|
while (array != end) {
|
|
|
|
if (*(array++) != 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const int32_t GAMEPAD_KEYCODES[] = {
|
|
|
|
AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
|
|
|
|
AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
|
|
|
|
AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
|
|
|
|
AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
|
|
|
|
AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
|
2011-01-15 18:14:15 -08:00
|
|
|
AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
|
|
|
|
AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4,
|
|
|
|
AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8,
|
|
|
|
AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12,
|
|
|
|
AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16,
|
2010-06-30 16:10:35 -07:00
|
|
|
};
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t EventHub::openDeviceLocked(const char *devicePath) {
|
2010-12-02 13:50:46 -08:00
|
|
|
char buffer[80];
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("Opening device: %s", devicePath);
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2012-01-19 14:32:47 -08:00
|
|
|
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
|
2009-03-03 19:31:44 -08:00
|
|
|
if(fd < 0) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
|
2009-03-03 19:31:44 -08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
InputDeviceIdentifier identifier;
|
|
|
|
|
|
|
|
// Get device name.
|
|
|
|
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
|
|
|
|
//fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
|
|
|
|
} else {
|
|
|
|
buffer[sizeof(buffer) - 1] = '\0';
|
|
|
|
identifier.name.setTo(buffer);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
// Check to see if the device is on our excluded list
|
2011-06-02 01:26:32 -07:00
|
|
|
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
|
|
|
|
const String8& item = mExcludedDevices.itemAt(i);
|
|
|
|
if (identifier.name == item) {
|
2012-01-04 20:05:49 +00:00
|
|
|
ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
|
2009-07-16 11:11:18 -04:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
// Get device driver version.
|
|
|
|
int driverVersion;
|
|
|
|
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
|
2010-08-17 16:48:25 -07:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
// Get device identifier.
|
|
|
|
struct input_id inputId;
|
|
|
|
if(ioctl(fd, EVIOCGID, &inputId)) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
|
2010-12-02 13:50:46 -08:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
identifier.bus = inputId.bustype;
|
|
|
|
identifier.product = inputId.product;
|
|
|
|
identifier.vendor = inputId.vendor;
|
|
|
|
identifier.version = inputId.version;
|
|
|
|
|
|
|
|
// Get device physical location.
|
|
|
|
if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
|
|
|
|
//fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
|
|
|
|
} else {
|
|
|
|
buffer[sizeof(buffer) - 1] = '\0';
|
|
|
|
identifier.location.setTo(buffer);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
// Get device unique id.
|
|
|
|
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
|
|
|
|
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
|
|
|
|
} else {
|
|
|
|
buffer[sizeof(buffer) - 1] = '\0';
|
|
|
|
identifier.uniqueId.setTo(buffer);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2012-04-10 14:30:49 -07:00
|
|
|
// Fill in the descriptor.
|
|
|
|
setDescriptor(identifier);
|
2012-04-06 14:51:01 -07:00
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
// Make file descriptor non-blocking for use with poll().
|
|
|
|
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("Error %d making device file descriptor non-blocking.", errno);
|
2010-12-02 13:50:46 -08:00
|
|
|
close(fd);
|
2009-03-03 19:31:44 -08:00
|
|
|
return -1;
|
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
|
|
|
|
// 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);
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2012-04-06 14:51:01 -07:00
|
|
|
ALOGV("add device %d: %s\n", deviceId, devicePath);
|
|
|
|
ALOGV(" bus: %04x\n"
|
|
|
|
" vendor %04x\n"
|
|
|
|
" product %04x\n"
|
|
|
|
" version %04x\n",
|
2010-12-02 13:50:46 -08:00
|
|
|
identifier.bus, identifier.vendor, identifier.product, identifier.version);
|
2012-04-06 14:51:01 -07:00
|
|
|
ALOGV(" name: \"%s\"\n", identifier.name.string());
|
|
|
|
ALOGV(" location: \"%s\"\n", identifier.location.string());
|
|
|
|
ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
|
2012-04-11 18:27:33 -07:00
|
|
|
ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
|
2012-04-06 14:51:01 -07:00
|
|
|
ALOGV(" driver: v%d.%d.%d\n",
|
2010-12-02 13:50:46 -08:00
|
|
|
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2010-11-29 17:37:49 -08:00
|
|
|
// Load the configuration file for the device.
|
2011-06-14 17:09:25 -07:00
|
|
|
loadConfigurationLocked(device);
|
2010-11-29 17:37:49 -08:00
|
|
|
|
2010-06-30 16:10:35 -07:00
|
|
|
// Figure out the kinds of events the device reports.
|
2011-06-14 17:09:25 -07:00
|
|
|
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
|
|
|
|
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
|
|
|
|
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
|
|
|
|
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
|
|
|
|
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
|
2012-04-13 04:09:27 -07:00
|
|
|
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
|
2011-06-14 17:09:25 -07:00
|
|
|
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
|
2011-05-24 01:07:44 -07:00
|
|
|
|
2011-02-19 01:08:02 -08:00
|
|
|
// See if this is a keyboard. Ignore everything in the button range except for
|
|
|
|
// joystick and gamepad buttons which are handled like keyboards for the most part.
|
2011-06-14 17:09:25 -07:00
|
|
|
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|
|
|
|
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
|
2011-02-19 01:08:02 -08:00
|
|
|
sizeof_bit_array(KEY_MAX + 1));
|
2011-06-14 17:09:25 -07:00
|
|
|
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
|
2011-03-03 03:39:29 -08:00
|
|
|
sizeof_bit_array(BTN_MOUSE))
|
2011-06-14 17:09:25 -07:00
|
|
|
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
|
2011-03-03 03:39:29 -08:00
|
|
|
sizeof_bit_array(BTN_DIGI));
|
2011-02-19 01:08:02 -08:00
|
|
|
if (haveKeyboardKeys || haveGamepadButtons) {
|
|
|
|
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2011-02-19 01:08:02 -08:00
|
|
|
|
2010-12-23 17:50:18 -08:00
|
|
|
// See if this is a cursor device such as a trackball or mouse.
|
2011-06-14 17:09:25 -07:00
|
|
|
if (test_bit(BTN_MOUSE, device->keyBitmask)
|
|
|
|
&& test_bit(REL_X, device->relBitmask)
|
|
|
|
&& test_bit(REL_Y, device->relBitmask)) {
|
2011-02-19 01:08:02 -08:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2010-06-30 16:10:35 -07:00
|
|
|
|
|
|
|
// See if this is a touch pad.
|
2011-02-19 01:08:02 -08:00
|
|
|
// Is this a new modern multi-touch driver?
|
2011-06-14 17:09:25 -07:00
|
|
|
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
|
|
|
|
&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
|
2011-02-19 01:08:02 -08:00
|
|
|
// Some joysticks such as the PS3 controller report axes that conflict
|
|
|
|
// with the ABS_MT range. Try to confirm that the device really is
|
|
|
|
// a touch screen.
|
2011-06-14 17:09:25 -07:00
|
|
|
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
|
2011-01-25 16:02:22 -08:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
|
2010-06-30 16:10:35 -07:00
|
|
|
}
|
2011-02-19 01:08:02 -08:00
|
|
|
// Is this an old style single-touch driver?
|
2011-06-14 17:09:25 -07:00
|
|
|
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
|
|
|
|
&& test_bit(ABS_X, device->absBitmask)
|
|
|
|
&& test_bit(ABS_Y, device->absBitmask)) {
|
2011-02-19 01:08:02 -08:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2011-03-03 03:39:29 -08:00
|
|
|
// See if this device is a joystick.
|
|
|
|
// Assumes that joysticks always have gamepad buttons in order to distinguish them
|
|
|
|
// from other devices such as accelerometers that also have absolute axes.
|
2011-08-31 12:56:34 -07:00
|
|
|
if (haveGamepadButtons) {
|
|
|
|
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
|
|
|
|
for (int i = 0; i <= ABS_MAX; i++) {
|
|
|
|
if (test_bit(i, device->absBitmask)
|
|
|
|
&& (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
|
|
|
|
device->classes = assumedClasses;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-03-03 03:39:29 -08:00
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// Check whether this device has switches.
|
|
|
|
for (int i = 0; i <= SW_MAX; i++) {
|
|
|
|
if (test_bit(i, device->swBitmask)) {
|
|
|
|
device->classes |= INPUT_DEVICE_CLASS_SWITCH;
|
|
|
|
break;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-13 04:09:27 -07:00
|
|
|
// Check whether this device supports the vibrator.
|
|
|
|
if (test_bit(FF_RUMBLE, device->ffBitmask)) {
|
|
|
|
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// Configure virtual keys.
|
2011-01-25 16:02:22 -08:00
|
|
|
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
|
2010-12-02 13:50:46 -08:00
|
|
|
// Load the virtual keys for the touch screen, if any.
|
|
|
|
// We do this now so that we can make sure to load the keymap if necessary.
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t status = loadVirtualKeyMapLocked(device);
|
2010-12-02 13:50:46 -08:00
|
|
|
if (!status) {
|
|
|
|
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
|
2010-09-12 17:55:08 -07:00
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
}
|
|
|
|
|
2011-03-03 03:39:29 -08:00
|
|
|
// Load the key map.
|
|
|
|
// We need to do this for joysticks too because the key layout may specify axes.
|
|
|
|
status_t keyMapStatus = NAME_NOT_FOUND;
|
|
|
|
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
|
2010-12-02 13:50:46 -08:00
|
|
|
// Load the keymap for the device.
|
2011-06-14 17:09:25 -07:00
|
|
|
keyMapStatus = loadKeyMapLocked(device);
|
2011-03-03 03:39:29 -08:00
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
|
2011-03-03 03:39:29 -08:00
|
|
|
// Configure the keyboard, gamepad or virtual keyboard.
|
|
|
|
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
|
2010-12-02 13:50:46 -08:00
|
|
|
// Register the keyboard as a built-in keyboard if it is eligible.
|
2011-03-03 03:39:29 -08:00
|
|
|
if (!keyMapStatus
|
2012-04-10 14:30:49 -07:00
|
|
|
&& mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
|
2010-12-02 13:50:46 -08:00
|
|
|
&& isEligibleBuiltInKeyboard(device->identifier,
|
|
|
|
device->configuration, &device->keyMap)) {
|
|
|
|
mBuiltInKeyboardId = device->id;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2009-08-04 05:49:43 -07:00
|
|
|
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
|
2010-10-01 17:46:21 -07:00
|
|
|
if (hasKeycodeLocked(device, AKEYCODE_Q)) {
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
|
2009-08-04 05:49:43 -07:00
|
|
|
}
|
2010-09-12 17:55:08 -07:00
|
|
|
|
2010-06-30 16:10:35 -07:00
|
|
|
// See if this device has a DPAD.
|
2010-10-01 17:46:21 -07:00
|
|
|
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
|
|
|
|
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
|
|
|
|
hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
|
|
|
|
hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
|
|
|
|
hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
|
Native input dispatch rewrite work in progress.
The old dispatch mechanism has been left in place and continues to
be used by default for now. To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.
Includes part of the new input event NDK API. Some details TBD.
To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument. The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points. The ViewRoot then
provides the InputChannel to the InputQueue. Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue. This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.
There can be zero or more targets for any given input event. Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets). Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.
End-to-end event dispatch mostly works!
To do: event injection, rate limiting, ANRs, testing, optimization, etc.
Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
2010-04-22 18:58:52 -07:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_DPAD;
|
2009-08-04 05:49:43 -07:00
|
|
|
}
|
2010-09-12 17:55:08 -07:00
|
|
|
|
2010-06-30 16:10:35 -07:00
|
|
|
// See if this device has a gamepad.
|
2010-10-21 15:46:03 -07:00
|
|
|
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
|
2010-10-01 17:46:21 -07:00
|
|
|
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
|
2010-06-30 16:10:35 -07:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2010-06-23 16:00:37 +07:00
|
|
|
// If the device isn't recognized as something we handle, don't monitor it.
|
|
|
|
if (device->classes == 0) {
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("Dropping device: id=%d, path='%s', name='%s'",
|
2010-12-02 13:50:46 -08:00
|
|
|
deviceId, devicePath, device->identifier.name.string());
|
2010-06-23 16:00:37 +07:00
|
|
|
delete device;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-03-02 19:23:13 -08:00
|
|
|
// Determine whether the device is external or internal.
|
2011-06-14 17:09:25 -07:00
|
|
|
if (isExternalDeviceLocked(device)) {
|
2011-03-02 19:23:13 -08:00
|
|
|
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
// Register with epoll.
|
|
|
|
struct epoll_event eventItem;
|
|
|
|
memset(&eventItem, 0, sizeof(eventItem));
|
|
|
|
eventItem.events = EPOLLIN;
|
|
|
|
eventItem.data.u32 = deviceId;
|
|
|
|
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
|
2011-06-14 17:09:25 -07:00
|
|
|
delete device;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-12-16 13:45:40 -08:00
|
|
|
// Enable wake-lock behavior on kernels that support it.
|
|
|
|
// TODO: Only need this for devices that can really wake the system.
|
2012-04-06 19:31:36 -07:00
|
|
|
bool usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1);
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, CLOCK_MONOTONIC);
|
2011-12-16 13:45:40 -08:00
|
|
|
|
2012-01-04 20:05:49 +00:00
|
|
|
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
|
2011-12-16 13:45:40 -08:00
|
|
|
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
|
2012-04-06 19:31:36 -07:00
|
|
|
"usingSuspendBlockIoctl=%s, usingClockIoctl=%s",
|
2010-12-02 13:50:46 -08:00
|
|
|
deviceId, fd, devicePath, device->identifier.name.string(),
|
|
|
|
device->classes,
|
|
|
|
device->configurationFile.string(),
|
|
|
|
device->keyMap.keyLayoutFile.string(),
|
|
|
|
device->keyMap.keyCharacterMapFile.string(),
|
2011-12-16 13:45:40 -08:00
|
|
|
toString(mBuiltInKeyboardId == deviceId),
|
2012-04-06 19:31:36 -07:00
|
|
|
toString(usingSuspendBlockIoctl), toString(usingClockIoctl));
|
2010-12-02 13:50:46 -08:00
|
|
|
|
2012-04-10 14:30:49 -07:00
|
|
|
addDeviceLocked(device);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2012-04-10 14:30:49 -07:00
|
|
|
void EventHub::createVirtualKeyboardLocked() {
|
|
|
|
InputDeviceIdentifier identifier;
|
|
|
|
identifier.name = "Virtual";
|
|
|
|
identifier.uniqueId = "<virtual>";
|
|
|
|
setDescriptor(identifier);
|
|
|
|
|
|
|
|
Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier);
|
|
|
|
device->classes = INPUT_DEVICE_CLASS_KEYBOARD
|
|
|
|
| INPUT_DEVICE_CLASS_ALPHAKEY
|
|
|
|
| INPUT_DEVICE_CLASS_DPAD
|
|
|
|
| INPUT_DEVICE_CLASS_VIRTUAL;
|
|
|
|
loadKeyMapLocked(device);
|
|
|
|
addDeviceLocked(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventHub::addDeviceLocked(Device* device) {
|
|
|
|
mDevices.add(device->id, device);
|
2009-03-03 19:31:44 -08:00
|
|
|
device->next = mOpeningDevices;
|
|
|
|
mOpeningDevices = device;
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
void EventHub::loadConfigurationLocked(Device* device) {
|
2010-12-02 13:50:46 -08:00
|
|
|
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
|
|
|
|
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
|
2010-11-29 17:37:49 -08:00
|
|
|
if (device->configurationFile.isEmpty()) {
|
2011-12-20 16:23:08 +00:00
|
|
|
ALOGD("No input device configuration file found for device '%s'.",
|
2010-12-02 13:50:46 -08:00
|
|
|
device->identifier.name.string());
|
2010-11-29 17:37:49 -08:00
|
|
|
} else {
|
|
|
|
status_t status = PropertyMap::load(device->configurationFile,
|
|
|
|
&device->configuration);
|
|
|
|
if (status) {
|
2012-01-06 19:20:56 +00:00
|
|
|
ALOGE("Error loading input device configuration file for device '%s'. "
|
2010-12-02 13:50:46 -08:00
|
|
|
"Using default configuration.",
|
|
|
|
device->identifier.name.string());
|
2010-11-29 17:37:49 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
|
2010-12-02 13:50:46 -08:00
|
|
|
// The virtual key map is supplied by the kernel as a system board property file.
|
|
|
|
String8 path;
|
|
|
|
path.append("/sys/board_properties/virtualkeys.");
|
|
|
|
path.append(device->identifier.name);
|
|
|
|
if (access(path.string(), R_OK)) {
|
|
|
|
return NAME_NOT_FOUND;
|
|
|
|
}
|
|
|
|
return VirtualKeyMap::load(path, &device->virtualKeyMap);
|
2010-09-12 17:55:08 -07:00
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t EventHub::loadKeyMapLocked(Device* device) {
|
2010-12-02 13:50:46 -08:00
|
|
|
return device->keyMap.load(device->identifier, device->configuration);
|
2010-09-12 17:55:08 -07:00
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
bool EventHub::isExternalDeviceLocked(Device* device) {
|
2011-03-02 19:23:13 -08:00
|
|
|
if (device->configuration) {
|
|
|
|
bool value;
|
2011-08-30 14:35:45 -07:00
|
|
|
if (device->configuration->tryGetProperty(String8("device.internal"), value)) {
|
|
|
|
return !value;
|
2011-03-02 19:23:13 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH;
|
|
|
|
}
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
bool EventHub::hasKeycodeLocked(Device* device, int keycode) const {
|
|
|
|
if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) {
|
2009-08-04 05:49:43 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector<int32_t> scanCodes;
|
2011-02-19 01:08:02 -08:00
|
|
|
device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes);
|
2009-08-04 05:49:43 -07:00
|
|
|
const size_t N = scanCodes.size();
|
|
|
|
for (size_t i=0; i<N && i<=KEY_MAX; i++) {
|
|
|
|
int32_t sc = scanCodes.itemAt(i);
|
|
|
|
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t EventHub::closeDeviceByPathLocked(const char *devicePath) {
|
|
|
|
Device* device = getDeviceByPathLocked(devicePath);
|
|
|
|
if (device) {
|
|
|
|
closeDeviceLocked(device);
|
|
|
|
return 0;
|
2011-02-24 20:55:35 -08:00
|
|
|
}
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("Remove device: %s not found, device may already have been removed.", devicePath);
|
2011-02-24 20:55:35 -08:00
|
|
|
return -1;
|
|
|
|
}
|
2010-12-02 13:50:46 -08:00
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
void EventHub::closeAllDevicesLocked() {
|
|
|
|
while (mDevices.size() > 0) {
|
|
|
|
closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventHub::closeDeviceLocked(Device* device) {
|
2012-01-04 20:05:49 +00:00
|
|
|
ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n",
|
2011-02-24 20:55:35 -08:00
|
|
|
device->path.string(), device->identifier.name.string(), device->id,
|
|
|
|
device->fd, device->classes);
|
2010-12-02 13:50:46 -08:00
|
|
|
|
2011-02-24 20:55:35 -08:00
|
|
|
if (device->id == mBuiltInKeyboardId) {
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
|
2011-02-24 20:55:35 -08:00
|
|
|
device->path.string(), mBuiltInKeyboardId);
|
2012-04-10 14:30:49 -07:00
|
|
|
mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD;
|
2011-02-24 20:55:35 -08:00
|
|
|
}
|
|
|
|
|
2012-04-10 14:30:49 -07:00
|
|
|
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);
|
|
|
|
}
|
2011-06-14 17:09:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
mDevices.removeItem(device->id);
|
2011-02-24 20:55:35 -08:00
|
|
|
device->close();
|
|
|
|
|
2011-03-12 19:46:59 -08:00
|
|
|
// Unlink for opening devices list if it is present.
|
|
|
|
Device* pred = NULL;
|
|
|
|
bool found = false;
|
|
|
|
for (Device* entry = mOpeningDevices; entry != NULL; ) {
|
|
|
|
if (entry == device) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pred = entry;
|
|
|
|
entry = entry->next;
|
|
|
|
}
|
|
|
|
if (found) {
|
|
|
|
// Unlink the device from the opening devices list then delete it.
|
|
|
|
// We don't need to tell the client that the device was closed because
|
|
|
|
// it does not even know it was opened in the first place.
|
2012-01-04 20:05:49 +00:00
|
|
|
ALOGI("Device %s was immediately closed after opening.", device->path.string());
|
2011-03-12 19:46:59 -08:00
|
|
|
if (pred) {
|
|
|
|
pred->next = device->next;
|
|
|
|
} else {
|
|
|
|
mOpeningDevices = device->next;
|
|
|
|
}
|
|
|
|
delete device;
|
|
|
|
} else {
|
|
|
|
// Link into closing devices list.
|
|
|
|
// The device will be deleted later after we have informed the client.
|
|
|
|
device->next = mClosingDevices;
|
|
|
|
mClosingDevices = device;
|
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t EventHub::readNotifyLocked() {
|
2009-03-03 19:31:44 -08:00
|
|
|
int res;
|
|
|
|
char devname[PATH_MAX];
|
|
|
|
char *filename;
|
|
|
|
char event_buf[512];
|
|
|
|
int event_size;
|
|
|
|
int event_pos = 0;
|
|
|
|
struct inotify_event *event;
|
|
|
|
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
|
2011-06-14 17:09:25 -07:00
|
|
|
res = read(mINotifyFd, event_buf, sizeof(event_buf));
|
2009-03-03 19:31:44 -08:00
|
|
|
if(res < (int)sizeof(*event)) {
|
|
|
|
if(errno == EINTR)
|
|
|
|
return 0;
|
2012-01-05 23:22:43 +00:00
|
|
|
ALOGW("could not get event, %s\n", strerror(errno));
|
2011-06-14 17:09:25 -07:00
|
|
|
return -1;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
//printf("got %d bytes of event information\n", res);
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
strcpy(devname, DEVICE_PATH);
|
2009-03-03 19:31:44 -08:00
|
|
|
filename = devname + strlen(devname);
|
|
|
|
*filename++ = '/';
|
|
|
|
|
|
|
|
while(res >= (int)sizeof(*event)) {
|
|
|
|
event = (struct inotify_event *)(event_buf + event_pos);
|
|
|
|
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
|
|
|
|
if(event->len) {
|
|
|
|
strcpy(filename, event->name);
|
|
|
|
if(event->mask & IN_CREATE) {
|
2011-06-14 17:09:25 -07:00
|
|
|
openDeviceLocked(devname);
|
|
|
|
} else {
|
2012-01-04 20:05:49 +00:00
|
|
|
ALOGI("Removing device '%s' due to inotify event\n", devname);
|
2011-06-14 17:09:25 -07:00
|
|
|
closeDeviceByPathLocked(devname);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
event_size = sizeof(*event) + event->len;
|
|
|
|
res -= event_size;
|
|
|
|
event_pos += event_size;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
status_t EventHub::scanDirLocked(const char *dirname)
|
2009-03-03 19:31:44 -08:00
|
|
|
{
|
|
|
|
char devname[PATH_MAX];
|
|
|
|
char *filename;
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *de;
|
|
|
|
dir = opendir(dirname);
|
|
|
|
if(dir == NULL)
|
|
|
|
return -1;
|
|
|
|
strcpy(devname, dirname);
|
|
|
|
filename = devname + strlen(devname);
|
|
|
|
*filename++ = '/';
|
|
|
|
while((de = readdir(dir))) {
|
|
|
|
if(de->d_name[0] == '.' &&
|
|
|
|
(de->d_name[1] == '\0' ||
|
|
|
|
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
|
|
|
|
continue;
|
|
|
|
strcpy(filename, de->d_name);
|
2011-06-14 17:09:25 -07:00
|
|
|
openDeviceLocked(devname);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
void EventHub::requestReopenDevices() {
|
2011-10-20 11:56:00 +01:00
|
|
|
ALOGV("requestReopenDevices() called");
|
2011-06-14 17:09:25 -07:00
|
|
|
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
mNeedToReopenDevices = true;
|
2011-06-02 01:26:32 -07:00
|
|
|
}
|
|
|
|
|
2010-10-01 17:46:21 -07:00
|
|
|
void EventHub::dump(String8& dump) {
|
|
|
|
dump.append("Event Hub State:\n");
|
|
|
|
|
|
|
|
{ // acquire lock
|
|
|
|
AutoMutex _l(mLock);
|
|
|
|
|
2010-12-02 13:50:46 -08:00
|
|
|
dump.appendFormat(INDENT "BuiltInKeyboardId: %d\n", mBuiltInKeyboardId);
|
2010-10-01 17:46:21 -07:00
|
|
|
|
|
|
|
dump.append(INDENT "Devices:\n");
|
|
|
|
|
2011-06-14 17:09:25 -07:00
|
|
|
for (size_t i = 0; i < mDevices.size(); i++) {
|
|
|
|
const Device* device = mDevices.valueAt(i);
|
|
|
|
if (mBuiltInKeyboardId == device->id) {
|
|
|
|
dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n",
|
|
|
|
device->id, device->identifier.name.string());
|
|
|
|
} else {
|
|
|
|
dump.appendFormat(INDENT2 "%d: %s\n", device->id,
|
|
|
|
device->identifier.name.string());
|
2010-10-01 17:46:21 -07:00
|
|
|
}
|
2011-06-14 17:09:25 -07:00
|
|
|
dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
|
|
|
|
dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
|
2012-04-06 14:51:01 -07:00
|
|
|
dump.appendFormat(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string());
|
2011-06-14 17:09:25 -07:00
|
|
|
dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string());
|
|
|
|
dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string());
|
|
|
|
dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
|
|
|
|
"product=0x%04x, version=0x%04x\n",
|
|
|
|
device->identifier.bus, device->identifier.vendor,
|
|
|
|
device->identifier.product, device->identifier.version);
|
|
|
|
dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n",
|
|
|
|
device->keyMap.keyLayoutFile.string());
|
|
|
|
dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n",
|
|
|
|
device->keyMap.keyCharacterMapFile.string());
|
|
|
|
dump.appendFormat(INDENT3 "ConfigurationFile: %s\n",
|
|
|
|
device->configurationFile.string());
|
2010-10-01 17:46:21 -07:00
|
|
|
}
|
|
|
|
} // release lock
|
|
|
|
}
|
|
|
|
|
2011-08-10 16:25:21 -07:00
|
|
|
void EventHub::monitor() {
|
|
|
|
// Acquire and release the lock to ensure that the event hub has not deadlocked.
|
|
|
|
mLock.lock();
|
|
|
|
mLock.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-03 19:31:44 -08:00
|
|
|
}; // namespace android
|